Android RecyclerView 基本用法&进阶

一、本章我们来介绍一下:RecyclerView
   RecyclerView是support.v7包中的控件,可以说是ListView和GridView的增强升级版。官方描述:A flexible view for providing a limited window into a large data set。

   整体上看RecyclerView架构,提供了一种插拔式的体验,高度的解耦,异常的灵活,通过设置它提供的不同LayoutManager,ItemDecoration , ItemAnimator实现令人瞠目的效果。

二、RecyclerView基本用法:
1、RecyclerView线性布局:

<?xml version="1.0" encoding="utf-8"?>  
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"  
    xmlns:app="http://schemas.android.com/apk/res-auto"  
    xmlns:tools="http://schemas.android.com/tools"  
    android:layout_width="match_parent"  
    android:layout_height="match_parent"  
    tools:context="com.demo.mvp.activity.RecyclerViewActivity">  
  
    <android.support.design.widget.AppBarLayout  
        android:layout_width="match_parent"  
        android:layout_height="wrap_content"  
        android:theme="@style/BaseTheme.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/BaseTheme.PopupOverlay" />  
  
    </android.support.design.widget.AppBarLayout>  
    <!-- RecyclerView  -->  
    <android.support.v7.widget.RecyclerView  
        app:layout_behavior="@string/appbar_scrolling_view_behavior"  
        android:id="@+id/recyclerView"  
        android:layout_width="match_parent"  
        android:layout_height="match_parent"/>  
  
    <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:layout_margin="@dimen/fab_margin"  
        app:srcCompat="@android:drawable/ic_dialog_email" />  
  
</android.support.design.widget.CoordinatorLayout>
Activity代码:

public class RecyclerViewActivity extends AppCompatActivity {  
    private RecyclerView recyclerView;  
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.activity_recycler_view);  
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);  
        setSupportActionBar(toolbar);  
        initView();  
        initData();  
    }  
    private void initView() {  
        recyclerView = (RecyclerView) findViewById(R.id.recyclerView);  
//        //1、线性布局  
//        //初始化线性管理器  
//        LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);  
//        //设置垂直展示还是水平展示  
//        linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);  
//        //设置RecyclerView显示布局  
//        recyclerView.setLayoutManager(linearLayoutManager);  
//        recyclerView.setAdapter(new Adapter(this));  
//  
//        //2、网格布局  
//        //初始化网络布局管理器  
//        GridLayoutManager gridLayoutManager = new GridLayoutManager(this,3,GridLayoutManager.VERTICAL,false);  
//        recyclerView.setLayoutManager(gridLayoutManager);  
//        recyclerView.setAdapter(new Adapter(this));  
   
        //3、瀑布流  
        StaggeredGridLayoutManager staggeredGridLayoutManager = new StaggeredGridLayoutManager(2,StaggeredGridLayoutManager.VERTICAL);  
        recyclerView.setLayoutManager(staggeredGridLayoutManager);  
        recyclerView.setAdapter(new Adapter(this));  
    }  
    private void initData() {  
    }  
   
    /** 
     * 初始化 Adapter 
     */  
    private class Adapter extends RecyclerView.Adapter {  
        private List<String> intArray;  
        private Context mContext;  
        private LayoutInflater inflater;  
   
        public Adapter(Context context) {  
            this.intArray = new ArrayList();  
            inflater = LayoutInflater.from(context);  
            this.mContext = context;  
            for (int i = 0; i < 30; i++) {  
                intArray.add("" + i);  
            }  
        }  
   
        @Override  
        public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {  
            return new ViewHolder(inflater.inflate(R.layout.layout_item_linear, parent,false));  
        }  
   
        @Override  
        public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {  
            ViewHolder viewHolder = (ViewHolder) holder;  
            viewHolder.textView.setText(String.valueOf(position));  
            //实现瀑布流控件大小  
            viewHolder.textView.setHeight(100 + (position % 3) * 50);  
        }  
   
        @Override  
        public int getItemCount() {  
            return intArray.size();  
        }  
    }  
   
    /** 
     * 初始化ViewHolder 
     */  
    private class ViewHolder extends RecyclerView.ViewHolder {  
        private TextView textView;  
   
        public ViewHolder(View itemView) {  
            super(itemView);  
            textView = (TextView) itemView.findViewById(R.id.item_linear_textView);  
        }  
    }  
   
}
item xml文件:

<?xml version="1.0" encoding="utf-8"?>  
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
    xmlns:card_view="http://schemas.android.com/apk/res-auto"  
    android:layout_width="match_parent"  
    android:layout_height="wrap_content"  
    android:background="@color/color_00000000">  
    <android.support.v7.widget.CardView  
        android:id="@+id/card_view"  
        android:layout_width="150dp"  
        android:layout_height="wrap_content"  
        android:layout_gravity="center"  
        card_view:cardCornerRadius="4dp"  
        card_view:cardUseCompatPadding="true">  
   
        <TextView  
            android:id="@+id/item_linear_textView"  
            android:layout_width="match_parent"  
            android:layout_height="wrap_content"  
            android:gravity="center"  
            android:textColor="@color/color_007aff"  
            android:textSize="30dp" />  
    </android.support.v7.widget.CardView>  
</RelativeLayout> 


以上就是RecyclerView基本用法,我们可以通过RecyclerView实现线性布局ListView、网格布局GridView、瀑布流,它相对于ListView、GridView等来说,是没有item点击事件的,需要自己自定义item事件。

二、RecyclerView添加分割线:



1、recyclerView.addItemDecoration()方法的参数为RecyclerView.ItemDecoration,该类为抽象类,官方目前并没有提供默认的实现类,我们先来看源码:

public static abstract class ItemDecoration {  
    /** 
     * Draw any appropriate decorations into the Canvas supplied to the RecyclerView. 
     * Any content drawn by this method will be drawn before the item views are drawn, 
     * and will thus appear underneath the views. 
     * 
     * @param c Canvas to draw into 
     * @param parent RecyclerView this ItemDecoration is drawing into 
     * @param state The current state of RecyclerView 
     */  
    public void onDraw(Canvas c, RecyclerView parent, State state) {  
        onDraw(c, parent);  
    }  
   
    /** 
     * @deprecated 
     * Override {@link #onDraw(Canvas, RecyclerView, RecyclerView.State)} 
     */  
    @Deprecated  
    public void onDraw(Canvas c, RecyclerView parent) {  
    }  
   
    /** 
     * Draw any appropriate decorations into the Canvas supplied to the RecyclerView. 
     * Any content drawn by this method will be drawn after the item views are drawn 
     * and will thus appear over the views. 
     * 
     * @param c Canvas to draw into 
     * @param parent RecyclerView this ItemDecoration is drawing into 
     * @param state The current state of RecyclerView. 
     */  
    public void onDrawOver(Canvas c, RecyclerView parent, State state) {  
        onDrawOver(c, parent);  
    }  
   
    /** 
     * @deprecated 
     * Override {@link #onDrawOver(Canvas, RecyclerView, RecyclerView.State)} 
     */  
    @Deprecated  
    public void onDrawOver(Canvas c, RecyclerView parent) {  
    }  
   
   
    /** 
     * @deprecated 
     * Use {@link #getItemOffsets(Rect, View, RecyclerView, State)} 
     */  
    @Deprecated  
    public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {  
        outRect.set(0, 0, 0, 0);  
    }  
   
    /** 
     * Retrieve any offsets for the given item. Each field of <code>outRect</code> specifies 
     * the number of pixels that the item view should be inset by, similar to padding or margin. 
     * The default implementation sets the bounds of outRect to 0 and returns. 
     * 
     * <p> 
     * If this ItemDecoration does not affect the positioning of item views, it should set 
     * all four fields of <code>outRect</code> (left, top, right, bottom) to zero 
     * before returning. 
     * 
     * <p> 
     * If you need to access Adapter for additional data, you can call 
     * {@link RecyclerView#getChildAdapterPosition(View)} to get the adapter position of the 
     * View. 
     * 
     * @param outRect Rect to receive the output. 
     * @param view    The child view to decorate 
     * @param parent  RecyclerView this ItemDecoration is decorating 
     * @param state   The current state of RecyclerView. 
     */  
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state) {  
        getItemOffsets(outRect, ((LayoutParams) view.getLayoutParams()).getViewLayoutPosition(),  
                parent);  
    }  
}  
通过源码,ItemDecoration类主要是三个方法:

//可以实现类似绘制背景的效果,内容在上面  
public void onDraw(Canvas c, RecyclerView parent, State state)  
//可以绘制在内容的上面,覆盖内容  
public void onDrawOver(Canvas c, RecyclerView parent, State state)  
//可以实现类似padding的效果  
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state)


具体实现分割线:

public class SimpleDividerDecoration extends RecyclerView.ItemDecoration {  
  
    private int dividerHeight;  
    private Paint dividerPaint;  
  
    public SimpleDividerDecoration(Context context) {  
        dividerPaint = new Paint();  
        dividerPaint.setColor(context.getResources().getColor(R.color.colorAccent));  
        dividerHeight = context.getResources().getDimensionPixelSize(R.dimen.divider_height);  
    }  
  
  
    @Override  
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {  
        super.getItemOffsets(outRect, view, parent, state);  
        outRect.bottom = dividerHeight;  
    }  
  
    @Override  
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {  
        int childCount = parent.getChildCount();  
        int left = parent.getPaddingLeft();  
        int right = parent.getWidth() - parent.getPaddingRight();  
  
        for (int i = 0; i < childCount - 1; i++) {  
            View view = parent.getChildAt(i);  
            float top = view.getBottom();  
            float bottom = view.getBottom() + dividerHeight;  
            c.drawRect(left, top, right, bottom, dividerPaint);  
        }  
    }  
}
2、section效果: 还是一样需要自己自定义 ItemDecoration,那么我看一下自定义 SetionDecoration实现:



public static class SectionDecoration extends RecyclerView.ItemDecoration {  
        private DecorationCallback callback;  
        private TextPaint textPaint;  
        private Paint paint;  
        private int topGap;  
        private Paint.FontMetrics fontMetrics;  
  
  
        public SectionDecoration(Context context, DecorationCallback decorationCallback) {  
            Resources res = context.getResources();  
            this.callback = decorationCallback;  
            paint = new Paint();  
            paint.setColor(res.getColor(R.color.colorAccent));  
            textPaint = new TextPaint();  
            textPaint.setTypeface(Typeface.DEFAULT_BOLD);  
            textPaint.setAntiAlias(true);  
            textPaint.setTextSize(80);  
            textPaint.setColor(Color.BLACK);  
            textPaint.getFontMetrics(fontMetrics);  
            textPaint.setTextAlign(Paint.Align.LEFT);  
            fontMetrics = new Paint.FontMetrics();  
            topGap = res.getDimensionPixelSize(R.dimen.item_max_font_size);  
  
  
        }  
  
  
        @Override  
        public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {  
            super.getItemOffsets(outRect, view, parent, state);  
            int pos = parent.getChildAdapterPosition(view);  
            long groupId = callback.getGroupId(pos);  
            if (groupId < 0) return;  
            if (pos == 0 || isFirstInGroup(pos)) {  
                outRect.top = topGap;  
            } else {  
                outRect.top = 0;  
            }  
        }  
  
        @Override  
        public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {  
            super.onDraw(c, parent, state);  
            int left = parent.getPaddingLeft();  
            int right = parent.getWidth() - parent.getPaddingRight();  
            int childCount = parent.getChildCount();  
            for (int i = 0; i < childCount; i++) {  
                View view = parent.getChildAt(i);  
                int position = parent.getChildAdapterPosition(view);  
                long groupId = callback.getGroupId(position);  
                if (groupId < 0) return;  
                String textLine = callback.getGroupFirstLine(position).toUpperCase();  
                if (position == 0 || isFirstInGroup(position)) {  
                    float top = view.getTop() - topGap;  
                    float bottom = view.getTop();  
                    c.drawRect(left, top, right, bottom, paint);  
                    c.drawText(textLine, left, bottom, textPaint);  
                }  
            }  
        }  
  
  
        private boolean isFirstInGroup(int pos) {  
            if (pos == 0) {  
                return true;  
            } else {  
                long prevGroupId = callback.getGroupId(pos - 1);  
                long groupId = callback.getGroupId(pos);  
                return prevGroupId != groupId;  
            }  
        }  
  
        public interface DecorationCallback {  
  
            long getGroupId(int position);  
  
            String getGroupFirstLine(int position);  
        }  
    }
通过代码我们发现:其实是重写getItemOffsets()和onDraw(),下面我们再看看recyclerView.addItemDecoration()

recyclerView.addItemDecoration(new SectionDecoration(this, new SectionDecoration.DecorationCallback() {  
            @Override  
            public long getGroupId(int position) {  
               if (position < 5){  
                   return 1;  
               }else if (position < 10){  
                   return 2;  
               }else {  
                   return 3;  
               }  
            }  
  
            @Override  
            public String getGroupFirstLine(int position) {  
                if (position < 5){  
                    return "0`5";  
                }else if (position < 10){  
                    return "5`10";  
                }else {  
                    return ">=10";  
                }  
            }  
}));
3、我们实现固定头部,我们来看效果



效果大家都看到了,其实跟SetionDecoration一样代码,只是多重写onDrawOver:

public static class SectionDecoration extends RecyclerView.ItemDecoration {  
        private DecorationCallback callback;  
        private TextPaint textPaint;  
        private Paint paint;  
        private int topGap;  
        private Paint.FontMetrics fontMetrics;  
  
  
        public SectionDecoration(Context context, DecorationCallback decorationCallback) {  
            Resources res = context.getResources();  
            this.callback = decorationCallback;  
            paint = new Paint();  
            paint.setColor(res.getColor(R.color.colorAccent));  
            textPaint = new TextPaint();  
            textPaint.setTypeface(Typeface.DEFAULT_BOLD);  
            textPaint.setAntiAlias(true);  
            textPaint.setTextSize(80);  
            textPaint.setColor(Color.BLACK);  
            textPaint.getFontMetrics(fontMetrics);  
            textPaint.setTextAlign(Paint.Align.LEFT);  
            fontMetrics = new Paint.FontMetrics();  
            topGap = res.getDimensionPixelSize(R.dimen.item_max_font_size);  
  
  
        }  
  
  
        @Override  
        public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {  
            super.getItemOffsets(outRect, view, parent, state);  
            int pos = parent.getChildAdapterPosition(view);  
            long groupId = callback.getGroupId(pos);  
            if (groupId < 0) return;  
            if (pos == 0 || isFirstInGroup(pos)) {  
                outRect.top = topGap;  
            } else {  
                outRect.top = 0;  
            }  
        }  
  
        @Override  
        public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {  
            super.onDraw(c, parent, state);  
            int left = parent.getPaddingLeft();  
            int right = parent.getWidth() - parent.getPaddingRight();  
            int childCount = parent.getChildCount();  
            for (int i = 0; i < childCount; i++) {  
                View view = parent.getChildAt(i);  
                int position = parent.getChildAdapterPosition(view);  
                long groupId = callback.getGroupId(position);  
                if (groupId < 0) return;  
                String textLine = callback.getGroupFirstLine(position).toUpperCase();  
                if (position == 0 || isFirstInGroup(position)) {  
                    float top = view.getTop() - topGap;  
                    float bottom = view.getTop();  
                    c.drawRect(left, top, right, bottom, paint);  
                    c.drawText(textLine, left, bottom, textPaint);  
                }  
            }  
        }  
  
        //通过该方式实现固定头部。  
        @Override  
        public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {  
            super.onDrawOver(c, parent, state);  
            int itemCount = state.getItemCount();  
            int childCount = parent.getChildCount();  
            int left = parent.getPaddingLeft();  
            int right = parent.getWidth() - parent.getPaddingRight();  
  
            long preGroupId, groupId = -1;  
            for (int i = 0; i < childCount; i++) {  
                View view = parent.getChildAt(i);  
                int position = parent.getChildAdapterPosition(view);  
  
                preGroupId = groupId;  
                groupId = callback.getGroupId(position);  
                if (groupId < 0 || groupId == preGroupId) continue;  
  
                String textLine = callback.getGroupFirstLine(position).toUpperCase();  
                if (TextUtils.isEmpty(textLine)) continue;  
                int viewBottom = view.getBottom();  
                float textY = Math.max(topGap, view.getTop());  
                if (position + 1 < itemCount) { //下一个和当前不一样移动当前  
                    long nextGroupId = callback.getGroupId(position + 1);  
                    if (nextGroupId != groupId && viewBottom < textY ) {//组内最后一个view进入了header  
                        textY = viewBottom;  
                    }  
                }  
                c.drawRect(left, textY - topGap, right, textY, paint);  
                c.drawText(textLine, left, textY, textPaint);  
            }  
  
        }  
  
        private boolean isFirstInGroup(int pos) {  
            if (pos == 0) {  
                return true;  
            } else {  
                long prevGroupId = callback.getGroupId(pos - 1);  
                long groupId = callback.getGroupId(pos);  
                return prevGroupId != groupId;  
            }  
        }  
  
        public interface DecorationCallback {  
  
            long getGroupId(int position);  
  
            String getGroupFirstLine(int position);  
        }  
    }
以上就是通过recyclerView.addItemDecoration()实现我们想要各种RecyclerView效果,大家可以再研究一下,还有其他各种效果。

三、ItemTouchHelper实现RecyclerView的滑动删除和拖拽:

1、首先看效果图:



2、代码:

/** 
  * dragDirs - 表示拖动的方向,有六个类型的值:LEFT、RIGHT、START、END、UP、DOWN 
  * swipeDirs - 表示滑动的方向,有六个类型的值:LEFT、RIGHT、START、END、UP、DOWN 
  * 如果为0,则表示不触发该操作(滑动or拖动) 
  */  
ItemTouchHelper.Callback callback = new ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP | ItemTouchHelper.DOWN,ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {  
            /** 
             *  设置滑动类型标记 
             * @param recyclerView 
             * @param viewHolder 
             * @return  返回一个整数类型的标识,用于判断Item那种移动行为是允许的 
             */  
            @Override  
            public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {  
                return makeMovementFlags(getDragDirs(recyclerView, viewHolder),getSwipeDirs(recyclerView, viewHolder));  
            }  
            /** 
             *  拖动回调 
             * @param recyclerView 
             * @param viewHolder  拖动的viewholder 
             * @param target   目标位置的viewholder 
             * @return 
             */  
            @Override  
            public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {  
                int from = viewHolder.getAdapterPosition();  
                int to = target.getAdapterPosition();  
                //用于交换在指定列表中的指定位置的元素  
                Collections.swap(adapter.getList(),from , to);  
                adapter.notifyItemMoved(from, to);  
                return true;  
            }  
  
            /** 
             * Item是否支持长按拖动 
             * @return  true  支持长按操作 
             *            false 不支持长按操作 
             */  
            @Override  
            public boolean isLongPressDragEnabled() {  
                return super.isLongPressDragEnabled();  
            }  
  
            /** 
             * 滑动回调 
             * @param viewHolder  滑动ViewHolder 
             * @param direction  滑动方向 
             */  
            @Override  
            public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {  
                //这里我们通过viewHolder获取position  
                int position = viewHolder.getAdapterPosition();  
                adapter.getList().remove(position);  
                adapter.notifyItemRemoved(position);  //RecyclerView特有  
  
  
//                streamInfos.remove(viewHolder.getLayoutPosition());  
//                commonAdapter.notifyDataSetChanged();  //常规写法  
  
  
  
            }  
  
            /** 
             * 是否支持滑动 
             * @return 
             */  
            @Override  
            public boolean isItemViewSwipeEnabled() {  
                return super.isItemViewSwipeEnabled();  
            }  
  
  
            /** 
             * 移动过程回调 
             * 绘制一些动画,渐变等效果 
             * @param c 
             * @param recyclerView 
             * @param viewHolder 
             * @param dX X轴移动的距离 
             * @param dY Y轴移动的距离 
             * @param actionState 当前Item的状态 
             * @param isCurrentlyActive 如果当前被用户操作为true,反之为false 
             */  
            @Override  
            public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean 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);  
                }else {  
                    super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);  
                }  
            }  
            /** 
             * 移动过程中绘制Item 
             * 
             * @param c 
             * @param recyclerView 
             * @param viewHolder 
             * @param dX 
             *          X轴移动的距离 
             * @param dY 
             *          Y轴移动的距离 
             * @param actionState 
             *          当前Item的状态 
             * @param isCurrentlyActive 
             *          如果当前被用户操作为true,反之为false 
             */  
            @Override  
            public void onChildDrawOver(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {  
                super.onChildDrawOver(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);  
            }  
  
            /** 
             * 当选中Item时候会调用该方法,重写此方法可以实现选中时候的一些动画逻辑 
             * @param viewHolder 
             * @param actionState 
             *当前Item的状态 
             *          ItemTouchHelper.ACTION_STATE_IDLE   闲置状态 
             *          ItemTouchHelper.ACTION_STATE_SWIPE  滑动中状态 
             *          ItemTouchHelper#ACTION_STATE_DRAG   拖拽中状态 
             */  
            @Override  
            public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {  
                if(actionState != ItemTouchHelper.ACTION_STATE_IDLE) {  
                    viewHolder.itemView.setBackgroundResource(R.color.colorAccent);  
                }  
                super.onSelectedChanged(viewHolder, actionState);  
            }  
  
            /** 
             * 用户操作完毕或者动画完毕后会被调用 
             * @param recyclerView 
             * @param viewHolder 
             */  
            @Override  
            public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {  
                viewHolder.itemView.setBackgroundResource(R.color.color_ffffff);  
                viewHolder.itemView.setAlpha(1.0f);  
                super.clearView(recyclerView, viewHolder);  
            }  
  
        };  
  
 ItemTouchHelper itemTouchHelper = new ItemTouchHelper(callback);  
 itemTouchHelper.attachToRecyclerView(recyclerView);

代码中有详细说明,这里就不多说了

四、RecyclerView动画ItemAnimator:官方文档传送门

通过官方文档,我们发现RecyclerView.ItemAnimator有两个子类分别是:SimpleItemAnimatorDefaultItemAnimator


1、SimpleItemAnimator抽象类,需要自己实现,需要实现方法如下:

//Item移除回调  
            @Override  
            public boolean animateRemove(RecyclerView.ViewHolder holder) {  
                return false;  
            }  
  
            //Item添加回调  
            @Override  
            public boolean animateAdd(RecyclerView.ViewHolder holder) {  
                ViewCompat.setAlpha(holder.itemView, 0);  
                return false;  
            }  
  
  
            //用于控制添加,移动更新时,其它Item的动画执行  
            @Override  
            public boolean animateMove(RecyclerView.ViewHolder holder, int fromX, int fromY, int toX, int toY) {  
                return false;  
            }  
  
            //Item更新回调  
            @Override  
            public boolean animateChange(RecyclerView.ViewHolder oldHolder, RecyclerView.ViewHolder newHolder, int fromLeft, int fromTop, int toLeft, int toTop) {  
                return false;  
            }  
  
            //真正控制执行动画的地方  
            @Override  
            public void runPendingAnimations() {  
  
            }  
  
            //停止某个Item的动画  
            @Override  
            public void endAnimation(RecyclerView.ViewHolder item) {  
  
            }  
  
            //停止所有动画  
            @Override  
            public void endAnimations() {  
  
            }  
  
            @Override  
            public boolean isRunning() {  
                return false;  
            }
2、DefaultItemAnimator已经帮我们实现对应的动画效果:源码如下


public class DefaultItemAnimator extends SimpleItemAnimator {  
    private static final boolean DEBUG = false;  
  
    private ArrayList<ViewHolder> mPendingRemovals = new ArrayList<>();  
    private ArrayList<ViewHolder> mPendingAdditions = new ArrayList<>();  
    private ArrayList<MoveInfo> mPendingMoves = new ArrayList<>();  
    private ArrayList<ChangeInfo> mPendingChanges = new ArrayList<>();  
  
    ArrayList<ArrayList<ViewHolder>> mAdditionsList = new ArrayList<>();  
    ArrayList<ArrayList<MoveInfo>> mMovesList = new ArrayList<>();  
    ArrayList<ArrayList<ChangeInfo>> mChangesList = new ArrayList<>();  
  
    ArrayList<ViewHolder> mAddAnimations = new ArrayList<>();  
    ArrayList<ViewHolder> mMoveAnimations = new ArrayList<>();  
    ArrayList<ViewHolder> mRemoveAnimations = new ArrayList<>();  
    ArrayList<ViewHolder> mChangeAnimations = new ArrayList<>();  
  
    private static class MoveInfo {  
        public ViewHolder holder;  
        public int fromX, fromY, toX, toY;  
  
        MoveInfo(ViewHolder holder, int fromX, int fromY, int toX, int toY) {  
            this.holder = holder;  
            this.fromX = fromX;  
            this.fromY = fromY;  
            this.toX = toX;  
            this.toY = toY;  
        }  
    }  
  
    private static class ChangeInfo {  
        public ViewHolder oldHolder, newHolder;  
        public int fromX, fromY, toX, toY;  
        private ChangeInfo(ViewHolder oldHolder, ViewHolder newHolder) {  
            this.oldHolder = oldHolder;  
            this.newHolder = newHolder;  
        }  
  
        ChangeInfo(ViewHolder oldHolder, ViewHolder newHolder,  
                int fromX, int fromY, int toX, int toY) {  
            this(oldHolder, newHolder);  
            this.fromX = fromX;  
            this.fromY = fromY;  
            this.toX = toX;  
            this.toY = toY;  
        }  
  
        @Override  
        public String toString() {  
            return "ChangeInfo{" +  
                    "oldHolder=" + oldHolder +  
                    ", newHolder=" + newHolder +  
                    ", fromX=" + fromX +  
                    ", fromY=" + fromY +  
                    ", toX=" + toX +  
                    ", toY=" + toY +  
                    '}';  
        }  
    }  
  
    @Override  
    public void runPendingAnimations() {  
        boolean removalsPending = !mPendingRemovals.isEmpty();  
        boolean movesPending = !mPendingMoves.isEmpty();  
        boolean changesPending = !mPendingChanges.isEmpty();  
        boolean additionsPending = !mPendingAdditions.isEmpty();  
        if (!removalsPending && !movesPending && !additionsPending && !changesPending) {  
            // nothing to animate  
            return;  
        }  
        // First, remove stuff  
        for (ViewHolder holder : mPendingRemovals) {  
            animateRemoveImpl(holder);  
        }  
        mPendingRemovals.clear();  
        // Next, move stuff  
        if (movesPending) {  
            final ArrayList<MoveInfo> moves = new ArrayList<>();  
            moves.addAll(mPendingMoves);  
            mMovesList.add(moves);  
            mPendingMoves.clear();  
            Runnable mover = new Runnable() {  
                @Override  
                public void run() {  
                    for (MoveInfo moveInfo : moves) {  
                        animateMoveImpl(moveInfo.holder, moveInfo.fromX, moveInfo.fromY,  
                                moveInfo.toX, moveInfo.toY);  
                    }  
                    moves.clear();  
                    mMovesList.remove(moves);  
                }  
            };  
            if (removalsPending) {  
                View view = moves.get(0).holder.itemView;  
                ViewCompat.postOnAnimationDelayed(view, mover, getRemoveDuration());  
            } else {  
                mover.run();  
            }  
        }  
        // Next, change stuff, to run in parallel with move animations  
        if (changesPending) {  
            final ArrayList<ChangeInfo> changes = new ArrayList<>();  
            changes.addAll(mPendingChanges);  
            mChangesList.add(changes);  
            mPendingChanges.clear();  
            Runnable changer = new Runnable() {  
                @Override  
                public void run() {  
                    for (ChangeInfo change : changes) {  
                        animateChangeImpl(change);  
                    }  
                    changes.clear();  
                    mChangesList.remove(changes);  
                }  
            };  
            if (removalsPending) {  
                ViewHolder holder = changes.get(0).oldHolder;  
                ViewCompat.postOnAnimationDelayed(holder.itemView, changer, getRemoveDuration());  
            } else {  
                changer.run();  
            }  
        }  
        // Next, add stuff  
        if (additionsPending) {  
            final ArrayList<ViewHolder> additions = new ArrayList<>();  
            additions.addAll(mPendingAdditions);  
            mAdditionsList.add(additions);  
            mPendingAdditions.clear();  
            Runnable adder = new Runnable() {  
                @Override  
                public void run() {  
                    for (ViewHolder holder : additions) {  
                        animateAddImpl(holder);  
                    }  
                    additions.clear();  
                    mAdditionsList.remove(additions);  
                }  
            };  
            if (removalsPending || movesPending || changesPending) {  
                long removeDuration = removalsPending ? getRemoveDuration() : 0;  
                long moveDuration = movesPending ? getMoveDuration() : 0;  
                long changeDuration = changesPending ? getChangeDuration() : 0;  
                long totalDelay = removeDuration + Math.max(moveDuration, changeDuration);  
                View view = additions.get(0).itemView;  
                ViewCompat.postOnAnimationDelayed(view, adder, totalDelay);  
            } else {  
                adder.run();  
            }  
        }  
    }  
  
    @Override  
    public boolean animateRemove(final ViewHolder holder) {  
        resetAnimation(holder);  
        mPendingRemovals.add(holder);  
        return true;  
    }  
  
    private void animateRemoveImpl(final ViewHolder holder) {  
        final View view = holder.itemView;  
        final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view);  
        mRemoveAnimations.add(holder);  
        animation.setDuration(getRemoveDuration())  
                .alpha(0).setListener(new VpaListenerAdapter() {  
            @Override  
            public void onAnimationStart(View view) {  
                dispatchRemoveStarting(holder);  
            }  
  
            @Override  
            public void onAnimationEnd(View view) {  
                animation.setListener(null);  
                ViewCompat.setAlpha(view, 1);  
                dispatchRemoveFinished(holder);  
                mRemoveAnimations.remove(holder);  
                dispatchFinishedWhenDone();  
            }  
        }).start();  
    }  
  
    @Override  
    public boolean animateAdd(final ViewHolder holder) {  
        resetAnimation(holder);  
        ViewCompat.setAlpha(holder.itemView, 0);  
        mPendingAdditions.add(holder);  
        return true;  
    }  
  
    void animateAddImpl(final ViewHolder holder) {  
        final View view = holder.itemView;  
        final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view);  
        mAddAnimations.add(holder);  
        animation.alpha(1).setDuration(getAddDuration()).  
                setListener(new VpaListenerAdapter() {  
                    @Override  
                    public void onAnimationStart(View view) {  
                        dispatchAddStarting(holder);  
                    }  
                    @Override  
                    public void onAnimationCancel(View view) {  
                        ViewCompat.setAlpha(view, 1);  
                    }  
  
                    @Override  
                    public void onAnimationEnd(View view) {  
                        animation.setListener(null);  
                        dispatchAddFinished(holder);  
                        mAddAnimations.remove(holder);  
                        dispatchFinishedWhenDone();  
                    }  
                }).start();  
    }  
  
    @Override  
    public boolean animateMove(final ViewHolder holder, int fromX, int fromY,  
            int toX, int toY) {  
        final View view = holder.itemView;  
        fromX += ViewCompat.getTranslationX(holder.itemView);  
        fromY += ViewCompat.getTranslationY(holder.itemView);  
        resetAnimation(holder);  
        int deltaX = toX - fromX;  
        int deltaY = toY - fromY;  
        if (deltaX == 0 && deltaY == 0) {  
            dispatchMoveFinished(holder);  
            return false;  
        }  
        if (deltaX != 0) {  
            ViewCompat.setTranslationX(view, -deltaX);  
        }  
        if (deltaY != 0) {  
            ViewCompat.setTranslationY(view, -deltaY);  
        }  
        mPendingMoves.add(new MoveInfo(holder, fromX, fromY, toX, toY));  
        return true;  
    }  
  
    void animateMoveImpl(final ViewHolder holder, int fromX, int fromY, int toX, int toY) {  
        final View view = holder.itemView;  
        final int deltaX = toX - fromX;  
        final int deltaY = toY - fromY;  
        if (deltaX != 0) {  
            ViewCompat.animate(view).translationX(0);  
        }  
        if (deltaY != 0) {  
            ViewCompat.animate(view).translationY(0);  
        }  
        // TODO: make EndActions end listeners instead, since end actions aren't called when  
        // vpas are canceled (and can't end them. why?)  
        // need listener functionality in VPACompat for this. Ick.  
        final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view);  
        mMoveAnimations.add(holder);  
        animation.setDuration(getMoveDuration()).setListener(new VpaListenerAdapter() {  
            @Override  
            public void onAnimationStart(View view) {  
                dispatchMoveStarting(holder);  
            }  
            @Override  
            public void onAnimationCancel(View view) {  
                if (deltaX != 0) {  
                    ViewCompat.setTranslationX(view, 0);  
                }  
                if (deltaY != 0) {  
                    ViewCompat.setTranslationY(view, 0);  
                }  
            }  
            @Override  
            public void onAnimationEnd(View view) {  
                animation.setListener(null);  
                dispatchMoveFinished(holder);  
                mMoveAnimations.remove(holder);  
                dispatchFinishedWhenDone();  
            }  
        }).start();  
    }  
  
    @Override  
    public boolean animateChange(ViewHolder oldHolder, ViewHolder newHolder,  
            int fromX, int fromY, int toX, int toY) {  
        if (oldHolder == newHolder) {  
            // Don't know how to run change animations when the same view holder is re-used.  
            // run a move animation to handle position changes.  
            return animateMove(oldHolder, fromX, fromY, toX, toY);  
        }  
        final float prevTranslationX = ViewCompat.getTranslationX(oldHolder.itemView);  
        final float prevTranslationY = ViewCompat.getTranslationY(oldHolder.itemView);  
        final float prevAlpha = ViewCompat.getAlpha(oldHolder.itemView);  
        resetAnimation(oldHolder);  
        int deltaX = (int) (toX - fromX - prevTranslationX);  
        int deltaY = (int) (toY - fromY - prevTranslationY);  
        // recover prev translation state after ending animation  
        ViewCompat.setTranslationX(oldHolder.itemView, prevTranslationX);  
        ViewCompat.setTranslationY(oldHolder.itemView, prevTranslationY);  
        ViewCompat.setAlpha(oldHolder.itemView, prevAlpha);  
        if (newHolder != null) {  
            // carry over translation values  
            resetAnimation(newHolder);  
            ViewCompat.setTranslationX(newHolder.itemView, -deltaX);  
            ViewCompat.setTranslationY(newHolder.itemView, -deltaY);  
            ViewCompat.setAlpha(newHolder.itemView, 0);  
        }  
        mPendingChanges.add(new ChangeInfo(oldHolder, newHolder, fromX, fromY, toX, toY));  
        return true;  
    }  
  
    void animateChangeImpl(final ChangeInfo changeInfo) {  
        final ViewHolder holder = changeInfo.oldHolder;  
        final View view = holder == null ? null : holder.itemView;  
        final ViewHolder newHolder = changeInfo.newHolder;  
        final View newView = newHolder != null ? newHolder.itemView : null;  
        if (view != null) {  
            final ViewPropertyAnimatorCompat oldViewAnim = ViewCompat.animate(view).setDuration(  
                    getChangeDuration());  
            mChangeAnimations.add(changeInfo.oldHolder);  
            oldViewAnim.translationX(changeInfo.toX - changeInfo.fromX);  
            oldViewAnim.translationY(changeInfo.toY - changeInfo.fromY);  
            oldViewAnim.alpha(0).setListener(new VpaListenerAdapter() {  
                @Override  
                public void onAnimationStart(View view) {  
                    dispatchChangeStarting(changeInfo.oldHolder, true);  
                }  
  
                @Override  
                public void onAnimationEnd(View view) {  
                    oldViewAnim.setListener(null);  
                    ViewCompat.setAlpha(view, 1);  
                    ViewCompat.setTranslationX(view, 0);  
                    ViewCompat.setTranslationY(view, 0);  
                    dispatchChangeFinished(changeInfo.oldHolder, true);  
                    mChangeAnimations.remove(changeInfo.oldHolder);  
                    dispatchFinishedWhenDone();  
                }  
            }).start();  
        }  
        if (newView != null) {  
            final ViewPropertyAnimatorCompat newViewAnimation = ViewCompat.animate(newView);  
            mChangeAnimations.add(changeInfo.newHolder);  
            newViewAnimation.translationX(0).translationY(0).setDuration(getChangeDuration()).  
                    alpha(1).setListener(new VpaListenerAdapter() {  
                @Override  
                public void onAnimationStart(View view) {  
                    dispatchChangeStarting(changeInfo.newHolder, false);  
                }  
                @Override  
                public void onAnimationEnd(View view) {  
                    newViewAnimation.setListener(null);  
                    ViewCompat.setAlpha(newView, 1);  
                    ViewCompat.setTranslationX(newView, 0);  
                    ViewCompat.setTranslationY(newView, 0);  
                    dispatchChangeFinished(changeInfo.newHolder, false);  
                    mChangeAnimations.remove(changeInfo.newHolder);  
                    dispatchFinishedWhenDone();  
                }  
            }).start();  
        }  
    }  
  
    private void endChangeAnimation(List<ChangeInfo> infoList, ViewHolder item) {  
        for (int i = infoList.size() - 1; i >= 0; i--) {  
            ChangeInfo changeInfo = infoList.get(i);  
            if (endChangeAnimationIfNecessary(changeInfo, item)) {  
                if (changeInfo.oldHolder == null && changeInfo.newHolder == null) {  
                    infoList.remove(changeInfo);  
                }  
            }  
        }  
    }  
  
    private void endChangeAnimationIfNecessary(ChangeInfo changeInfo) {  
        if (changeInfo.oldHolder != null) {  
            endChangeAnimationIfNecessary(changeInfo, changeInfo.oldHolder);  
        }  
        if (changeInfo.newHolder != null) {  
            endChangeAnimationIfNecessary(changeInfo, changeInfo.newHolder);  
        }  
    }  
    private boolean endChangeAnimationIfNecessary(ChangeInfo changeInfo, ViewHolder item) {  
        boolean oldItem = false;  
        if (changeInfo.newHolder == item) {  
            changeInfo.newHolder = null;  
        } else if (changeInfo.oldHolder == item) {  
            changeInfo.oldHolder = null;  
            oldItem = true;  
        } else {  
            return false;  
        }  
        ViewCompat.setAlpha(item.itemView, 1);  
        ViewCompat.setTranslationX(item.itemView, 0);  
        ViewCompat.setTranslationY(item.itemView, 0);  
        dispatchChangeFinished(item, oldItem);  
        return true;  
    }  
  
    @Override  
    public void endAnimation(ViewHolder item) {  
        final View view = item.itemView;  
        // this will trigger end callback which should set properties to their target values.  
        ViewCompat.animate(view).cancel();  
        // TODO if some other animations are chained to end, how do we cancel them as well?  
        for (int i = mPendingMoves.size() - 1; i >= 0; i--) {  
            MoveInfo moveInfo = mPendingMoves.get(i);  
            if (moveInfo.holder == item) {  
                ViewCompat.setTranslationY(view, 0);  
                ViewCompat.setTranslationX(view, 0);  
                dispatchMoveFinished(item);  
                mPendingMoves.remove(i);  
            }  
        }  
        endChangeAnimation(mPendingChanges, item);  
        if (mPendingRemovals.remove(item)) {  
            ViewCompat.setAlpha(view, 1);  
            dispatchRemoveFinished(item);  
        }  
        if (mPendingAdditions.remove(item)) {  
            ViewCompat.setAlpha(view, 1);  
            dispatchAddFinished(item);  
        }  
  
        for (int i = mChangesList.size() - 1; i >= 0; i--) {  
            ArrayList<ChangeInfo> changes = mChangesList.get(i);  
            endChangeAnimation(changes, item);  
            if (changes.isEmpty()) {  
                mChangesList.remove(i);  
            }  
        }  
        for (int i = mMovesList.size() - 1; i >= 0; i--) {  
            ArrayList<MoveInfo> moves = mMovesList.get(i);  
            for (int j = moves.size() - 1; j >= 0; j--) {  
                MoveInfo moveInfo = moves.get(j);  
                if (moveInfo.holder == item) {  
                    ViewCompat.setTranslationY(view, 0);  
                    ViewCompat.setTranslationX(view, 0);  
                    dispatchMoveFinished(item);  
                    moves.remove(j);  
                    if (moves.isEmpty()) {  
                        mMovesList.remove(i);  
                    }  
                    break;  
                }  
            }  
        }  
        for (int i = mAdditionsList.size() - 1; i >= 0; i--) {  
            ArrayList<ViewHolder> additions = mAdditionsList.get(i);  
            if (additions.remove(item)) {  
                ViewCompat.setAlpha(view, 1);  
                dispatchAddFinished(item);  
                if (additions.isEmpty()) {  
                    mAdditionsList.remove(i);  
                }  
            }  
        }  
  
        // animations should be ended by the cancel above.  
        //noinspection PointlessBooleanExpression,ConstantConditions  
        if (mRemoveAnimations.remove(item) && DEBUG) {  
            throw new IllegalStateException("after animation is cancelled, item should not be in "  
                    + "mRemoveAnimations list");  
        }  
  
        //noinspection PointlessBooleanExpression,ConstantConditions  
        if (mAddAnimations.remove(item) && DEBUG) {  
            throw new IllegalStateException("after animation is cancelled, item should not be in "  
                    + "mAddAnimations list");  
        }  
  
        //noinspection PointlessBooleanExpression,ConstantConditions  
        if (mChangeAnimations.remove(item) && DEBUG) {  
            throw new IllegalStateException("after animation is cancelled, item should not be in "  
                    + "mChangeAnimations list");  
        }  
  
        //noinspection PointlessBooleanExpression,ConstantConditions  
        if (mMoveAnimations.remove(item) && DEBUG) {  
            throw new IllegalStateException("after animation is cancelled, item should not be in "  
                    + "mMoveAnimations list");  
        }  
        dispatchFinishedWhenDone();  
    }  
  
    private void resetAnimation(ViewHolder holder) {  
        AnimatorCompatHelper.clearInterpolator(holder.itemView);  
        endAnimation(holder);  
    }  
  
    @Override  
    public boolean isRunning() {  
        return (!mPendingAdditions.isEmpty() ||  
                !mPendingChanges.isEmpty() ||  
                !mPendingMoves.isEmpty() ||  
                !mPendingRemovals.isEmpty() ||  
                !mMoveAnimations.isEmpty() ||  
                !mRemoveAnimations.isEmpty() ||  
                !mAddAnimations.isEmpty() ||  
                !mChangeAnimations.isEmpty() ||  
                !mMovesList.isEmpty() ||  
                !mAdditionsList.isEmpty() ||  
                !mChangesList.isEmpty());  
    }  
  
    /** 
     * Check the state of currently pending and running animations. If there are none 
     * pending/running, call {@link #dispatchAnimationsFinished()} to notify any 
     * listeners. 
     */  
    void dispatchFinishedWhenDone() {  
        if (!isRunning()) {  
            dispatchAnimationsFinished();  
        }  
    }  
  
    @Override  
    public void endAnimations() {  
        int count = mPendingMoves.size();  
        for (int i = count - 1; i >= 0; i--) {  
            MoveInfo item = mPendingMoves.get(i);  
            View view = item.holder.itemView;  
            ViewCompat.setTranslationY(view, 0);  
            ViewCompat.setTranslationX(view, 0);  
            dispatchMoveFinished(item.holder);  
            mPendingMoves.remove(i);  
        }  
        count = mPendingRemovals.size();  
        for (int i = count - 1; i >= 0; i--) {  
            ViewHolder item = mPendingRemovals.get(i);  
            dispatchRemoveFinished(item);  
            mPendingRemovals.remove(i);  
        }  
        count = mPendingAdditions.size();  
        for (int i = count - 1; i >= 0; i--) {  
            ViewHolder item = mPendingAdditions.get(i);  
            View view = item.itemView;  
            ViewCompat.setAlpha(view, 1);  
            dispatchAddFinished(item);  
            mPendingAdditions.remove(i);  
        }  
        count = mPendingChanges.size();  
        for (int i = count - 1; i >= 0; i--) {  
            endChangeAnimationIfNecessary(mPendingChanges.get(i));  
        }  
        mPendingChanges.clear();  
        if (!isRunning()) {  
            return;  
        }  
  
        int listCount = mMovesList.size();  
        for (int i = listCount - 1; i >= 0; i--) {  
            ArrayList<MoveInfo> moves = mMovesList.get(i);  
            count = moves.size();  
            for (int j = count - 1; j >= 0; j--) {  
                MoveInfo moveInfo = moves.get(j);  
                ViewHolder item = moveInfo.holder;  
                View view = item.itemView;  
                ViewCompat.setTranslationY(view, 0);  
                ViewCompat.setTranslationX(view, 0);  
                dispatchMoveFinished(moveInfo.holder);  
                moves.remove(j);  
                if (moves.isEmpty()) {  
                    mMovesList.remove(moves);  
                }  
            }  
        }  
        listCount = mAdditionsList.size();  
        for (int i = listCount - 1; i >= 0; i--) {  
            ArrayList<ViewHolder> additions = mAdditionsList.get(i);  
            count = additions.size();  
            for (int j = count - 1; j >= 0; j--) {  
                ViewHolder item = additions.get(j);  
                View view = item.itemView;  
                ViewCompat.setAlpha(view, 1);  
                dispatchAddFinished(item);  
                additions.remove(j);  
                if (additions.isEmpty()) {  
                    mAdditionsList.remove(additions);  
                }  
            }  
        }  
        listCount = mChangesList.size();  
        for (int i = listCount - 1; i >= 0; i--) {  
            ArrayList<ChangeInfo> changes = mChangesList.get(i);  
            count = changes.size();  
            for (int j = count - 1; j >= 0; j--) {  
                endChangeAnimationIfNecessary(changes.get(j));  
                if (changes.isEmpty()) {  
                    mChangesList.remove(changes);  
                }  
            }  
        }  
  
        cancelAll(mRemoveAnimations);  
        cancelAll(mMoveAnimations);  
        cancelAll(mAddAnimations);  
        cancelAll(mChangeAnimations);  
  
        dispatchAnimationsFinished();  
    }  
  
    void cancelAll(List<ViewHolder> viewHolders) {  
        for (int i = viewHolders.size() - 1; i >= 0; i--) {  
            ViewCompat.animate(viewHolders.get(i).itemView).cancel();  
        }  
    }  
  
    /** 
     * {@inheritDoc} 
     * <p> 
     * If the payload list is not empty, DefaultItemAnimator returns <code>true</code>. 
     * When this is the case: 
     * <ul> 
     * <li>If you override {@link #animateChange(ViewHolder, ViewHolder, int, int, int, int)}, both 
     * ViewHolder arguments will be the same instance. 
     * </li> 
     * <li> 
     * If you are not overriding {@link #animateChange(ViewHolder, ViewHolder, int, int, int, int)}, 
     * then DefaultItemAnimator will call {@link #animateMove(ViewHolder, int, int, int, int)} and 
     * run a move animation instead. 
     * </li> 
     * </ul> 
     */  
    @Override  
    public boolean canReuseUpdatedViewHolder(@NonNull ViewHolder viewHolder,  
            @NonNull List<Object> payloads) {  
        return !payloads.isEmpty() || super.canReuseUpdatedViewHolder(viewHolder, payloads);  
    }  
  
    private static class VpaListenerAdapter implements ViewPropertyAnimatorListener {  
        VpaListenerAdapter() {  
        }  
  
        @Override  
        public void onAnimationStart(View view) {}  
  
        @Override  
        public void onAnimationEnd(View view) {}  
  
        @Override  
        public void onAnimationCancel(View view) {}  
    }  
}

大家在实现自定义动画效果的时候,可以参考DefaultItemAnimator继承SimpleItemAnimator 。下面推荐一种继承DefaultItemAnimator实现自定义动画:传送门
效果图:



五、RecyclerView实现下拉刷新滚动加载更多
1、下来刷新,主要RecyclerView的onTouchEvent事件进行监听。

@Override  
   public boolean onTouchEvent(MotionEvent ev) {  
       if (mLastY == -1) {  
           mLastY = ev.getRawY();  
       }  
       switch (ev.getAction()) {  
           case MotionEvent.ACTION_DOWN:  
               mLastY = ev.getRawY();  
               break;  
           case MotionEvent.ACTION_MOVE:  
               final float deltaY = ev.getRawY() - mLastY;  
               mLastY = ev.getRawY();  
               if (isOnTop() && pullRefreshEnabled && appbarState == AppBarStateChangeListener.State.EXPANDED) {  
                   mRefreshHeader.onMove(deltaY / DRAG_RATE);  //设置头显示布局的大小
                   if (mRefreshHeader.getVisibleHeight() > 1 && mRefreshHeader.getState() < ArrowRefreshHeader.STATE_REFRESHING) {  
                       return false;  
                   }  
               }  
               break;  
           default:  
               mLastY = -1; // reset  
               if (isOnTop() && pullRefreshEnabled && appbarState == AppBarStateChangeListener.State.EXPANDED) {  
                   if (mRefreshHeader.releaseAction()) {  
                       if (mLoadingListener != null) {  
                           mLoadingListener.onRefresh();  
                       }  
                   }  
               }  
               break;  
       }  
       return super.onTouchEvent(ev);  
   }  
2、其实RecyclerView滚动加载更多主要是对RecyclerView的滑动进行监听:RecyclerView重写onScrollStateChanged方法

@Override  
   public void onScrollStateChanged(int state) {  
       super.onScrollStateChanged(state);  
       if (state == RecyclerView.SCROLL_STATE_IDLE && mLoadingListener != null && !isLoadingData && loadingMoreEnabled) {  
           LayoutManager layoutManager = getLayoutManager();  //得到当前布局管理器  
           int lastVisibleItemPosition;   
         //逐一判断,然后得到最后一个item的position  
		 if (layoutManager instanceof GridLayoutManager) {  
			lastVisibleItemPosition = ((GridLayoutManager) layoutManager).findLastVisibleItemPosition();  
		} else if (layoutManager instanceof StaggeredGridLayoutManager) {  
			int[] into = new int[((StaggeredGridLayoutManager) layoutManager).getSpanCount()];  
			((StaggeredGridLayoutManager) layoutManager).findLastVisibleItemPositions(into);  
			lastVisibleItemPosition = findMax(into);  
		} else {  
			lastVisibleItemPosition = ((LinearLayoutManager) layoutManager).findLastVisibleItemPosition();  
		}  
		//判断所有item总数大于0、最后一条item大于总共item数、当前是最后item是否显示、RecyclerView是否在刷新状态  
            if (layoutManager.getChildCount() > 0  
                    && lastVisibleItemPosition >= layoutManager.getItemCount() - 1 && layoutManager.getItemCount() > layoutManager.getChildCount() && !isNoMore && mRefreshHeader.getState() < ArrowRefreshHeader.STATE_REFRESHING) {  
                isLoadingData = true;  
                if (mFootView instanceof LoadingMoreFooter) {  
                    ((LoadingMoreFooter) mFootView).setState(LoadingMoreFooter.STATE_LOADING);  
                } else {  
                    mFootView.setVisibility(View.VISIBLE);  
                }  
                mLoadingListener.onLoadMore();  
            }  
        }  
    }  
也有的是这样做的:

recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {  
           @Override  
           public void onScrollStateChanged(RecyclerView recyclerView, int newState) {  
               super.onScrollStateChanged(recyclerView, newState);  
           }  
  
           @Override  
           public void onScrolled(RecyclerView recyclerView, int dx, int dy) {  
               super.onScrolled(recyclerView, dx, dy);  
           }  
       });  

以上就是RecyclerView学习的基本和进阶的相关知识了。如有不足之处望指出。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值