RecyclerView 使用基础

概念

RecyclerView 类关系

A flexible view for providing a limited window into a large data set.

一个为了大型数据集提供提供有限窗口的灵活视图。

在实现 RecyclerView 过程中,几个主要的类说明如下:

  • LayoutManager:控制 item 的排列方式;
  • RecyclerView.Adapter:创建适配器;
  • RecyclerView.ItemDecoration:item 间的间隔;
  • RecyclerView.ItemAnimator:控制 item 的增删动画;

示例说明

1、在使用该控件之前,我们需要手动将引用包添加到 gradle 中:

implementation 'com.android.support:recyclerview-v7:26.1.0'

注意:在 Android Studio 3.0 之后,grade 文件发送了一些变化,在引用其它包的时候 compile 变成了 implementation。还有一些其他变化,可自行查找。

2、在 XML 文件中使用 RecyclerView 控件进行布局

<android.support.v7.widget.RecyclerView
        android:id="@+id/my_rv"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        />
<!--item 布局 -->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" 
    android:background="@android:color/white"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/text"
        android:padding="10dp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</LinearLayout>

3、在 Activity 中进行配置

public class RecyclerViewActivity extends AppCompatActivity {

    private RecyclerView mRecyclerView;

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

        mRecyclerView = findViewById(R.id.my_rv);
        mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
//        mRecyclerView.setLayoutManager(new GridLayoutManager(this, 2));
//        mRecyclerView.setLayoutManager(new StaggeredGridLayoutManager(2, OrientationHelper.VERTICAL));
        mRecyclerView.setAdapter(new MyRvAdapter(this));
    }
}

适配器代码

public class MyRvAdapter extends RecyclerView.Adapter<MyRvAdapter.MyHolder> {

    private LayoutInflater mLayoutInflater;
    private String[] mTitles;

    public MyRvAdapter(Context context) {
        mLayoutInflater = LayoutInflater.from(context);
        mTitles = context.getResources().getStringArray(R.array.titles);
    }

    @Override
    public MyHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        return new MyHolder(mLayoutInflater.inflate(R.layout.item_view, parent, false));
    }

    @Override
    public void onBindViewHolder(MyHolder holder, int position) {
        holder.textView.setText(mTitles[position]);
    }

    @Override
    public int getItemCount() {
        return mTitles == null ? 0 : mTitles.length;
    }

    class MyHolder extends RecyclerView.ViewHolder {

        private TextView textView;

        public MyHolder(View itemView) {
            super(itemView);
            textView = itemView.findViewById(R.id.text_view);
        }
    }
}

效果图如下:
效果图

LayoutManager

LayoutManager 类关系说明

A LayoutManager is responsible for measuring and positioning item views within a RecyclerView as well as determining the policy for when to recycle item views that are no longer visible to the user.

LayoutManager 负责在 RecyclerView 中测量和定位 item 视图,以及确定何时回收不再对用户可见的 item 视图。

通过改变 LayoutManager,可以使用 RecyclerView 来实现标准的垂直滚动列表、统一网格、交错网格、水平滚动集合等等。控件为一般用途提供了几个布局管理器:

  • LinearLayoutManager:以垂直或水平滚动列表的方式显示;
  • GridLayoutManager:以网格形式显示;
  • StaggeredGridLayoutManager:以交错网格形式显示。

RecyclerView.Adapter

可以看出该适配器相比以前使用 ListView 时用的 BaseAdapter 发生了一些变化。默认实现以下三个方法:

@Override
    public MyHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        return null;
    }

    @Override
    public void onBindViewHolder(MyHolder holder, int position) {

    }

    @Override
    public int getItemCount() {
        return 0;
    }

我们创建的 ViewHolder 必须继承自 RecyclerView.ViewHolder,且构造函数中必须传入一个 View itemView,这个 View 就相当于我们 ListView getView() 中的 convertView。

瀑布流样式

我们只需在 Activity 中配置 StaggeredGridLayoutManage,在适配器中为每个 item 设置一个随机的高度即可。代码如下:

public class MyRvAdapter extends RecyclerView.Adapter<MyRvAdapter.MyHolder> {

    private LayoutInflater mLayoutInflater;
    private String[] mTitles;
    private Integer[] mHeights;

    public MyRvAdapter(Context context) {
        mLayoutInflater = LayoutInflater.from(context);
        mTitles = context.getResources().getStringArray(R.array.letter);

        mHeights = new Integer[mTitles.length];
        for (int i = 0; i < mTitles.length; i++) {
            mHeights[i] = (int)(300 + Math.random() * 300 );
        }
    }

    @Override
    public MyHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        return new MyHolder(mLayoutInflater.inflate(R.layout.item_view, parent, false));
    }

    @Override
    public void onBindViewHolder(MyHolder holder, int position) {
        ViewGroup.LayoutParams layoutParams = holder.textView.getLayoutParams();
        layoutParams.height = mHeights[position];
        holder.textView.setLayoutParams(layoutParams);
        holder.textView.setText(mTitles[position]);
    }

    @Override
    public int getItemCount() {
        return mTitles == null ? 0 : mTitles.length;
    }

    class MyHolder extends RecyclerView.ViewHolder {

        private TextView textView;

        public MyHolder(View itemView) {
            super(itemView);
            textView = itemView.findViewById(R.id.text_view);
        }
    }
}

效果图

这里写图片描述

RecyclerView.ItemDecoration

An ItemDecoration allows the application to add a special drawing and layout offset to specific item views from the adapter’s data set. This can be useful for drawing dividers between items, highlights, visual grouping boundaries and more.

在适配器的数据集上添加特殊的绘图布局偏移量,有助于在 items 之间添加明显的,可视分组边界的分割线。

onDraw() 方法

onDraw(Canvas c, RecyclerView parent, RecyclerView.State state)

Draw any appropriate decorations into the Canvas supplied to the RecyclerView.

将任何合适的装饰绘制到 RecyclerView 的画布上。

onDraw()onDrawOver() 之间的区别:

  • onDraw():在 item 绘制之前先开始画,被 item 的内容覆盖。
  • onDrawOver():在 item 绘制之后开始画,覆盖 item 的内容。

同时,onDraw() 和 onDrawOver() 方法只要手指触摸到屏幕就会被调用,而且在滑动时会被多次调用。

getItemOffsets() 方法

getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state)

Retrieve any offsets for the given item.

检索给定 item 的任何偏移量。

设置 item 的偏移量,item 空出来的部分用来绘制分割线。假设一屏最多显示10个 item,那么这个方法只会被调用10次。

注意:
如果使用 onDraw() 方法来绘制分割线,getItemOffsets() 方法中必须设置对 item 的偏移量,否则分割线无法显示(因为分割线先绘制,被 item 覆盖无法显示)。

如果使用 onDrawOver() 方法来绘制分割线,getItemOffsets() 方法中如果设置了偏移量,分割线会正常显示。如果没有设置偏移量,分割线也会显示(覆盖在 item 上)。

示例代码

自定义 ItemDecoration

public class RecyclerViewItemDecoration extends RecyclerView.ItemDecoration {

    private Paint mPaint;
    private Drawable mDivider;
    private int mDividerHeight = 2; // 分割线高度,默认
    private int mOrientation; // 列表方向
    public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL;
    public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL;
    private static final int[] ATTRS = new int[]{android.R.attr.listDivider};

    /**
     * 默认分割线:高度 2px,颜色灰色
     *
     * */
    public RecyclerViewItemDecoration(Context context, int mOrientation) {
        final TypedArray typedArray = context.obtainStyledAttributes(ATTRS);
        mDivider = typedArray.getDrawable(0);
        typedArray.recycle();
        setmOrientation(mOrientation);
    }

    public void setmOrientation(int mOrientation) {
        if (mOrientation != HORIZONTAL_LIST && mOrientation != VERTICAL_LIST) {
            try {
                throw new IllegalAccessException("invalid orientation!");
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        this.mOrientation = mOrientation;
    }


    /**
     * 自定义分割线
     * @param drawableId 分割线图片
     *
     * */
    public RecyclerViewItemDecoration(Context context, int mOrientation, int drawableId) {
        setmOrientation(mOrientation);
        mDivider = ContextCompat.getDrawable(context, drawableId);
        mDividerHeight = mDivider.getIntrinsicHeight();
    }


    /**
     *
     * 自定义分割线
     *
     * @param dividerHeight 分割线高度
     * @param dividerColor 分割线颜色
     * */
    public RecyclerViewItemDecoration(Context context, int mOrientation, int dividerHeight,
                                      int dividerColor) {
        setmOrientation(mOrientation);
        mDividerHeight = dividerHeight;
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setColor(context.getResources().getColor(dividerColor));
        mPaint.setStyle(Paint.Style.FILL);
    }

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

    /**
     * 绘制横向排列分割线
     *
     * */
    private void drawHorizontal(Canvas canvas, RecyclerView parent) {
        final int top = parent.getPaddingTop();
        final int bottom = parent.getHeight() - parent.getPaddingBottom();
        final int childSize = parent.getChildCount();
        for (int i = 0; i < childSize - 1; i++) {
            final View child = parent.getChildAt(i);
            RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
            final int left = child.getRight() + params.rightMargin;
            final int right = left + mDividerHeight;
            if (mDivider != null) {
                mDivider.setBounds(left, top, right, bottom);
                mDivider.draw(canvas);
            } else {
                if (mPaint != null) {
                    canvas.drawRect(left, top, right, bottom, mPaint);
                }
            }
        }
    }


    /**
     * 绘制竖向排列分割线
     *
     * */
    private void drawVertical(Canvas canvas, RecyclerView parent) {
        final int left = parent.getPaddingLeft();
        final int right = parent.getWidth() - parent.getPaddingRight();
        final int childSize = parent.getChildCount();
        for (int i = 0; i < childSize - 1; i++) {
            final  View view = parent.getChildAt(i);
            RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) view.getLayoutParams();
            final int top = view.getBottom() + params.bottomMargin;
            final int bottom = top + mDividerHeight;
            if (mDivider != null) {
                mDivider.setBounds(left, top, right, bottom);
                mDivider.draw(canvas);
            } else {
                if (mPaint != null) {
                    canvas.drawRect(left, top, right, bottom, mPaint);
                }
            }
        }
    }

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

自定义分割线样式 line_divider.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">

    <size
        android:width="1dp"
        android:height="1dp"
        />
    <solid android:color="@color/colorAccent"/>
</shape>

这里写图片描述

RecyclerView.ItemAnimator

This class defines the animations that take place on items as changes are made to the adapter.
Subclasses of ItemAnimator can be used to implement custom animations for actions on ViewHolder items.

这个抽象类定义了适配器变化时发生在 item 上的动画。ItemAnimator 的子类可以用来实现对 item 的定制动画。默认情况下,RecyclerView 使用 DefaultItemAnimator

// 设置item动画
mRecyclerView.setItemAnimator(new DefaultItemAnimator());

RecyclerView 动画库
https://github.com/wasabeef/recyclerview-animators
https://github.com/gabrielemariotti/RecyclerViewItemAnimators

关于 RecyclerView的Adapter 各种解决方案

https://github.com/CymChad/BaseRecyclerViewAdapterHelper

文章只是作为个人记录学习使用,如有不妥之处请指正,谢谢。

参考文章
https://blog.csdn.net/xx326664162/article/details/61199895

猜你喜欢

转载自blog.csdn.net/modurookie/article/details/80341119