qt 工具栏分隔符_带有分隔线和上下文工具栏的RecyclerView Android

qt 工具栏分隔符

Today we’ll be developing a RecyclerView Android app with contextual toolbar to let us select, delete or mark the rows of a RecyclerView. Furthermore, we’ll place dividers between RecyclerView rows.

今天,我们将开发带有上下文工具栏的RecyclerView Android应用程序,让我们选择,删除或标记RecyclerView的行。 此外,我们将在RecyclerView行之间放置分隔线。

带有分频器和上下文工具栏演示的RecyclerView Android (RecyclerView Android with Dividers and Contextual Toolbar Demo)

We’ll be developing an application that displays the number of rows selected. Our app will allow us to delete, mark, refresh and select all rows.

我们将开发一个显示所选行数的应用程序。 我们的应用程序将允许我们删除,标记,刷新和选择所有行。

A preview of what we’ll going to achieve by the end of this tutorial is given below.

以下是本教程结束时我们将要实现的目标的预览。

RecyclerView Android示例 (RecyclerView Android Example)

ActionMode is used to display the contextual toolbar when a row is long pressed in the list. This enables us to provide a set of alternative toolbar icons.

当在列表中长按一行时, ActionMode用于显示上下文工具栏。 这使我们能够提供一组替代工具栏图标。

We’ll be implementing the four action modes present on the top right.

我们将实现右上角的四种操作模式。

  1. Reload list

    重新载入清单
  2. Mark row text

    标记行文字
  3. Delete row

    删除行
  4. Select all rows

    选择所有行

To implement the Contextual Toolbar and the above actions, we’ll need to implement the ActionMode.Callback interface in our MainActivity.java class.

要实现上下文工具栏和上述操作,我们需要在MainActivity.java类中实现ActionMode.Callback接口。

The ActionMode.Callback interface consists of 4 methods that we’ll be overriding.

ActionMode.Callback接口包含我们将要覆盖的4个方法。

  1. onCreateActionMode: The menu.xml file is inflated in this method.

    onCreateActionMode :在此方法中将menu.xml文件放大。
  2. onPrepareActionMode: This is called every time the Contextual Toolbar is shown.

    onPrepareActionMode :每当显示上下文工具栏时调用。
  3. onActionItemClicked: This is invoked every time a menu item from the Contextual Toolbar is clicked.

    onActionItemClicked :每次单击上下文工具栏中的菜单项时,都会调用此方法。
  4. onDestroyActionMode: This is invoked when the Contextual Toolbar is closed.

    onDestroyActionMode :当上下文工具栏关闭时调用。

RecyclerView android依赖项 (RecyclerView android dependencies)

Let’s start off by adding the following dependencies in our gradle build file.

让我们从在gradle构建文件中添加以下依赖关系开始。

compile 'com.android.support:design:25.3.1'
compile 'com.android.support:recyclerview-v7:25.3.1'

Set the activity’s theme to AppTheme.NoActionBar in the Manifest.xml file as shown below.

将活动的主题设置为Manifest.xml文件中的AppTheme.NoActionBar ,如下所示。

<activity
            android:name=".MainActivity"
            android:theme="@style/AppTheme.NoActionBar">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

RecyclerView Android示例项目结构 (RecyclerView Android Example Project Structure)

The code for activity_main.xml is given below.

下面给出了activity_main.xml的代码。

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:app="https://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:popupTheme="@style/AppTheme.PopupOverlay" />

    </android.support.design.widget.AppBarLayout>

    <include layout="@layout/content_main" />

    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|end"
        android:visibility="gone"
        android:layout_margin="@dimen/fab_margin"
        app:backgroundTint="@color/colorPrimary"
        app:srcCompat="@android:drawable/ic_input_add" />


</android.support.design.widget.CoordinatorLayout>

The code for content_main.xml is given below:

content_main.xml的代码如下:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:app="https://schemas.android.com/apk/res-auto"
    xmlns:tools="https://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:showIn="@layout/activity_main">


    <android.support.v7.widget.RecyclerView
        android:id="@+id/recycler_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scrollbars="vertical" />

</android.support.constraint.ConstraintLayout>

The layout code for each row of the RecyclerView is given in the file recyclerview_list_row.xml.

RecyclerView每行的布局代码在文件recyclerview_list_row.xml给出。

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
    android:id="@+id/relativeLayout"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@drawable/bg_list_row"
    android:clickable="true"
    android:focusable="true"
    android:orientation="vertical"
    android:padding="@dimen/fab_margin">


    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:ellipsize="end"
        android:lines="1"
        android:textSize="16sp"
        android:textStyle="bold" />


</RelativeLayout>

The background of the RelativeLayout is a StateListDrawable (bg_list_row.xml) that’ll change its background when the row is selected/deselected.

RelativeLayout的背景是StateListDrawable(bg_list_row.xml),当选择/取消选择该行时,它将更改其背景。

The code for bg_list_row.xml is given below:

bg_list_row.xml的代码如下:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="https://schemas.android.com/apk/res/android">
    <item android:drawable="@color/row_activated" android:state_activated="true" />
    <item android:drawable="@android:color/transparent" />
</selector>

The menu that’ll be displayed inside the Contextual Toolbar is defined in the file menu_action_mode.xml as shown below.

上下文工具menu_action_mode.xml将显示的菜单在文件menu_action_mode.xml定义,如下所示。

<menu xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:app="https://schemas.android.com/apk/res-auto">
    <item
        android:id="@+id/action_delete"
        android:icon="@drawable/ic_delete"
        android:orderInCategory="300"
        android:title="Delete"
        app:showAsAction="always" />

    <item
        android:id="@+id/action_color"
        android:icon="@drawable/ic_color_mark"
        android:orderInCategory="200"
        android:title="Color"
        app:showAsAction="always" />

    <item
        android:id="@+id/action_refresh"
        android:icon="@drawable/ic_refresh"
        android:orderInCategory="100"
        android:title="Refresh"
        app:showAsAction="always" />

    <item
        android:id="@+id/action_select_all"
        android:icon="@drawable/ic_select_all"
        android:orderInCategory="400"
        android:title="ALL"
        app:showAsAction="always" />

</menu>

We’ve created a custom ItemDecoration for displaying dividers for each of the rows. The code for DividerItemDecoration.java is given below.

我们创建了一个自定义ItemDecoration来显示每行的分隔线。 下面给出了DividerItemDecoration.java的代码。

package com.journaldev.recyclerviewdividersandselectors;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;

public class DividerItemDecoration extends RecyclerView.ItemDecoration {

    private static final int[] ATTRS = new int[]{
            android.R.attr.listDivider
    };

    public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL;

    public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL;

    private Drawable mDivider;

    private int mOrientation;

    public DividerItemDecoration(Context context, int orientation) {
        final TypedArray a = context.obtainStyledAttributes(ATTRS);
        mDivider = a.getDrawable(0);
        a.recycle();
        setOrientation(orientation);
    }

    public void setOrientation(int orientation) {
        if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST) {
            throw new IllegalArgumentException("wrong orientation");
        }
        mOrientation = orientation;
    }

    @Override
    public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
        if (mOrientation == VERTICAL_LIST) {
            drawVertical(c, parent);
        } else {
            drawHorizontal(c, parent);
        }
    }

    public void drawVertical(Canvas c, RecyclerView parent) {
        final int left = parent.getPaddingLeft();
        final int right = parent.getWidth() - parent.getPaddingRight();

        final int childCount = parent.getChildCount();
        for (int i = 0; i < childCount; i++) {
            final View child = parent.getChildAt(i);
            final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
                    .getLayoutParams();
            final int top = child.getBottom() + params.bottomMargin;
            final int bottom = top + mDivider.getIntrinsicHeight();
            mDivider.setBounds(left, top, right, bottom);
            mDivider.draw(c);
        }
    }

    public void drawHorizontal(Canvas c, RecyclerView parent) {
        final int top = parent.getPaddingTop();
        final int bottom = parent.getHeight() - parent.getPaddingBottom();

        final int childCount = parent.getChildCount();
        for (int i = 0; i < childCount; i++) {
            final View child = parent.getChildAt(i);
            final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
                    .getLayoutParams();
            final int left = child.getRight() + params.rightMargin;
            final int right = left + mDivider.getIntrinsicHeight();
            mDivider.setBounds(left, top, right, bottom);
            mDivider.draw(c);
        }
    }

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        if (mOrientation == VERTICAL_LIST) {
            outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
        } else {
            outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
        }
    }
}

The above code creates a divider line (similar to ListView) after each RecyclerView row based on the orientation.

上面的代码根据方向在每个RecyclerView行之后创建一个分隔线(类似于ListView)。

The code for Model.java that holds the data for each row is given below.

下面Model.java了保存每一行数据的Model.java代码。

package com.journaldev.recyclerviewdividersandselectors;

public class Model {

    String text;
    boolean colored;

    public Model(String text, boolean colored) {
        this.text = text;
        this.colored = colored;
    }
}

The code for RecyclerViewAdapter.java is given below:

下面给出了RecyclerViewAdapter.java的代码:

package com.journaldev.recyclerviewdividersandselectors;

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.util.SparseBooleanArray;
import android.view.HapticFeedbackConstants;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.RelativeLayout;
import android.widget.TextView;

import java.util.ArrayList;
import java.util.List;

public class RecyclerViewAdapter extends RecyclerView.Adapter {
    private Context mContext;
    private List modelList;
    private ClickAdapterListener listener;
    private SparseBooleanArray selectedItems;


    private static int currentSelectedIndex = -1;

    public class MyViewHolder extends RecyclerView.ViewHolder implements View.OnLongClickListener {
        public TextView textView;
        public RelativeLayout relativeLayout;

        public MyViewHolder(View view) {
            super(view);
            textView = (TextView) view.findViewById(R.id.textView);
            relativeLayout = (RelativeLayout) view.findViewById(R.id.relativeLayout);
            view.setOnLongClickListener(this);
        }

        @Override
        public boolean onLongClick(View view) {
            listener.onRowLongClicked(getAdapterPosition());
            view.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
            return true;
        }
    }


    public RecyclerViewAdapter(Context mContext, List modelList, ClickAdapterListener listener) {
        this.mContext = mContext;
        this.modelList = modelList;
        this.listener = listener;
        selectedItems = new SparseBooleanArray();
    }

    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View itemView = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.recyclerview_list_row, parent, false);

        return new MyViewHolder(itemView);
    }

    @Override
    public void onBindViewHolder(final MyViewHolder holder, final int position) {
        String text = modelList.get(position).text;
        holder.textView.setText(text);

        if (modelList.get(position).colored)
            holder.textView.setTextColor(mContext.getResources().getColor(android.R.color.holo_red_dark));

        holder.itemView.setActivated(selectedItems.get(position, false));

        applyClickEvents(holder, position);
    }

    private void applyClickEvents(MyViewHolder holder, final int position) {
        holder.relativeLayout.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                listener.onRowClicked(position);
            }
        });

        holder.relativeLayout.setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View view) {
                listener.onRowLongClicked(position);
                view.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
                return true;
            }
        });
    }


    @Override
    public int getItemCount() {
        return modelList.size();
    }

    public void toggleSelection(int pos) {
        currentSelectedIndex = pos;
        if (selectedItems.get(pos, false)) {
            selectedItems.delete(pos);
        } else {
            selectedItems.put(pos, true);
        }
        notifyItemChanged(pos);
    }

    public void selectAll() {

        for (int i = 0; i < getItemCount(); i++)
            selectedItems.put(i, true);
        notifyDataSetChanged();

    }


    public void clearSelections() {
        selectedItems.clear();
        notifyDataSetChanged();
    }

    public int getSelectedItemCount() {
        return selectedItems.size();
    }

    public List getSelectedItems() {
        List items =
                new ArrayList(selectedItems.size());
        for (int i = 0; i < selectedItems.size(); i++) {
            items.add(selectedItems.keyAt(i));
        }
        return items;
    }

    public void removeData(int position) {
        modelList.remove(position);
        resetCurrentIndex();
    }

    public void updateData(int position) {
        modelList.get(position).colored = true;
        resetCurrentIndex();
    }

    private void resetCurrentIndex() {
        currentSelectedIndex = -1;
    }

    public interface ClickAdapterListener {

        void onRowClicked(int position);

        void onRowLongClicked(int position);
    }
}

The following code snippet is used to change the state of the StateListDrawable.

以下代码段用于更改StateListDrawable的状态。

holder.itemView.setActivated(selectedItems.get(position, false));

The methods selectAll(), removeData() and updateData() would be invoked from the MainActivity.java based on the menu item clicked.

方法selectAll()removeData()updateData()将基于所单击的菜单项从MainActivity.java中调用。

The code for MainActivity.java is given below.

MainActivity.java的代码如下。

package com.journaldev.recyclerviewdividersandselectors;

import android.support.design.widget.FloatingActionButton;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.view.ActionMode;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;

import java.util.ArrayList;
import java.util.List;

import static android.view.View.GONE;

public class MainActivity extends AppCompatActivity implements RecyclerViewAdapter.ClickAdapterListener {


    RecyclerView recyclerView;
    LinearLayoutManager layoutManager;
    ArrayList dataModel;
    RecyclerViewAdapter mAdapter;
    private ActionModeCallback actionModeCallback;
    private ActionMode actionMode;
    FloatingActionButton fab;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        fab = (FloatingActionButton) findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                fab.setVisibility(GONE);
                populateDataAndSetAdapter();

            }
        });


        recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
        recyclerView.setHasFixedSize(true);

        layoutManager = new LinearLayoutManager(this);
        recyclerView.setLayoutManager(layoutManager);
        recyclerView.setItemAnimator(new DefaultItemAnimator());
        recyclerView.addItemDecoration(new DividerItemDecoration(this, LinearLayoutManager.VERTICAL));
        actionModeCallback = new ActionModeCallback();

        populateDataAndSetAdapter();
    }

    @Override
    public void onRowClicked(int position) {
        enableActionMode(position);
    }

    @Override
    public void onRowLongClicked(int position) {
        enableActionMode(position);
    }

    private void enableActionMode(int position) {
        if (actionMode == null) {
            actionMode = startSupportActionMode(actionModeCallback);
        }
        toggleSelection(position);
    }

    private void toggleSelection(int position) {
        mAdapter.toggleSelection(position);
        int count = mAdapter.getSelectedItemCount();

        if (count == 0) {
            actionMode.finish();
            actionMode = null;
        } else {
            actionMode.setTitle(String.valueOf(count));
            actionMode.invalidate();
        }
    }

    private void selectAll() {
        mAdapter.selectAll();
        int count = mAdapter.getSelectedItemCount();

        if (count == 0) {
            actionMode.finish();
        } else {
            actionMode.setTitle(String.valueOf(count));
            actionMode.invalidate();
        }

        actionMode = null;
    }


    private class ActionModeCallback implements ActionMode.Callback {
        @Override
        public boolean onCreateActionMode(ActionMode mode, Menu menu) {
            mode.getMenuInflater().inflate(R.menu.menu_action_mode, menu);

            return true;
        }

        @Override
        public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
            return false;
        }

        @Override
        public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
            Log.d("API123", "here");
            switch (item.getItemId()) {


                case R.id.action_delete:
                    // delete all the selected rows
                    deleteRows();
                    mode.finish();
                    return true;

                case R.id.action_color:
                    updateColoredRows();
                    mode.finish();
                    return true;

                case R.id.action_select_all:
                    selectAll();
                    return true;

                case R.id.action_refresh:
                    populateDataAndSetAdapter();
                    mode.finish();
                    return true;

                default:
                    return false;
            }
        }

        @Override
        public void onDestroyActionMode(ActionMode mode) {
            mAdapter.clearSelections();
            actionMode = null;
        }
    }

    private void deleteRows() {
        List selectedItemPositions =
                mAdapter.getSelectedItems();
        for (int i = selectedItemPositions.size() - 1; i >= 0; i--) {
            mAdapter.removeData(selectedItemPositions.get(i));
        }
        mAdapter.notifyDataSetChanged();

        if (mAdapter.getItemCount() == 0)
            fab.setVisibility(View.VISIBLE);

        actionMode = null;
    }

    private void updateColoredRows() {
        List selectedItemPositions =
                mAdapter.getSelectedItems();
        for (int i = selectedItemPositions.size() - 1; i >= 0; i--) {
            mAdapter.updateData(selectedItemPositions.get(i));
        }
        mAdapter.notifyDataSetChanged();
        actionMode = null;
    }

    private void populateDataAndSetAdapter() {
        dataModel = new ArrayList();
        dataModel.add(new Model("Item 1", false));
        dataModel.add(new Model("Item 2", false));
        dataModel.add(new Model("Item 3", false));
        dataModel.add(new Model("Item 4", false));
        dataModel.add(new Model("Item 5", false));
        dataModel.add(new Model("Item 6", false));
        dataModel.add(new Model("Item 7", false));
        dataModel.add(new Model("Item 8", false));
        dataModel.add(new Model("Item 9", false));
        dataModel.add(new Model("Item 10", false));
        dataModel.add(new Model("Item 11", false));
        dataModel.add(new Model("Item 12", false));

        mAdapter = new RecyclerViewAdapter(this, dataModel, this);
        recyclerView.setAdapter(mAdapter);
    }
}

The following code is used to add dividers between the rows.

以下代码用于在行之间添加分隔符。

recyclerView.addItemDecoration(new DividerItemDecoration(this, LinearLayoutManager.VERTICAL));

onRowClicked() and onRowLongClicked() are called every time a RecyclerView row is clicked.

每次单击RecyclerView行时,都会调用onRowClicked()onRowLongClicked()

enableActionMode() is used to show the Contextual Toolbar.

enableActionMode()用于显示上下文工具栏。

The Contextual Toolbar displays the number of rows selected based upon the getSelectedItemCount() from the adapter class.

上下文工具栏显示基于适配器类中的getSelectedItemCount()选择的行数。

If all the rows are deleted, we show a floating action button that lets the user populate the RecyclerView with dummy data once again.

如果所有行均被删除,我们将显示一个浮动操作按钮,该按钮使用户可以再次使用伪数据填充RecyclerView。

The output of the above application in action is given below.

android recyclerview contextual toolbar app

下面给出了上面应用程序的输出。

Contextual Toolbar is commonly seen in applications like Whatsapp and Inbox.

上下文工具栏通常在诸如Whatsapp和Inbox之类的应用程序中看到。

This brings an end to the RecyclerView Android example with divider and selectors. You can download the final Android RecyclerViewDividersAndSelectors Project from the link below.

这结束了带有分隔符和选择器的RecyclerView Android示例。 您可以从下面的链接下载最终的Android RecyclerViewDividersAndSelectors项目

Reference: Android Doc

参考: Android文档

翻译自: https://www.journaldev.com/15035/recyclerview-android-dividers-contextual-toolbar

qt 工具栏分隔符

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值