RecyclerView常用功能全面总结

      • 简介
      • 和ListView的比较
          • ListView的优点:
          • RecyclerView的优点
      • 简单使用
      • GridLayoutManager,StaggeredGridLayoutManager
      • ItemDecoration
      • ItemAnimator
      • Item侧滑和拖拽
      • 多种类型布局
      • 总结

简介

RecyclerView可以说是做Android应用开发使用最广的几个控件之一。它是在Android 5.0版本引入进来的,位于support-v7包中,可以通过在build.gradle中添加如下代码将它引入到项目中:

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

和ListView的比较

在RecyclerView出现之前Android中的复用列表大多通过ListView实现的,RecyclerView并不会完全替代ListView,至少现在来看ListView并没有被废弃掉。RecyclerView和ListView互有优势。

ListView的优点:
  • 可以直接通过addHeaderView(), addFooterView()添加头视图和尾视图。
  • 直接提供了setOnItemClickListener()和setOnItemLongClickListener()设置点击事件和长按事件。
  • “android:divider”设置自定义分割线。
RecyclerView的优点
  • 提供了多种LayoutManager,可轻松实现多种样式的布局
  • 支持局部刷新
  • 已经实现了View的复用,不需要类似if(convertView == null)的实现,而且回收机制更加完善
  • 容易实现添加item、删除item的动画效果
  • 容易实现拖拽、侧滑删除等功能

简单使用

RecylerView的使用几个必须的步骤如下:
- 创建Adapter,并设置给RecylerView
- 为RecylerView设置LayoutManager
这两个是RecylerView使用的必须步骤,可以看出
RecylerView和ListView的使用方法相比只是多了一步设置LayoutManager。

通过代码来看Adapter的具体实现:

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
    private Context context;
    private List mDatas;

    public MyAdapter(Context context , List mDatas){
        this.context = context;
        this.mDatas = mDatas;
    }

    @NonNull
    @Override
    public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        return new MyViewHolder(LayoutInflater.from(context).inflate(R.layout.item_layout,parent,false));

    }

    @Override
    public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
        holder.textView.setText("第"+position);
    }

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

    public class MyViewHolder extends RecyclerView.ViewHolder{

        private TextView textView;
        public MyViewHolder(View itemView) {
            super(itemView);
            textView = itemView.findViewById(R.id.textview);
        }
    }
}

将Adapter和LayoutManager设置给RecylerView:

        myAdapter = new MyAdapter(this,datas);
        LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
        mRl.setLayoutManager(linearLayoutManager);
        mRl.setAdapter(myAdapter);

这样一个最基本的RecyclerView就实现了。

GridLayoutManager,StaggeredGridLayoutManager

在前面基本使用的代码中,给RecyclerView设置的是LinearLayoutManager,其实RecyclerView还提供了另外两种布局管理器,这就是GridLayoutManager和StaggeredGridLayoutManager,通过它们可以很简单的分别实现表格布局和瀑布流布局。

通过GridLayoutManager可以让RecyclerVie实现GridView的效果。GridLayoutManager的使用方式如下:

//4可以理解为一行显示4列
GridLayoutManager linearLayoutManager = new GridLayoutManager(this,4);
mRl.setLayoutManager(linearLayoutManager);

通过StaggeredGridLayoutManager可以让RecyclerVie实现瀑布流的效果。StaggeredGridLayoutManager的使用方式如下:

  StaggeredGridLayoutManager linearLayoutManager = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);

当然也可以根据需求自定义LayoutManager.通过源码就可以可以看到GridLayoutManager其实也是继承了LinearLayoutManager,相当于自定义一个LinearLayoutManager,把一行一列,变为一行多列。

ItemDecoration

ItemDecoration其实翻译过来就是item装饰品,所以它不仅仅是用来绘制分割线,它可以绘制出各式各样的的itemview的装饰界面。只是由于RecyclerView中并没有提供类似android:divider的方法来实现分割线,所以往往通过ItemDecoration来实现各种分割线效果。
ItemDecoration是一个抽象类,主要有下面3个方法:

    //用于撑开整个itemview
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state)
    //用于绘制具体的分隔线形状,在itemview前面绘制,有可能被itemview覆盖
    public void onDraw(Canvas c, RecyclerView parent, State state) 
    //它是在itemview之后绘制,可以覆盖itemview的内容
    public void onDrawOver(Canvas c, RecyclerView parent, State state) 

通过代码来实现一条绿色的分割线:

    public class MyItemDecoration extends RecyclerView.ItemDecoration{
        private int mDiverHeight;
        private Paint mPaint;

        public MyItemDecoration(){
            mPaint = new Paint();
            mPaint.setColor(Color.GREEN);
        }

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

            int childCount = parent.getChildCount();

            for (int i = 0; i < childCount; i++) {
                View view = parent.getChildAt(i);

                if (parent.getChildAdapterPosition(view) !=0){
                    float top = view.getTop()-mDiverHeight;
                    float left = parent.getPaddingLeft();
                    float right = parent.getWidth()-parent.getPaddingRight();
                    float bottom = view.getTop();

                    c.drawRect(left,top,right,bottom,mPaint);
                }
            }

        }


        @Override
        public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
            super.onDrawOver(c, parent, state);
//
//            int childCount = parent.getChildCount();
//
//            for (int i = 0; i < childCount; i++) {
//                View view = parent.getChildAt(i);
//
//                if (parent.getChildAdapterPosition(view) !=0){
//                    float top = view.getTop()-mDiverHeight;
//                    float left = parent.getPaddingLeft();
//                    float right = parent.getWidth()-parent.getPaddingRight();
//                    float bottom = view.getTop();
//
//                    c.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 (parent.getChildLayoutPosition(view) != 0){
                outRect.top = 10;
                mDiverHeight = 40;
            }
        }
    }

分析一下上面的代码,
1. 首先在getItemOffsets中利用 outRect.top在itemview的顶部撑开了10px的高度,将mDiverHeight的值设置为40px.
2. onDraw方法中具体绘制分隔线样式
3. onDrawOver方法中同样的代码绘制分割线

分别看下在不同方法中绘制出来的效果:
RecyclerView常用功能全面总结_第1张图片
RecyclerView常用功能全面总结_第2张图片
图一,虽然mDiverHeight的高度设置为40px,但并没有显示那么高,因为在getItemOffsets方法中只将itemview撑开了10px,进一步验证了onDraw绘制的分隔线有可能被itemview覆盖。

图二,mDiverHeight的高度设置为40px,实际显示也是40px,虽然在getItemOffsets方法中只将itemview撑开了10px,但是分隔线覆盖掉了itemview部分界面,也验证了onDrawOver绘制的分隔线会覆盖itemview。

总结,在熟悉了onDraw和onDrawOver方法之后可以利用它们绘制出多种多样的itemview装饰界面。

ItemAnimator

通过ItemAnimator可以设置Item添加、删除、更新的动画,即使没有设置它也会看到动画,因为RecyclerView设置了默认动画DefaultItemAnimator,通过代码来看下DefaultItemAnimator的效果:

 DefaultItemAnimator defaultItemAnimator = new DefaultItemAnimator();
 defaultItemAnimator.setAddDuration(1000);//设置时间长一点,容易看出效果
 mRl.setItemAnimator(defaultItemAnimator);
需要注意的是这里改变数据刷新并不是调用的notifyDataSetChanged()方法,添加时调用notifyItemInserted(1),删除时调用notifyItemRemoved(1)。 从效果可以看出默认动画的效果是: item添加时:渐变色由0-1显现 item删除时:渐变色由1-0消失 只有DefaultItemAnimator往往不能满足需求,这时就需要自定义ItemAnimator,虽然动画效果需要自定义,但是基本都是在add和remove时展示,所以只需要在DefaultItemAnimator的基础上做修改就行了,下面实现一个在默认动画的基础上再增加一个平移动画。Copy DefaultItemAnimator全部代码至MyItemAnimator,只需要修改添加部分: MyItemAnimator.java
 @Override
    public boolean animateAdd(final RecyclerView.ViewHolder holder) {
        resetAnimation(holder);
        holder.itemView.setAlpha(0);
        mPendingAdditions.add(holder);

        //新增平移动画,默认将itemview左移自身宽度
        ViewCompat.setTranslationX(holder.itemView,-holder.itemView.getWidth());
        mPendingAdditions.add(holder);
        return true;
    }

    void animateAddImpl(final RecyclerView.ViewHolder holder) {
        final View view = holder.itemView;
        final ViewPropertyAnimator animation = view.animate();
        mAddAnimations.add(holder);
        //新增translationX(0),将itemview平移出来
        animation.alpha(1).translationX(0).setDuration(getAddDuration())
                .setListener(new AnimatorListenerAdapter() {
                    @Override
                    public void onAnimationStart(Animator animator) {
                        dispatchAddStarting(holder);
                    }

                    @Override
                    public void onAnimationCancel(Animator animator) {
                        ViewCompat.setAlpha(view, 1);
                    }

                    @Override
                    public void onAnimationEnd(Animator animator) {
                        animation.setListener(null);
                        dispatchAddFinished(holder);
                        mAddAnimations.remove(holder);
                        dispatchFinishedWhenDone();
                    }
                }).start();

    }

效果如下:

一个简单的自定义ItemAnimator就这样实现了。

Item侧滑和拖拽

RecyclerView的item侧滑功能主要是通过ItemTouchHelper实现的,它是一个支持RecyclerView滑动删除和拖拽的实用工具类,使用也很简单:

ItemTouchHelper itemTouchHelper = new ItemTouchHelper(mCallBack);
itemTouchHelper.attachToRecyclerView(mRl);

所以只需要关注这里的mCallBack,看下它的实现:

public ItemTouchHelper.Callback mCallBack  = new ItemTouchHelper.Callback() {
    /**
     * 指定可以拖拽(drag)和滑动(swipe)的方向
     * @param recyclerView
     * @param viewHolder
     * @return
     */
    @Override
    public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
        return 0;
    }

    /**
     * 拖拽回调
     * @param recyclerView
     * @param viewHolder
     * @param target
     * @return
     */
    @Override
    public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
        return false;
    }

    /**
     * 滑动回调
     * @param viewHolder
     * @param direction
     */
    @Override
    public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {

    }
};

但是在实际使用的时候更多的是通过SimpleCallback,它是继承至ItemTouchHelper.Callback,通过代码来实现一个简单的带有侧滑和拖拽的效果:

ItemTouchHelper.SimpleCallback mCallback =
    new ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP |
            ItemTouchHelper.DOWN | ItemTouchHelper.LEFT |ItemTouchHelper.RIGHT,
            ItemTouchHelper.START | ItemTouchHelper.END) {

        @Override
        public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
            int fromPosition = viewHolder.getAdapterPosition();//当前ViewHolder的position
            int toPosition = target.getAdapterPosition();//目标ViewHolder的position

            //交换位置
            if (fromPosition < toPosition) {
                for (int i = fromPosition; i < toPosition; i++) {
                    Collections.swap(datas, i, i + 1);
                }
            } else {
                for (int i = fromPosition; i > toPosition; i--) {
                    Collections.swap(datas, i, i - 1);
                }
            }
            myAdapter.notifyItemMoved(fromPosition, toPosition);
            return true;
        }


        @Override
        public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
            int position = viewHolder.getAdapterPosition();
            datas.remove(position);//删除数据
            myAdapter.notifyItemRemoved(position); 
        }


        @Override
        public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
            super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
            if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) {
                //滑动时改变Item的透明度
                final float alpha = 1 - Math.abs(dX) / (float) viewHolder.itemView.getWidth();
                viewHolder.itemView.setAlpha(alpha);
                viewHolder.itemView.setTranslationX(dX);
            }
        }
    };

然后看下效果图:

可以看到最基本的拖拽和侧滑都已经实现了。

多种类型布局

RecyclerView的常用功能里面还有就是多种状态布局,也就是用一个RecyclerView实现有多种状态的布局,比如给RecyclerView增加一个Header和Footer
然后中间是常规数据,主要通过Adapter的getItemViewType方法来区分不同的布局。修改Adapter的代码如下:

public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    private Context context;
    private List mDatas;

    public static final int ITME_TYPE_HEADER = 1;
    public static final int ITME_TYPE_CONTENT = 2;
    public static final int ITME_TYPE_BOTTOM = 3;

    private int mHeaderCount = 1;
    private int mBottmoCount = 1;

    public MyAdapter(Context context , List mDatas){

        this.context = context;
        this.mDatas = mDatas;
    }

    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        switch (viewType){
            case ITME_TYPE_HEADER:
                return new HeadViewHolder(LayoutInflater.from(context).inflate(R.layout.item_header,parent,false));
            case ITME_TYPE_CONTENT:
                return new MyViewHolder(LayoutInflater.from(context).inflate(R.layout.item_layout,parent,false));
            case ITME_TYPE_BOTTOM:
                return new BottomViewHolder(LayoutInflater.from(context).inflate(R.layout.item_header,parent,false));
        }
        return null;

    }

    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {

        if (holder instanceof  HeadViewHolder){
            ((HeadViewHolder) holder).textView_header.setText("我是头");
        }else if (holder instanceof  MyViewHolder){
            //注意这里要减去头的数量
            ((MyViewHolder) holder).textView.setText("第"+(position-mHeaderCount));
        }else if (holder instanceof BottomViewHolder){
            ((BottomViewHolder) holder).textView_bottom.setText("我是尾");
        }
    }


    /**
     * 要加上头尾
     * @return
     */
    @Override
    public int getItemCount() {
        return mDatas.size()+mHeaderCount+mBottmoCount;
    }

    /**
     * 根据position来区分布局类型
     * @param position
     * @return
     */
    @Override
    public int getItemViewType(int position) {
        if (positionreturn ITME_TYPE_HEADER;
        }else if(position>= mHeaderCount+mDatas.size()){
            return ITME_TYPE_BOTTOM;
        }else{
            return ITME_TYPE_CONTENT;
        }
    }

    public class MyViewHolder extends RecyclerView.ViewHolder{

        private TextView textView;
        public MyViewHolder(View itemView) {
            super(itemView);
            textView = itemView.findViewById(R.id.textview);

        }
    }

    //头部ViewHolder
    public static class HeadViewHolder extends RecyclerView.ViewHolder {
        private TextView textView_header;
        public HeadViewHolder(View itemView) {
            super(itemView);
            textView_header = itemView.findViewById(R.id.textView1);
        }
    }

    //尾部ViewHodler
    public static class BottomViewHolder extends RecyclerView.ViewHolder {
        private TextView textView_bottom;
        public BottomViewHolder(View itemView) {
            super(itemView);
            textView_bottom = itemView.findViewById(R.id.textView1);
        }
    }
}

效果如图:

RecyclerView常用功能全面总结_第3张图片

总结

RecyclerView真的很强大,很好用。

你可能感兴趣的:(android基础知识)