Android RecyclerView使用详解二

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/sinat_30276961/article/details/50210277

转载请注明:
http://blog.csdn.net/sinat_30276961/article/details/50210277

上一篇,介绍了RecyclerView的特点,并展示了一个基本的RecyclerView需要涉及到的步骤:获取RecyclerView,创建并设置排布方式,复写adapter和内部的ViewHolder,并设置adapter。本篇将展示一下,水平排布、网格排布和添加删除动画效果。

在上一篇,有说过,RecyclerView的子View的排布方式有:
1. LinearLayoutManager 线性排列
2. GridLayoutManager 网格排列
3. StaggeredGridLayoutManager 瀑布流排列

对于LinearLayoutManager,它有两种方式:横向和纵向。上一篇展示的是纵向。
对于GridLayoutManager,它呈现的是网格式排布,类似于GridView。
添加和删除动画用到的是默认的动画效果。

按照惯例,我先呈现一下效果:

这里写图片描述 这里写图片描述

用RecyclerView实现横向,那可是分分钟搞定的事啊。如果是用ListView,要么复写它,要么就用GridView去展示一行的数据,都不怎么理想。

第一个实例代码也很简单,就是布局文件里放置两个RecyclerView,然后每个item的布局文件贴一下:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:paddingLeft="8dp"
    android:paddingRight="8dp"
    android:paddingTop="16dp"
    android:paddingBottom="16dp"
    android:gravity="center_horizontal"
    android:background="@drawable/recycler_list_bg">

    <TextView
        android:layout_above="@+id/logo"
        android:id="@+id/text"
        android:textColor="#000000"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:ems="1"
        android:layout_marginBottom="16dp"
        android:layout_centerHorizontal="true"
        android:textAppearance="?attr/textAppearanceListItem"/>

    <com.office.steven.androidtest.commonwidget.CircleImageView
        android:id="@+id/logo"
        android:layout_width="40dp"
        android:layout_height="40dp"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        app:civ_border_width="2dp"
        app:civ_border_color="#ff999999"/>

</RelativeLayout>

这里有设置android:ems,是为了横向时,TextView呈现文字能舒服点。
主体代码,只要注意一点就行:创建的LinearLayoutManager设置为LinearLayoutManager.HORIZONTAL。

再来看看LinearLayoutManager的构造函数。

    /**
     * @param context       Current context, will be used to access resources.
     * @param orientation   Layout orientation. Should be {@link #HORIZONTAL} or {@link
     *                      #VERTICAL}.
     * @param reverseLayout When set to true, layouts from end to start.
     */
    public LinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
        setOrientation(orientation);
        setReverseLayout(reverseLayout);
    }

前面两个不说了,第三个的效果就是反向排列。

接着,来看看GridLayoutManager,它是继承自LinearLayoutManager。我们来看看它的构造函数:

    /**
     * Creates a vertical GridLayoutManager
     *
     * @param context Current context, will be used to access resources.
     * @param spanCount The number of columns in the grid
     */
    public GridLayoutManager(Context context, int spanCount) {
        super(context);
        setSpanCount(spanCount);
    }

可以看到,相比较LinearLayoutManager,它多了个spanCount。这个参数是用来控制有多少列的。

ok,我直接贴出添加和删除核心代码:

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

        mGridCb = (CheckBox) findViewById(R.id.is_grid);
        mGridCb.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (mGridCb.isChecked()) {
                    setGrid();
                    // 如果当前的添加删除的索引值超过了当前展示的grid的item数量,那就设置索引值为item的数量
                    if (mFromIndex > mGridAdapter.getNum())
                        mFromIndex = mGridAdapter.getNum();
                    mRandomFrom.setText(String.valueOf(mFromIndex));
                } else {
                    setLinear();
                    // 同上
                    if (mFromIndex > mAdapter.getNum())
                        mFromIndex = mAdapter.getNum();
                    mRandomFrom.setText(String.valueOf(mFromIndex));
                }
            }
        });
        // 用来随机产生一个数,增加/减少item时根据这个数值
        mRandomNum = (Button) findViewById(R.id.random_option_num);
        mRandomNum.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 随机产生1到10的数值
                mNum = new Random().nextInt(10)+1;
                mRandomNum.setText(String.valueOf(mNum));
            }
        });
        // 用来随机产生一个数,指定增加/减少操作的起始index
        mRandomFrom = (Button) findViewById(R.id.random_from_num);
        mRandomFrom.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 先获取当前的RecyclerView还剩多少item
                final int leftNum;
                if (mGridCb.isChecked()) {
                    leftNum = mGridAdapter.getNum();
                } else {
                    leftNum = mAdapter.getNum();
                }
                // 索引值只能在[0,leftNum+1)取
                mFromIndex = new Random().nextInt(leftNum+1);
                mRandomFrom.setText(String.valueOf(mFromIndex));
            }
        });
        mRecyclerView = (RecyclerView) findViewById(R.id.recyclerview);
        mRecyclerView.setHasFixedSize(true);

        mLayoutManager = new LinearLayoutManager(this){
            @Override
            public RecyclerView.LayoutParams generateDefaultLayoutParams() {
                return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                        ViewGroup.LayoutParams.WRAP_CONTENT);
            }
        };
        mLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
        mRecyclerView.setLayoutManager(mLayoutManager);

        mGridLayoutManager = new GridLayoutManager(this, 5);
        mGridAdapter = new GridRecyclerViewAdapter(this, R.layout.item_recycler_view_grid, 10);
        mGridAdapter.setOnItemClickListener(new CommonAdapter.OnRecyclerViewItemClickListener() {
            @Override
            public void onItemClick(View view, int position) {
                Toast.makeText(AddDeleteTest.this, "Grid " + position + " click", Toast.LENGTH_SHORT).show();
            }
        });

        mAdapter = new RecyclerViewAdapter(this, R.layout.item_normal_recycler_view, 3);
        mAdapter.setOnItemClickListener(new CommonAdapter.OnRecyclerViewItemClickListener() {
            @Override
            public void onItemClick(View view, int position) {
                Toast.makeText(AddDeleteTest.this, "Linear " + position + " click", Toast.LENGTH_SHORT).show();
            }
        });
        mRecyclerView.setAdapter(mAdapter);
    }

在第6-24行,我用CheckBox来切换RecyclerView的排布方式。

    private void setGrid() {
        mRecyclerView.setLayoutManager(mGridLayoutManager);
        mRecyclerView.setAdapter(mGridAdapter);
    }

    private void setLinear() {
        mRecyclerView.setLayoutManager(mLayoutManager);
        mRecyclerView.setAdapter(mAdapter);
    }

mFromIndex是增加和删除Item的起始值。增加:在当前索引值往后增加item;删除:在当前索引值往前删除item

第26行和第36行的mRandomNum和mRandomFrom,这两个是textview,点击时,我就随机产生一个数赋给它。这两个分别代表添加和删除的数量,添加和删除的索引值。

在55行和65行,分别创建了线性布局和网格布局。然后针对两个布局,分别创建了各自的adapter。

接着,贴出添加和删除的代码:

    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_add) {
            if (mGridCb.isChecked()) {
                final int num = mGridAdapter.getNum();
                mGridAdapter.setNum(num+mNum);
                mGridAdapter.notifyItemRangeInserted(mFromIndex, mNum);
            } else {
                final int num = mAdapter.getNum();
                mAdapter.setNum(num+mNum);
                mAdapter.notifyItemRangeInserted(mFromIndex, mNum);
            }

            return true;
        } else if (id == R.id.action_delete) {
            if (mGridCb.isChecked()) {
                final int num = mGridAdapter.getNum();
                int deleteNum = mNum;
                if (mNum > num-mFromIndex) {
                    deleteNum = num-mFromIndex;
                }
                mGridAdapter.setNum(num-deleteNum);
                mGridAdapter.notifyItemRangeRemoved(mFromIndex, deleteNum);
            } else {
                final int num = mAdapter.getNum();
                int deleteNum = mNum;
                if (mNum > num-mFromIndex) {
                    deleteNum = num-mFromIndex;
                }
                mAdapter.setNum(num-deleteNum);
                mAdapter.notifyItemRangeRemoved(mFromIndex, deleteNum);
            }
        }

        return super.onOptionsItemSelected(item);
    }

核心api是notifyItemRangeInserted和notifyItemRangeRemoved。
这两个api针对的是批量添加和删除数据之后的通知RecyclerView重绘。

下面再列一下其他通知重绘api。可以看到,相比较ListView的adapter,这个就多了很多。有针对插入数据,移除数据,改变数据和移动数据。当然,它也保留了我们以前在ListView最常用到的notifyDataSetChanged。
这里写图片描述

接着,再回到代码,添加就是在索引值以后再添加一段数据。删除就是从索引值往前删除数据。在删除那里,做了特殊处理:如果当前索引值往前的数量少于随机产生的删除数量,那把删除数量调整为索引值往前的item的数量。

说到这,该讲的内容基本都讲完,就剩下一个东西还没讲到,那就是动画效果。可以看到,我写的代码里没有涉及到动画效果的创建。那么动画效果是在哪里设置的呢?

这又得去看RecyclerView的源码了。源码中有这样一句:

ItemAnimator mItemAnimator = new DefaultItemAnimator();

也就是说,在初始化RecyclerView时,就会默认的创建一个DefaultItemAnimator动画效果。

动画效果的设置是通过setItemAnimator这个方法的。

    public void setItemAnimator(ItemAnimator animator) {
        if (mItemAnimator != null) {
            mItemAnimator.endAnimations();
            mItemAnimator.setListener(null);
        }
        mItemAnimator = animator;
        if (mItemAnimator != null) {
            mItemAnimator.setListener(mItemAnimatorListener);
        }
    }

至于这个DefaultItemAnimator,是google提供给我们直接使用的。
它继承自RecyclerView.ItemAnimator,如果我们要自己实现一个动画效果,也需要继承它。关于这块,我会在后面详细讲解。

ok,今天讲解了关于水平布局和网格布局的细节部分,并讲解了添加和删除的动画效果展示。下篇会接着讲解分隔图等。

Have a good night!

猜你喜欢

转载自blog.csdn.net/sinat_30276961/article/details/50210277