Android RecyclerView 详解

1:分割线的使用

1.1LinearLayoutManager时分割线使用

  RecyclerView设置分割线的方法:

 public void addItemDecoration(@NonNull RecyclerView.ItemDecoration decor) {
        this.addItemDecoration(decor, -1);
    }

   如果只需要一条灰色的线,如上调用recyclerView的addItemDecoration方法传入ItemDecoration即可。

  这里RecyclerView为我们提供了DividerItemDecoration。这个类继承自ItemDecoration,内部方法十分简单,设置分割线的    orientation以及传入一个drawable。

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

 

 或者传入drawable

 DividerItemDecoration dividerItemDecoration= new DividerItemDecoration(this,DividerItemDecoration.VERTICAL);
        dividerItemDecoration.setDrawable(getResources().getDrawable(R.drawable.ic_launcher_background));
recyclerView.addItemDecoration(dividerItemDecoration);

 

1.1GridLayoutManager分割线使用

类似于LinearLayoutManager ,我们可以直接添加两个addItemDecoration,分别传入横向和纵向:

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

 看下效果:

 

 效果不是特别好,我们希望item的底部不需要线或者不想要这样的效果,要怎么做呢?

我们知道DividerItemDecoration是继承自ItemDecoration,那我们是不是也可以重写class继承ItemDecoration呢?

答案是:可以。

首先我们看下ItemDecoration的结构。

 public abstract static class ItemDecoration {
        public ItemDecoration() {
        }

        public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
            this.onDraw(c, parent);
        }

        /** @deprecated */
        @Deprecated
        public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent) {
        }

        public void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
            this.onDrawOver(c, parent);
        }

        /** @deprecated */
        @Deprecated
        public void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent) {
        }

        /** @deprecated */
        @Deprecated
        public void getItemOffsets(@NonNull Rect outRect, int itemPosition, @NonNull RecyclerView parent) {
            outRect.set(0, 0, 0, 0);
        }

        public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
            this.getItemOffsets(outRect, ((RecyclerView.LayoutParams)view.getLayoutParams()).getViewLayoutPosition(), parent);
        }
    }

如上是recyclerview中ItemDecoration的各个属性。

我们用的有两个方法 onDraw和getItemOffsets。

ondraw中绘制横向和纵向的分割线,其中我们需要判断是否绘制item的横向和纵向的分割线。

定义属性:

//是否绘制横向和纵向的分割线。
        boolean isDrawHorDivider;
        boolean isDrawVerDivider;

其次,我们根据获取到的view的位置,来判断是否绘制该view的横向和纵向分割线。

 //获取子view个数
int childCount=parent.getChildCount();
//获取spanCount
int spanCount= ((GridLayoutManager)(parent.getLayoutManager())).getSpanCount();
int orientation=((GridLayoutManager)(parent.getLayoutManager())).getOrientation();

 获取到子view的个数以及gridlayoutManager的spanCount后,我们来判断是否绘制。

 int extra=childCount%spanCount==0?spanCount:childCount%spanCount;

上面这个值是计算出来的一个偏移量,用子view的数量-extra获取的值,如果小于子view的position。

那就说明,这个子view会于recyclerView的最后一行或者最后一列。(横向和纵向的grid)

所以,判断是否绘制时,我们也需要分开来判断orientation。

        for (int i=0;i<childCount;i++){
            isDrawHorDivider=true;
            isDrawVerDivider=true;

            // 最右侧一列不绘制竖直方向
            if (orientation== OrientationHelper.VERTICAL&&(i+1)%spanCount==0){
                isDrawVerDivider=false;
            }

            //最后一行不绘制横向分割线
            if (orientation== OrientationHelper.VERTICAL&&i>=(childCount-extra)){
                isDrawHorDivider=false;
            }

            //水平方向时  如果是底部一行 不绘制横向
            if (orientation== OrientationHelper.HORIZONTAL&&(i+1)%spanCount==0){
                isDrawHorDivider=false;
            }
            //水平方向时  如果是右侧一列 不绘制纵向
            if (orientation== OrientationHelper.HORIZONTAL&&i>=(childCount-extra)){
                isDrawVerDivider=false;
            }

            //绘制部分。。。

        }

 是否绘制判断完了,接着我们来编写绘制分割线的代码。

这里分为两块,分别的drawHorizontal和drawVertical。

我们从onDraw中获取三个参数,分别是canvas,recyclerView和position。

绘制drawable我们先setBound,所以先分别确定left,top,right以及bottom。

代码如下:

    public void drawVertical(Canvas canvas,RecyclerView parent,int position){
        View childView =parent.getChildAt(position);
        RecyclerView.LayoutParams params= (RecyclerView.LayoutParams) childView.getLayoutParams();
        /**
         *   &&&&&&&&&&&&&&&&&
         *   &               &
         *   &     *   *     &
         *   &     *   *     &
         *   &               &
         *   &&&&&&&&&&&&&&&&&
         *
         *   如上回字型 中间 表示子view 外层是layoutParam的margins
         *   在右侧绘制drawable时:
         *   view的top= childView.getTop-param.topMargin;
         *   view的left=childView.getRight+param.rightMargin;
         *   view的right=left+drawable的宽度
         *   view的bottom=childView.getBottom+param.bottomMargin+drawable的高度
         * */
        int top=childView.getTop()-params.topMargin;
        int left=childView.getRight()+params.rightMargin;
        int right=left+drawable.getIntrinsicWidth();
        int bottom=childView.getBottom()+params.bottomMargin+drawable.getIntrinsicHeight();

        drawable.setBounds(left,top,right,bottom);
        drawable.draw(canvas);
    }

 横向的和上面类似,整体代码如下:

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.support.annotation.NonNull;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.OrientationHelper;
import android.support.v7.widget.RecyclerView;
import android.view.View;

/**
 * create by zj on 2019/6/20
 */
public class MyGridDivider extends RecyclerView.ItemDecoration {

    private Drawable drawable;

    public MyGridDivider(Drawable drawable){
        this.drawable=drawable;
    }
    @Override
    public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {

        //获取子view个数
        int childCount=parent.getChildCount();
        //获取spanCount
        int spanCount= ((GridLayoutManager)(parent.getLayoutManager())).getSpanCount();
        int orientation=((GridLayoutManager)(parent.getLayoutManager())).getOrientation();

        //是否绘制横向和纵向的分割线。
        boolean isDrawHorDivider;
        boolean isDrawVerDivider;


        int extra=childCount%spanCount==0?spanCount:childCount%spanCount;

        for (int i=0;i<childCount;i++){
            isDrawHorDivider=true;
            isDrawVerDivider=true;

            // 最右侧一列不绘制竖直方向
            if (orientation== OrientationHelper.VERTICAL&&(i+1)%spanCount==0){
                isDrawVerDivider=false;
            }

            //最后一行不绘制横向分割线
            if (orientation== OrientationHelper.VERTICAL&&i>=(childCount-extra)){
                isDrawHorDivider=false;
            }

            //水平方向时  如果是底部一行 不绘制横向
            if (orientation== OrientationHelper.HORIZONTAL&&(i+1)%spanCount==0){
                isDrawHorDivider=false;
            }
            //水平方向时  如果是右侧一列 不绘制纵向
            if (orientation== OrientationHelper.HORIZONTAL&&i>=(childCount-extra)){
                isDrawVerDivider=false;
            }

            if (isDrawVerDivider){
                drawVertical(c,parent,i);
            }

            if (isDrawHorDivider){
                drawHorizontal(c,parent,i);
            }

        }
    }

    @Override
    public void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
        super.onDrawOver(c, parent, state);
    }

    @Override
    public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
        //获取spanCount
        int spanCount= ((GridLayoutManager)(parent.getLayoutManager())).getSpanCount();
        int orientation=((GridLayoutManager)(parent.getLayoutManager())).getOrientation();

        //getChildViewHolderInt获取位置
        int position=parent.getChildLayoutPosition(view);

        if (orientation==OrientationHelper.VERTICAL&&(position+1)%spanCount==0){
            outRect.set(0,0,0,drawable.getIntrinsicHeight());
            return;
        }

        if (orientation==OrientationHelper.HORIZONTAL&&(position+1)%spanCount==0){
            outRect.set(0,0,drawable.getIntrinsicWidth(),0);
            return;
        }

        outRect.set(0,0,drawable.getIntrinsicWidth(),drawable.getIntrinsicHeight());

    }



    public void drawVertical(Canvas canvas,RecyclerView parent,int position){
        View childView =parent.getChildAt(position);
        RecyclerView.LayoutParams params= (RecyclerView.LayoutParams) childView.getLayoutParams();
        /**
         *   &&&&&&&&&&&&&&&&&
         *   &               &
         *   &     *   *     &
         *   &     *   *     &
         *   &               &
         *   &&&&&&&&&&&&&&&&&
         *
         *   如上回字型 中间 表示子view 外层是layoutParam的margins
         *   在右侧绘制drawable时:
         *   view的top= childView.getTop-param.topMargin;
         *   view的left=childView.getRight+param.rightMargin;
         *   view的right=left+drawable的宽度
         *   view的bottom=childView.getBottom+param.bottomMargin+drawable的高度
         * */
        int top=childView.getTop()-params.topMargin;
        int left=childView.getRight()+params.rightMargin;
        int right=left+drawable.getIntrinsicWidth();
        int bottom=childView.getBottom()+params.bottomMargin+drawable.getIntrinsicHeight();

        drawable.setBounds(left,top,right,bottom);
        drawable.draw(canvas);
    }

    public void drawHorizontal(Canvas canvas,RecyclerView parent,int position){
        View childView =parent.getChildAt(position);
        RecyclerView.LayoutParams params= (RecyclerView.LayoutParams) childView.getLayoutParams();

        int top=childView.getBottom()+params.bottomMargin;
        int left=childView.getLeft()-params.leftMargin;
        int right=childView.getRight()+params.rightMargin+drawable.getIntrinsicWidth();
        int bottom=top+drawable.getIntrinsicHeight();
        drawable.setBounds(left,top,right,bottom);
        drawable.draw(canvas);
    }
}

最后我们看下效果:

 

 

2:设置动画

    可以通过调用setItemAnimator属性,设置动画。

   如下是设置动画的源码。

  public void setItemAnimator(@Nullable RecyclerView.ItemAnimator animator) {
        if (this.mItemAnimator != null) {
            this.mItemAnimator.endAnimations();
            this.mItemAnimator.setListener((RecyclerView.ItemAnimator.ItemAnimatorListener)null);
        }

        this.mItemAnimator = animator;
        if (this.mItemAnimator != null) {
            this.mItemAnimator.setListener(this.mItemAnimatorListener);
        }

    }
RecyclerView提供了默认的ItemAnimator实现类:DefaultItemAnimator。如果没有特殊的需求,默认使用这个动画即可。

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值