RecyclerView上下拖动条目排序,左右划出屏幕删除条目的最简单的实现

效果图

RecyclerView上下拖动条目排序,左右划出屏幕删除条目的最简单的实现_第1张图片

参考

RecyclerView的拖动和滑动 第一部分 :基本的ItemTouchHelper示例

概述:

RecyclerView上下拖动条目排序,左右划出屏幕删除条目的最简单的实现_第2张图片

ItemTouchHelperAdapter:(★)

RecyclerListAdapter需要实现这个接口,
RecyclerListAdapter通过ItemTouchHelperAdapter的onItemMove和onItemDismiss方法来完成条目移动、条目消除的操作,并且通过notifyItemMoved()和notifyItemRemoved()来通知adapter数据的改变

ItemTouchHelperViewHolder:(★)

RecyclerListAdapter的内部类ItemViewHolder需要实现这个接口ItemViewHolder,通过调用ItemTouchHelperViewHolder的onItemSelected()和onItemClear()来完成条目被选中时(底色变灰色)、条目动画完成时(底色变透明)的操作

SimpleItemTouchHelperCallback:(★★★★★)

该类继承于抽象类ItemTouchHelper.Callback,ItemTouchHelper.Callback已经封装了RecyclerView拖动、侧滑的效果,我们只需要重写其暴露出来的方法来控制拖拽即可,具体涉及到的方法有:(详细介绍见源码)
1. 构造函数
2. isLongPressDragEnabled()
3. isItemViewSwipeEnabled()
4. getMovementFlags()
5. onMove()
6. onSwiped()
7. onSelectedChanged()
8. clearView()

MainActivity/RecyclerListFragment(★)

外界如何调用

RecyclerListAdapter(★★★★★)

  • 该类实现了ItemTouchHelperAdapter接口

    RecyclerListAdapter通过ItemTouchHelperAdapter的onItemMove和onItemDismiss方法来完成条目移动、条目消除的操作,并且通过notifyItemMoved()和notifyItemRemoved()来通知adapter数据的改变

  • 其内部类ItemViewHolder实现这个接口ItemTouchHelperViewHolder

通过调用ItemTouchHelperViewHolder的onItemSelected()和onItemClear()来完成条目被选中时(底色变灰色)、条目动画完成时(底色变透明)的操作

外部使用Activity/Fragment

package co.paulburke.android.itemtouchhelperdemo;

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.helper.ItemTouchHelper;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import co.paulburke.android.itemtouchhelperdemo.helper.SimpleItemTouchHelperCallback;

/**
 * @author Paul Burke (ipaulpro)
 */
public class RecyclerListFragment extends Fragment {

    private ItemTouchHelper mItemTouchHelper;

    public RecyclerListFragment() {
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_main, container, false);
    }

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
//         ★★★关键之处在于RecyclerListAdapter的写法
        RecyclerListAdapter adapter = new RecyclerListAdapter();

        RecyclerView recyclerView = (RecyclerView) view.findViewById(R.id.recycler_view);
//        setHasFixedSize()方法用来使RecyclerView保持固定的大小,该信息被用于自身的优化。
        recyclerView.setHasFixedSize(true);
        recyclerView.setAdapter(adapter);
        recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));

        /**
         * ★★★★★★★★★★★★★★★★★★★★★★★
         * 要使用ItemTouchHelper,你需要创建一个ItemTouchHelper.Callback。
         * 这个接口可以让你监听“move(上下移动)”与 “swipe(左右滑动)”事件。这里还是
         * ★控制view被选中
         * 的状态以及★重写默认动画的地方。
         *
         * 如果你只是想要一个基本的实现,有一个
         * 帮助类可以使用:SimpleCallback,但是为了了解其工作机制,我们还是自己实现
         */
        ItemTouchHelper.Callback callback = new SimpleItemTouchHelperCallback(adapter);
        mItemTouchHelper = new ItemTouchHelper(callback);
//        将定义好的mItemTouchHelper应用于我们的recyclerView,使得recyclerView获得move和swipe的效果
        mItemTouchHelper.attachToRecyclerView(recyclerView);


    }

}

SimpleItemTouchHelperCallback

package co.paulburke.android.itemtouchhelperdemo.helper;

import android.graphics.Canvas;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.helper.ItemTouchHelper;

/**
 * An implementation of {@link ItemTouchHelper.Callback} that enables basic drag & drop and
 * swipe-to-dismiss. Drag events are automatically started by an item long-press.
*
* Expects the RecyclerView.Adapter to react to {@link * ItemTouchHelperAdapter} callbacks and the RecyclerView.ViewHolder to implement * {@link ItemTouchHelperViewHolder}. * * 接下来的两个是onMove()和onSwiped(),用于通知底层数据的更新。 * * @author Paul Burke (ipaulpro) */
public class SimpleItemTouchHelperCallback extends ItemTouchHelper.Callback { private final ItemTouchHelperAdapter mAdapter; public SimpleItemTouchHelperCallback(ItemTouchHelperAdapter adapter) { mAdapter = adapter; } /** * 要支持长按RecyclerView item进入拖动操作, * 你必须在isLongPressDragEnabled()方法中返回true。 * 或者,也可以调用ItemTouchHelper.startDrag(RecyclerView.ViewHolder) * 方法来开始一个拖动。 */ @Override public boolean isLongPressDragEnabled() { return true;//返回true表示支持长按开始拖拽 } /** * 而要在view任意位置触摸事件发生时启用滑动操作, * 则直接在sItemViewSwipeEnabled()中返回true就可以了。 * 或者,你也主动调用ItemTouchHelper.startSwipe(RecyclerView.ViewHolder) * 来开始滑动操作。 */ @Override public boolean isItemViewSwipeEnabled() { return true;//返回true表示支持左右滑动 } /** * ItemTouchHelper可以让你轻易得到一个事件的方向。 * 你需要重写getMovementFlags()方法来指定可以支持 * 的拖放和滑动的方向。 * 使用helperItemTouchHelper.makeMovementFlags(int, int) * 来构造返回的flag。这里我们启用了上下左右两种方向。 * 注:上下为拖动(drag),左右为滑动(swipe)。 */ @Override public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) { final int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN; final int swipeFlags = ItemTouchHelper.START | ItemTouchHelper.END; return makeMovementFlags(dragFlags, swipeFlags);//表示支持上下拖拽和左右滑动 // return makeMovementFlags(dragFlags, 0);//第二项为0,表示你支持拖拽,不支持左右滑动 } /** * Called when ItemTouchHelper wants to move the dragged item from its old position to * the new position. * 当移动一个条目时被调用 *

* If this method returns true, ItemTouchHelper assumes {@code viewHolder} has been moved * to the adapter position of {@code target} ViewHolder * ({@link RecyclerView.ViewHolder#getAdapterPosition() * ViewHolder#getAdapterPosition()}). * 如果该方法返回true,认定条目已经移动到了新的位置 *

* If you don't support drag & drop, this method will never be called. * 如果你不支持拖拽,此方法不会被调用 * * @param recyclerView The RecyclerView to which ItemTouchHelper is attached to. * ItemTouchHelper所附着的RecyclerView * @param source The ViewHolder which is being dragged by the user. * 正在被用户拖拽的位置 * @param target The ViewHolder over which the currently active item is being * dragged. * 当前正在被拖拽的条目所经过的位置 * @return True if the {@code viewHolder} has been moved to the adapter position of * {@code target}. * @see #onMoved(RecyclerView, RecyclerView.ViewHolder, int, RecyclerView.ViewHolder, int, int, int) */ @Override public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder source, RecyclerView.ViewHolder target) { mAdapter.onItemMove(source.getAdapterPosition(), target.getAdapterPosition()); return true; } /** * Called when a ViewHolder is swiped by the user. * 当被左右滑动时,调用该方法 * 注意:这里的ViewHolder统一翻译成条目,通过ViewHolder可以得到关于条目的所有属性(如:位置等等) *

* If you are returning relative directions ( {START} , {END}) from the * {@link #getMovementFlags(RecyclerView, RecyclerView.ViewHolder)} method, this method * will also use relative directions. Otherwise, it will use absolute directions. * 当你返回一个相对方向时(由getMovementFlags()所返回的方向),该方法使用的是相对方向,否则使用绝对方向 *

* If you don't support swiping, this method will never be called. * 如果你不支持左右滑动,该方法不会被调用 *

* ItemTouchHelper will keep a reference to the View until it is detached from * RecyclerView. * ItemTouchHelper将会持有View的引用,直到ItemTouchHelper不再附着在RecyclerView上时 * As soon as it is detached, ItemTouchHelper will call * {@link #clearView(RecyclerView, RecyclerView.ViewHolder)}. * 当不再附着时,ItemTouchHelper会调用clearView方法 * * @param viewHolder The ViewHolder which has been swiped by the user. * 用户拖拽的条目 * @param direction The direction to which the ViewHolder is swiped. It is one of * {UP}, {DOWN}, * {LEFT} or {RIGHT}. If your * {@link #getMovementFlags(RecyclerView, RecyclerView.ViewHolder)} * method * returned relative flags instead of {LEFT} / {RIGHT}; * `direction` will be relative as well. ({START} or { * END}). * 条目被左右滑动的方向,他是上、下、左、右中的一个,如果getMovementFlags返回了 * 相对标志flags代替了左、右方向,将使用这个返回的方向 * */ @Override public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) { mAdapter.onItemDismiss(viewHolder.getAdapterPosition()); } /** * Called when the ViewHolder swiped or dragged by the ItemTouchHelper is changed. * 当条目被改变(拖拽、侧滑)时,该方法被调用 *

* If you override this method, you should call super. * 如果你重写该方法,你应该调用super * * @param viewHolder The new ViewHolder that is being swiped or dragged. Might be null if * it is cleared. * 正在被拖拽/侧滑的条目,可能为null,如果他被清除掉 * @param actionState One of {@link ItemTouchHelper#ACTION_STATE_IDLE}, * {@link ItemTouchHelper#ACTION_STATE_SWIPE} or * {@link ItemTouchHelper#ACTION_STATE_DRAG}. * 活动状态:空闲、侧滑、拖拽,根据这个判断不同状态下的操作 * * @see #clearView(RecyclerView, RecyclerView.ViewHolder) */ @Override public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) { if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) {//只要这个条目不是空闲状态(即:拖拽或者侧滑) ItemTouchHelperViewHolder itemViewHolder = (ItemTouchHelperViewHolder) viewHolder; itemViewHolder.onItemSelected();//这里改变选中时的条目的状态 } super.onSelectedChanged(viewHolder, actionState); } /** * Called by the ItemTouchHelper when the user interaction with an element is over and it * also completed its animation. * 当ItemTouchHelper已经完成他的动画时(即用户与条目交互完成后),该方法被调用 *

* This is a good place to clear all changes on the View that was done in * {@link #onSelectedChanged(RecyclerView.ViewHolder, int)}, * {@link #onChildDraw(Canvas, RecyclerView, RecyclerView.ViewHolder, float, float, int, * boolean)} or * {@link #onChildDrawOver(Canvas, RecyclerView, RecyclerView.ViewHolder, float, float, int, boolean)}. * 这是一个很好的地方,用来清除所有条目动画完成后的效果(可能你在onSelectedChanged()、onChildDraw() * onChildDrawOver()时赋予了条目很多效果,在这个方法里,统统把这些效果清除掉) * * @param recyclerView The RecyclerView which is controlled by the ItemTouchHelper. * @param viewHolder The View that was interacted by the user. */ @Override public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) { super.clearView(recyclerView, viewHolder); ItemTouchHelperViewHolder itemViewHolder = (ItemTouchHelperViewHolder) viewHolder; itemViewHolder.onItemClear();//在这里处理清除掉条目效果 } }

ItemTouchHelperAdapter

package co.paulburke.android.itemtouchhelperdemo.helper;

import android.support.v7.widget.RecyclerView;

/**
 * Interface to notify a {@link RecyclerView.Adapter} of moving and dismissal event from a {@link
 * android.support.v7.widget.helper.ItemTouchHelper.Callback}.
 *
 * @author Paul Burke (ipaulpro)
 */
public interface ItemTouchHelperAdapter {

    /**
     * Called when an item has been dragged far enough to trigger a move. This is called every time
     * an item is shifted, and not at the end of a "drop" event.
     *
     *当一个条目被拖拽移动时,该方法将被调用,当条目被移动时,该方法都会被调用,而不是在拖拽结尾才被调用
     *
     * @param fromPosition The start position of the moved item.
     * @param toPosition   Then end position of the moved item.
     * @see RecyclerView#getAdapterPositionFor(RecyclerView.ViewHolder)
     * @see RecyclerView.ViewHolder#getAdapterPosition()
     */

    void onItemMove(int fromPosition, int toPosition);


    /**
     * Called when an item has been dismissed by a swipe.
     *
     * 当左右滑动条目时,调用该方法
     *
     * @param position The position of the item dismissed.
     * @see RecyclerView#getAdapterPositionFor(RecyclerView.ViewHolder)
     * @see RecyclerView.ViewHolder#getAdapterPosition()
     */
    void onItemDismiss(int position);
}

ItemTouchHelperViewHolder

package co.paulburke.android.itemtouchhelperdemo.helper;

import android.support.v7.widget.helper.ItemTouchHelper;

/**
 * Interface to notify an item ViewHolder of relevant callbacks from {@link
 * android.support.v7.widget.helper.ItemTouchHelper.Callback}.
 *
 * @author Paul Burke (ipaulpro)
 */
public interface ItemTouchHelperViewHolder {

    /**
     * Called when the {@link ItemTouchHelper} first registers an item as being moved or swiped.
     * Implementations should update the item view to indicate it's active state.
     * 当ItemTouchHelper将拖拽/侧滑注册(作用)到条目上时,实现该方法去更新这个条目的视图效果,
     * 以暗示该条目正在被操作
     *
     */
    void onItemSelected();


    /**
     * Called when the {@link ItemTouchHelper} has completed the move or swipe, and the active item
     * state should be cleared.
     * 当ItemTouchHelper完成了拖拽/侧滑时,激活的条目效果应该被消除
     */
    void onItemClear();
}

主要的代码:RecyclerListAdapter

package co.paulburke.android.itemtouchhelperdemo;

import android.graphics.Color;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import co.paulburke.android.itemtouchhelperdemo.helper.ItemTouchHelperAdapter;
import co.paulburke.android.itemtouchhelperdemo.helper.ItemTouchHelperViewHolder;

/**
 * @author Paul Burke (ipaulpro)
 * ★ RecyclerListAdapter要实现ItemTouchHelperAdapter
 *    重写onItemDismiss(删除条目)和onItemMove(移动条目)
 *
 * ★ RecyclerView.ViewHolder实现ItemTouchHelperViewHolder
 *    重写onItemSelected(条目被选中时)和onItemClear(条目被拖拽之后)
 */
public class RecyclerListAdapter extends RecyclerView.Adapter<RecyclerListAdapter.ItemViewHolder>
        implements ItemTouchHelperAdapter {

    private static final String[] STRINGS = new String[]{
            "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten"
    };

    private final List mItems = new ArrayList<>();

    public RecyclerListAdapter() {
        mItems.addAll(Arrays.asList(STRINGS));
    }

    @Override
    public ItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_main, parent, false);
        ItemViewHolder itemViewHolder = new ItemViewHolder(view);
        return itemViewHolder;
    }

    @Override
    public void onBindViewHolder(final ItemViewHolder holder, int position) {
        holder.textView.setText(mItems.get(position));
    }

//   当条目被删除时的操作(实现ItemTouchHelperAdapter重写onItemDismiss方法)
    @Override
    public void onItemDismiss(int position) {
        mItems.remove(position);
//        通知条目已经被删除,与adapter互动
        notifyItemRemoved(position);
    }

    //   当条目被移动时的操作(实现ItemTouchHelperAdapter重写onItemMove方法)
    @Override
    public void onItemMove(int fromPosition, int toPosition) {
//        ★移动一个条目分两步:首先把该条目先移除,再考虑在对应要移动到的地方增加这个条目
        String prev = mItems.remove(fromPosition);
//        toPosition > fromPosition ? toPosition - 1 : toPosition,
//        即将拖动到的位置是否比开始拖动的位置大,
//        如果大,则在集合里在toPosition - 1处填写该条目,否则在toPosition处添加该条目
        mItems.add(toPosition > fromPosition ? toPosition - 1 : toPosition, prev);
//        通知条目被移动,与adapter互动
        notifyItemMoved(fromPosition, toPosition);
    }

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

    public static class ItemViewHolder extends RecyclerView.ViewHolder implements
            ItemTouchHelperViewHolder {

        public final TextView textView;

        public ItemViewHolder(View itemView) {
            super(itemView);
            textView = (TextView) itemView;
        }

        @Override
        public void onItemSelected() {
//            当选中时,背景色为灰色
            itemView.setBackgroundColor(Color.LTGRAY);
        }

        @Override
        public void onItemClear() {
//            当拖拽完毕后,背景色为透明
            itemView.setBackgroundColor(0);
        }
    }
}

完整源码:

github

你可能感兴趣的:(动画相关)