关于Recyclerview使用小记

网上很多东西都比较琐碎,丢三落四的很常见,recyclerview作为最常用的android5.0新增组件,自己专门研究了一天,在此做一个简单的整合。关于recyclerview用法参考了很多大神的代码,很多代码只是拿来用,还未认真去思考原理,虽然我只是搬运工,但是相信文章还是值得一看的。

1. 基本用法

关于介绍和基础用法,可以参考大神的帖子:http://blog.csdn.net/lmj623565791/article/details/45059587
基本流程和listview是一致的:获取recyclerView对象,初始化并设置LinearLayoutManager、初始化并设置Adapter。
recycler view功能强大,但是没有ItemClick监听、没有HeaderView、没有FooterView、没有EmptyView,这些东西都需要自定义。。以下是最基础的初始化代码片段

recyclerView = (CustomEmptyRecyclerView) findViewById(R.id.mainactivity_recyclerview);
        LinearLayoutManager lm = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
        recyclerView.setLayoutManager(lm);
        initDatas();
        adapter = new CustomRecyclerViewAdapter(this, datas);
        //GridLayoutManager lm = new GridLayoutManager(this,4);
        recyclerView.setAdapter(adapter);

2.设置item点击监听

先自己定义一个接口

public interface OnItemClickListener {
    void onClick(View view, int position);
}

在Adapter中声明该接口,通过setXxxListener(OnItemClickListener listener)方式,传入.

 public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
        this.onItemClickListener = onItemClickListener;
    }

当viewholder被点击之后调用this.onItemClickListener.onClick()方法,至此点击监听完毕。

 holder.holderView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (onItemClickListener != null) {
                    onItemClickListener.onClick(v, position);
                }
            }
        });

3. 下拉刷新、上拉加载更多

关于recycler view的下拉刷新和上拉加载更多,直接使用的大神的自定义View,相当给力,且用法简单。
地址奉上:https://github.com/nuptboyzhb/SuperSwipeRefreshLayout。用法上边有介绍,直接拿来用的,丝毫不用修改,向大神致敬。

4.自定义添加删除动画

关于自定义添加删除动画还没有研读完全,但是简单的修改一下,应付项目还是够用了。参考了这位大神的博客http://blog.csdn.net/hongtao_studio/article/details/50733118,讲解的比较细致。这里直接放上基本无修改的大神核心类完整代码:
使用方法就一句话:
recyclerView.setItemAnimator(new CustomItemAnimator());

/**
* 修改动画需要修改三个地方,以自定义addItem动画为例子,需要修改的地方有
* animateAdd
* animateAddImpl
* endAnimations()和endAnimation()的addItem的相关部分
*
*/
public class CustomItemAnimator extends SimpleItemAnimator {

private static final boolean DEBUG = false;
private ArrayList<ViewHolder> mPendingAdditions = new ArrayList<>();
private ArrayList<ViewHolder> mPendingRemovals = new ArrayList<>();
private ArrayList<MoveInfo> mPendingMoves = new ArrayList<>();
private ArrayList<ChangeInfo> mPendingChanges = new ArrayList<>();

private ArrayList<ArrayList<ViewHolder>> mAdditionsList = new ArrayList<>();
private ArrayList<ArrayList<MoveInfo>> mMovesList = new ArrayList<>();
private ArrayList<ArrayList<ChangeInfo>> mChangesList = new ArrayList<>();

private ArrayList<ViewHolder> mAddAnimations = new ArrayList<>();
private ArrayList<ViewHolder> mMoveAnimations = new ArrayList<>();
private ArrayList<ViewHolder> mRemoveAnimations = new ArrayList<>();
private ArrayList<ViewHolder> mChangeAnimations = new ArrayList<>();

private static class MoveInfo {

    public ViewHolder holder;
    public int fromX, fromY, toX, toY;

    private 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;
    }

    private 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 +
                '}';
    }
}


public CustomItemAnimator(){
    setAddDuration(200);
    setRemoveDuration(200);
}
@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) {
        return;
    }
    for (ViewHolder holder : mPendingRemovals) {
        animateRemoveImpl(holder);
    }
    mPendingRemovals.clear();
    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();
        }
    }
    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();
        }
    }
    if (additionsPending) {
        final ArrayList<ViewHolder> additions = new ArrayList<>();
        additions.addAll(mPendingAdditions);
        mAdditionsList.add(additions);
        mPendingAdditions.clear();
        Runnable adder = new Runnable() {
            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()).translationX(view.getWidth()).alpha(0)
            .setListener(new VpaListenerAdapter() {
                @Override
                public void onAnimationStart(View view) {
                    dispatchRemoveStarting(holder);
                }

                @Override
                public void onAnimationEnd(View view) {
                    animation.setListener(null);
                    ViewCompat.setTranslationX(view, 0);
                    ViewCompat.setAlpha(view, 1);
                    dispatchRemoveFinished(holder);
                    mRemoveAnimations.remove(holder);
                    dispatchFinishedWhenDone();
                }
            }).start();
}

@Override
public boolean animateAdd(final ViewHolder holder) {
    resetAnimation(holder);
    ViewCompat.setScaleX(holder.itemView,0);
    ViewCompat.setScaleY(holder.itemView,0);
    ViewCompat.setAlpha(holder.itemView,0);
    /*ViewCompat.setTranslationY(holder.itemView, -holder.itemView.getHeight());
    ViewCompat.setAlpha(holder.itemView, 0);*/
    mPendingAdditions.add(holder);
    return true;
}

private void animateAddImpl(final ViewHolder holder) {
    final View view = holder.itemView;
    final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view);
    mAddAnimations.add(holder);
    /*animation.translationY(0).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);
                    ViewCompat.setTranslationY(view, 0);
                }

                @Override
                public void onAnimationEnd(View view) {
                    animation.setListener(null);
                    dispatchAddFinished(holder);
                    mAddAnimations.remove(holder);
                    dispatchFinishedWhenDone();
                }
            }).start();*/
    animation.scaleX(1).scaleY(1).alpha(1).setDuration(getAddDuration()).
            setListener(new VpaListenerAdapter() {
                @Override
                public void onAnimationStart(View view) {
                    dispatchAddStarting(holder);
                }

                @Override
                public void onAnimationCancel(View view) {
                    ViewCompat.setScaleY(view,1);
                    ViewCompat.setScaleX(view,1);
                    ViewCompat.setAlpha(view, 1);

                    /*ViewCompat.setAlpha(view, 1);
                    ViewCompat.setTranslationY(view, 0);*/
                }

                @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));

    Log.i("Tao", toY + "");

    return true;
}

private 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);
    }

    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) {
        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);
    ViewCompat.setTranslationX(oldHolder.itemView, prevTranslationX);
    ViewCompat.setTranslationY(oldHolder.itemView, prevTranslationY);
    ViewCompat.setAlpha(oldHolder.itemView, prevAlpha);
    ViewCompat.setRotation(oldHolder.itemView, 0);
    if (newHolder != null) {
        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;
}

private 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).rotation(360)
                .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;
    ViewCompat.animate(view).cancel();
    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);
            ViewCompat.setTranslationY(view, 0);*/
            ViewCompat.setScaleX(view,1);
            ViewCompat.setScaleY(view,1);
            ViewCompat.setAlpha(view,1);

            dispatchAddFinished(item);
            if (additions.isEmpty()) {
                mAdditionsList.remove(i);
            }
        }
    }

    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());
}

private 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);
ViewCompat.setScaleX(view,1);
ViewCompat.setScaleY(view,1);
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();
    }
}

private static class VpaListenerAdapter implements ViewPropertyAnimatorListener {

    @Override
    public void onAnimationStart(View view) {
    }

    @Override
    public void onAnimationEnd(View view) {
    }

    @Override
    public void onAnimationCancel(View view) {
    }
}
}

5.滑动删除item、拖动排序列表

关于滑动删除和拖动排序,需要用到ItemTouchHelper.Callback条目被触摸的回调,以及用ItemTouchHelper将recycler view与之绑定。

itemTouchCallback = new CustomTouchHelperCallback(adapter);
        itemTouchHelper = new ItemTouchHelper(itemTouchCallback);
        itemTouchHelper.attachToRecyclerView(recyclerView);

ItemTouchHelper.Callback有几个比较重要的方法:
getMovementFlags ()返回支持哪些方向的事件(貌似拖动排序事件和左右滑动事件不能同时支持,拖动会覆盖左右滑动)。
onMove()拖动导致的移动item的时候回调该方法。
onSwiped()左右滑动过半的时候出发该方法。
onSelectedChanged()当item被选中(准备拖动时)出发该方法,可以通过一些缩放动画,表示被选中了
clearView()与onSelectedChanged相反,就是当item拖拽完放手后触发,可以通过反向的缩放动画,恢复原状。
isItemViewSwipeEnabled()返回值表示是否支持左右滑动
附上一个完整的自定义Callback:
相关接口:

public interface OnItemChangeListener {
    boolean onItemMove(int fromPosition, int toPosition);
    void onItemDismiss(int position);
}
public interface OnItemSelectListener {
    void onItemSelected();
    void onItemIdle();
}

完整Callback:

package recyclerview.demo.lbb.com.recyclerviewdemo.customview;

import android.graphics.Canvas;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.helper.ItemTouchHelper;
import android.util.Log;
import recyclerview.demo.lbb.com.recyclerviewdemo.listenerinterface.OnItemChangeListener;
import recyclerview.demo.lbb.com.recyclerviewdemo.listenerinterface.OnItemSelectListener;
    public class CustomTouchHelperCallback extends ItemTouchHelper.Callback {

    public static final float ALPHA_FULL = 1.0f;

    private final OnItemChangeListener adapter;

    public boolean flag;

    public CustomTouchHelperCallback(OnItemChangeListener adapter) {
        this.adapter = adapter;
    }

    /**
     * 支持长按开始拖拽
     *
     * @return
     */
    @Override
    public boolean isLongPressDragEnabled() {
        return true;
    }

    /**
     * 支持左右滑动
     *
     * @return
     */
    @Override
    public boolean isItemViewSwipeEnabled() {
        return true;
    }

    @Override
    public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
        //滑动的时候支持的方向
        int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
        //拖拽的时候支持的方向
        int swipeFlags = ItemTouchHelper.START | ItemTouchHelper.END;

        //必须调用该方法告诉ItemTouchHelper支持的flags
        if (flag)
            return makeMovementFlags(dragFlags, 0);
        else
            return makeMovementFlags(0, swipeFlags);
//        return makeMovementFlags(dragFlags, swipeFlags);
//        return makeMovementFlags(dragFlags, 0);
//        return makeMovementFlags(0, swipeFlags);
    }

    /**
     * Item移动的时候调用该方法
     *
     * @param recyclerView
     * @param viewHolder
     * @param target
     * @return
     */
    @Override
    public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
        if (viewHolder.getItemViewType() != target.getItemViewType()) {
            return false;
        }
        adapter.onItemMove(viewHolder.getAdapterPosition(), target.getAdapterPosition());
        return true;
    }

    /**
     * Item滑动的时候调用该方法
     *
     * @param viewHolder
     * @param direction
     */
    @Override
    public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
        adapter.onItemDismiss(viewHolder.getAdapterPosition());
    }

    @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 = ALPHA_FULL - 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);
        }

    }

    @Override
    public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
        if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) {
            Log.d("ACTION_STATE_IDLE", "ACTION_STATE_IDLE");
            if (viewHolder instanceof OnItemSelectListener) {
                Log.d("instanceof", "instanceof");
                OnItemSelectListener itemViewHolder = (OnItemSelectListener) viewHolder;
                itemViewHolder.onItemSelected();
            }
        }
        super.onSelectedChanged(viewHolder, actionState);
    }

    @Override
    public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
        super.clearView(recyclerView, viewHolder);

        Log.d("clearView", "clearView");
        viewHolder.itemView.setAlpha(ALPHA_FULL);

        if (viewHolder instanceof OnItemSelectListener) {
            OnItemSelectListener itemViewHolder = (OnItemSelectListener) viewHolder;
            itemViewHolder.onItemIdle();
        }
    }

6.添加自定义分割线

使用自定义颜色和宽度(高度)的分割线

 recyclerView.addItemDecoration(new CustomRecycleViewDivider(this, LinearLayoutManager.HORIZONTAL, 30, Color.parseColor("#ffffff")));

大神写的万能分割线,直接上完整代码

public class CustomRecycleViewDivider extends RecyclerView.ItemDecoration {

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

    /**
     * 默认分割线:高度为2px,颜色为灰色
     *
     * @param context
     * @param orientation 列表方向
     */
    public CustomRecycleViewDivider(Context context, int orientation) {
        if (orientation != LinearLayoutManager.VERTICAL && orientation != LinearLayoutManager.HORIZONTAL) {
            throw new IllegalArgumentException("请输入正确的参数!");
        }
        mOrientation = orientation;

        final TypedArray a = context.obtainStyledAttributes(ATTRS);
        mDivider = a.getDrawable(0);
        a.recycle();
    }

    /**
     * 自定义分割线
     *
     * @param context
     * @param orientation 列表方向
     * @param drawableId  分割线图片
     */
    public CustomRecycleViewDivider(Context context, int orientation, int drawableId) {
        this(context, orientation);
        mDivider = ContextCompat.getDrawable(context, drawableId);
        mDividerHeight = mDivider.getIntrinsicHeight();
    }

    /**
     * 自定义分割线
     *
     * @param context
     * @param orientation   列表方向
     * @param dividerHeight 分割线高度
     * @param dividerColor  分割线颜色
     */
    public CustomRecycleViewDivider(Context context, int orientation, int dividerHeight, int dividerColor) {
        this(context, orientation);
        mDividerHeight = dividerHeight;
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setColor(dividerColor);
        mPaint.setStyle(Paint.Style.FILL);
    }


    //获取分割线尺寸
    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        super.getItemOffsets(outRect, view, parent, state);
        outRect.set(0, 0, 0, mDividerHeight);
    }

    //绘制分割线
    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
        super.onDraw(c, parent, state);
        if (mOrientation == LinearLayoutManager.VERTICAL) {
            drawVertical(c, parent);
        } else {
            drawHorizontal(c, parent);
        }
    }

    //绘制横向 item 分割线
    private void drawHorizontal(Canvas canvas, RecyclerView parent) {
        final int left = parent.getPaddingLeft();
        final int right = parent.getMeasuredWidth() - parent.getPaddingRight();
        final int childSize = parent.getChildCount();
        for (int i = 0; i < childSize; i++) {
            final View child = parent.getChildAt(i);
            RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) child.getLayoutParams();
            final int top = child.getBottom() + layoutParams.bottomMargin;
            final int bottom = top + mDividerHeight;
            if (mDivider != null) {
                mDivider.setBounds(left, top, right, bottom);
                mDivider.draw(canvas);
            }
            if (mPaint != null) {
                canvas.drawRect(left, top, right, bottom, mPaint);
            }
        }
    }

    //绘制纵向 item 分割线
    private void drawVertical(Canvas canvas, RecyclerView parent) {
        final int top = parent.getPaddingTop();
        final int bottom = parent.getMeasuredHeight() - parent.getPaddingBottom();
        final int childSize = parent.getChildCount();
        for (int i = 0; i < childSize; i++) {
            final View child = parent.getChildAt(i);
            RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) child.getLayoutParams();
            final int left = child.getRight() + layoutParams.rightMargin;
            final int right = left + mDividerHeight;
            if (mDivider != null) {
                mDivider.setBounds(left, top, right, bottom);
                mDivider.draw(canvas);
            }
            if (mPaint != null) {
                canvas.drawRect(left, top, right, bottom, mPaint);
            }
        }
    }
}

7.添加EmptyView

需要注意的是EmptyView要和recycler view是同一个布局文件的,否则无效。布局文件应该是类似这样的。

<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/activity_main_layout"
    tools:context="recyclerview.demo.lbb.com.recyclerviewdemo.MainActivity">
    <include
        android:id="@+id/recyclerview_emptyview"
        layout="@layout/emptyview_recyclerview"
        />
    <recyclerview.demo.lbb.com.recyclerviewdemo.customview.CustomEmptyRecyclerView
        android:id="@+id/mainactivity_recyclerview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        >
    </recyclerview.demo.lbb.com.recyclerviewdemo.customview.CustomEmptyRecyclerView>
</RelativeLayout>

然后自定义Recyclerview,只需要

 View emptyView = findViewById(R.id.recyclerview_emptyview);
        emptyView.setVisibility(View.GONE);
        recyclerView.setEmptyView(emptyView);

以下是大神代码:

public class CustomEmptyRecyclerView extends RecyclerView {


    public CustomEmptyRecyclerView(Context context) {
        super(context);
    }

    public CustomEmptyRecyclerView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    private View emptyView;//自定义RecyclerView为空的时候的填充内容

    final private AdapterDataObserver observer = new AdapterDataObserver() {
        @Override
        public void onChanged() {
            checkIfEmpty();
        }

        @Override
        public void onItemRangeInserted(int positionStart, int itemCount) {
            checkIfEmpty();
        }

        @Override
        public void onItemRangeRemoved(int positionStart, int itemCount) {
            checkIfEmpty();
        }
    };


    private void checkIfEmpty() {
        if (emptyView != null && getAdapter() != null) {
            final boolean emptyViewVisible = getAdapter().getItemCount() == 0;
            emptyView.setVisibility(emptyViewVisible ? VISIBLE : GONE);
        }
    }

    @Override
    public void setAdapter(Adapter adapter) {
        final Adapter oldAdapter = getAdapter();
        if (oldAdapter != null) {
            oldAdapter.unregisterAdapterDataObserver(observer);
        }
        super.setAdapter(adapter);
        if (adapter != null) {
            adapter.registerAdapterDataObserver(observer);
        }

        checkIfEmpty();
    }

    public void setEmptyView(View emptyView) {
        this.emptyView = emptyView;
        checkIfEmpty();
    }
}

8.踩过的坑

①.垂直列表时,recyclerview的宽度明明是wrap_content,但是实际总是能match_parent,不造原因,在recyclerview-v7:22修复。
②.onCreateViewHolder方法中,viewholder的inflate方式错误导致item根标签即时设置成match_parent,实际表现依然wrap_content。解决办法就是用带有parent的方式填充,原因不详。
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
//使用这种方式填充子item布局的match_parent等属性将无效。
// View view = LayoutInflater.from(context).inflate(R.layout.item_recyclerview,null);
//所以应该用这种方式填充
View view = LayoutInflater.from(context).inflate(R.layout.item_recyclerview, parent, false);
MyViewHolder viewHolder = new MyViewHolder(view);
return viewHolder;
}
③.xml中某些属性无效时,可以用代码的方式尝试。例如在CardView中设置background为selector没有效果,在代码中控制之后居然有效果了。例子代码如下:

<android.support.v7.widget.CardView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:card_view="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    card_view:cardCornerRadius="4dp"
    //以下两句是添加android5.0新增的点击波纹效果
    android:clickable="true"
    android:foreground="?android:attr/selectableItemBackground"
    card_view:cardElevation="6dp"
    android:id="@+id/item_recyclerview_cardview"
    //设置背景这是无效的
android:background="@color/color_item_selector"
    android:layout_height="wrap_content"
>
    </android.support.v7.widget.CardView>

其中res下新建了color文件夹下新建了color_item_selector.xml文件,内容如下:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_pressed="true" android:color="@color/recyclerItemSelect" ></item>
    <item  android:color="@color/recyclerItemNormal"></item>
</selector>

但是在onBindViewHolder()中通过代码设置背景是有效的。

public void onBindViewHolder(final MyViewHolder holder, final int position) {
        holder.text.setText(dataList.get(position).getContent());
//以两句设置cardview背景有效,前提是xml设置为clickable并且设置了foreground水波效果(ps:实际测试是,android5.0必须设置,4.4的不设置也可以。)。        holder.holderView.setBackgroundResource(R.drawable.color_item_selector);
        ColorStateList csl = (ColorStateList) context.getResources().getColorStateList(R.color.color_item_selector);
        holder.cardView.setCardBackgroundColor(csl);
        }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值