ItemTouchHelper是什么?
ItemTouchHelper是v7包中的用于为RecyclerView中Item操作提供帮助的工具类。
- /**
- * This is a utility class to add swipe to dismiss and drag & drop support to RecyclerView.
- * <p>
- * XXXXXXXXXXXXXXXXXXXXXXXXXXXXX
- */
- public class ItemTouchHelper extends RecyclerView.ItemDecoration
- implements RecyclerView.OnChildAttachStateChangeListener {
它内部封装了 Item的左右滑动,上下平移的操作,并向外提供了一个Callback抽象类让我们使用
- /**
- * This class is the contract between ItemTouchHelper and your application. It lets you control
- * which touch behaviors are enabled per each ViewHolder and also receive callbacks when user
- * performs these actions.
- <span style="white-space:pre"> </span>XXXXXXXX
- */
- @SuppressWarnings("UnusedParameters")
- public abstract static class Callback {
我们使用的 时候只需继承该类并实现其中的几个抽象方法就可以轻松实现诸如Item的滑动删除,拖拽交换。不用我们再自己去实现复杂的交互逻辑。默认一共有三个抽象方法需要实现
- /**
- * Should return a composite flag which defines the enabled move directions in each state
- * (idle, swiping, dragging).
- * <p>
- * Instead of composing this flag manually, you can use {@link #makeMovementFlags(int,
- * int)}
- * or {@link #makeFlag(int, int)}.
- * <p>
- * This flag is composed of 3 sets of 8 bits, where first 8 bits are for IDLE state, next
- * 8 bits are for SWIPE state and third 8 bits are for DRAG state.
- * Each 8 bit sections can be constructed by simply OR'ing direction flags defined in
- * {@link ItemTouchHelper}.
- * <p>
- * For example, if you want it to allow swiping LEFT and RIGHT but only allow starting to
- * swipe by swiping RIGHT, you can return:
- * <pre>
- * makeFlag(ACTION_STATE_IDLE, RIGHT) | makeFlag(ACTION_STATE_SWIPE, LEFT | RIGHT);
- * </pre>
- * This means, allow right movement while IDLE and allow right and left movement while
- * swiping.
- *
- * @param recyclerView The RecyclerView to which ItemTouchHelper is attached.
- * @param viewHolder The ViewHolder for which the movement information is necessary.
- * @return flags specifying which movements are allowed on this ViewHolder.
- * @see #makeMovementFlags(int, int)
- * @see #makeFlag(int, int)
- */
- public abstract int getMovementFlags(RecyclerView recyclerView,
- ViewHolder viewHolder);
- /**
- * Called when ItemTouchHelper wants to move the dragged item from its old position to
- * the new position.
- * <p>
- * If this method returns true, ItemTouchHelper assumes {@code viewHolder} has been moved
- * to the adapter position of {@code target} ViewHolder
- * ({@link ViewHolder#getAdapterPosition()
- * ViewHolder#getAdapterPosition()}).
- * <p>
- * If you don't support drag & drop, this method will never be called.
- *
- * @param recyclerView The RecyclerView to which ItemTouchHelper is attached to.
- * @param viewHolder 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, ViewHolder, int, ViewHolder, int, int, int)
- */
- public abstract boolean onMove(RecyclerView recyclerView,
- ViewHolder viewHolder, ViewHolder target);
- /**
- * Called when a ViewHolder is swiped by the user.
- * <p>
- * If you are returning relative directions ({@link #START} , {@link #END}) from the
- * {@link #getMovementFlags(RecyclerView, ViewHolder)} method, this method
- * will also use relative directions. Otherwise, it will use absolute directions.
- * <p>
- * If you don't support swiping, this method will never be called.
- * <p>
- * ItemTouchHelper will keep a reference to the View until it is detached from
- * RecyclerView.
- * As soon as it is detached, ItemTouchHelper will call
- * {@link #clearView(RecyclerView, ViewHolder)}.
- *
- * @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
- * {@link #UP}, {@link #DOWN},
- * {@link #LEFT} or {@link #RIGHT}. If your
- * {@link #getMovementFlags(RecyclerView, ViewHolder)}
- * method
- * returned relative flags instead of {@link #LEFT} / {@link #RIGHT};
- * `direction` will be relative as well. ({@link #START} or {@link
- * #END}).
- */
- public abstract void onSwiped(ViewHolder viewHolder, int direction);
- <strong>getMovementFlags(RecyclerView recyclerView,ViewHolder viewHolder):</strong>该方法的返回值决定的所支持的滑动,拖拽的方向,有ItemTouchHelper.UP, ItemTouchHelper.DOWN ,ItemTouchHelper.LEFT,ItemTouchHelper.RIGHT,ItemTouchHelper.START, ItemTouchHelper.END等几种,比如划动删除的时候,只需要返ItemTouchHelper.LEFT,ItemTouchHelper.RIGHT,即可以支持左右的滑动删除。
- <strong><onMove(RecyclerView recyclerView, ViewHolder viewHolder, ViewHolder target):</strong>在拖拽Item的时候调用该方法,第一个ViewHolder代表正在拖拽的Item,第二个ViewHolder代表目标Item
- </pre><pre name="code" class="java"><strong>onSwiped(ViewHolder viewHolder, int direction):</strong>在滑动Item的时候调用该方法,第二个参数代表拖拽方向的相对位置。
- </pre><pre name="code" class="java">实际运用中还有几个方法需要重写:
- </pre><pre name="code" class="java"><strong>isLongPressDragEnabled():</strong>返回true代表支持当长按的时候开始拖拽操作
- </pre><pre name="code" class="java"><strong>isItemViewSwipeEnabled():</strong>返回true代表支持ItemView的左右滑动
- </pre><pre name="code" class="java"><strong>onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) :</strong>当系统绘制RecyclerView的时候会调用该方法,你可以重写改方法在里面写动画逻辑。其中cationState一共有三种,IDLE, SWIPE, DRAG分别代表了 静止,滑动,拖拽三种状态,
- </pre><pre name="code" class="java"><strong>onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) :</strong>在Item选中状态该表的时候会调用该方法,可以重写改方法,当选中时,写一些动画逻辑。
- </pre><pre name="code" class="java"><strong>clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) : </strong>当用户的操作和动画已经结束的时候调用该方法,可以重写该方法,恢复Item的初始状态
- 说了这么多,不如用代码来解释。
- <span style="font-family: Arial, Helvetica, sans-serif;"></span><pre name="code" class="java"><pre name="code" class="java">
- import android.graphics.Canvas;
- import android.support.v7.widget.RecyclerView;
- import android.support.v7.widget.helper.ItemTouchHelper;
- import android.util.Log;
- /**
- * Created by haggling on 2015/8/6.
- */
- public class MyTouchHelperCallback extends ItemTouchHelper.Callback {
- public static final float ALPHA_FULL = 1.0f;
- private final ItemTouchHelperAdapter adapter;
- public MyTouchHelperCallback(ItemTouchHelperAdapter 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
- return makeMovementFlags(dragFlags, 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 ItemTouchHelperViewHolder) {
- Log.d("instanceof", "instanceof");
- ItemTouchHelperViewHolder itemViewHolder = (ItemTouchHelperViewHolder)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 ItemTouchHelperViewHolder) {
- ItemTouchHelperViewHolder itemViewHolder = (ItemTouchHelperViewHolder)viewHolder;
- itemViewHolder.onItemClear();
- }
- }
- }
- <strong>其中有三个接口</strong>
- <span style="font-family: Arial, Helvetica, sans-serif;"></span><pre name="code" class="java">
- public interface ItemTouchHelperAdapter {
- boolean onItemMove(int fromPosition, int toPosition);
- void onItemDismiss(int position);
- }
- public interface ItemTouchHelperViewHolder {
- void onItemSelected();
- void onItemClear();
- }
- /**
- * Created by haoqinling on 2015/8/6.
- */
- import android.support.v7.widget.RecyclerView;
- public interface OnStartDragListener {
- void onStartDrag(RecyclerView.ViewHolder viewHolder);
- }
- package com.cecgt.haoqinling.myapplication.helper;
- import android.animation.AnimatorSet;
- import android.animation.ObjectAnimator;
- import android.content.Context;
- import android.graphics.Color;
- import android.support.v4.view.MotionEventCompat;
- import android.support.v7.widget.RecyclerView;
- import android.view.LayoutInflater;
- import android.view.MotionEvent;
- import android.view.View;
- import android.view.ViewGroup;
- import android.view.animation.DecelerateInterpolator;
- import android.widget.ImageView;
- import android.widget.TextView;
- import com.cecgt.haoqinling.myapplication.R;
- import java.util.ArrayList;
- import java.util.Arrays;
- import java.util.Collections;
- import java.util.List;
- /**
- * Created by haoqinling on 2015/7/8.
- */
- public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.ItemViewHolder> implements ItemTouchHelperAdapter{
- private final List<String> mItems = new ArrayList<>();
- private final OnStartDragListener mDragStartListener;
- public RecyclerViewAdapter(Context context, OnStartDragListener dragStartListener) {
- mDragStartListener = dragStartListener;
- mItems.addAll(Arrays.asList(context.getResources().getStringArray(R.array.dummy_items)));
- }
- @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));
- //当点击Item上面的图标的时候开始拖拽
- holder.imageView.setOnTouchListener(new View.OnTouchListener() {
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- if (MotionEventCompat.getActionMasked(event) == MotionEvent.ACTION_DOWN) {
- mDragStartListener.onStartDrag(holder);
- }
- return false;
- }
- });
- }
- @Override
- public int getItemCount() {
- return mItems.size();
- }
- @Override
- public boolean onItemMove(int fromPosition, int toPosition) {
- Collections.swap(mItems, fromPosition, toPosition);
- notifyItemMoved(fromPosition, toPosition);
- return true;
- }
- @Override
- public void onItemDismiss(int position) {
- mItems.remove(position);
- notifyItemRemoved(position);
- }
- public static class ItemViewHolder extends RecyclerView.ViewHolder implements ItemTouchHelperViewHolder {
- private final TextView textView;
- private final ImageView imageView;
- AnimatorSet upSet, downSet;
- //private View itemView;
- public ItemViewHolder(View itemView) {
- super(itemView);
- // this.itemView = itemView;
- textView = (TextView) itemView.findViewById(R.id.text);
- imageView = (ImageView) itemView.findViewById(R.id.handle);
- //创建动画
- ObjectAnimator scaleXAnim = ObjectAnimator.ofFloat(itemView, "scaleX", 0.95f);
- ObjectAnimator scaleYAnim = ObjectAnimator.ofFloat(itemView, "scaleY", 0.95f);
- ObjectAnimator upAnim = ObjectAnimator.ofFloat(itemView, "translationZ", 10);
- ObjectAnimator upColor = ObjectAnimator.ofArgb(itemView, "backgroundColor", Color.LTGRAY);
- upSet = new AnimatorSet();
- upSet.playSequentially(scaleXAnim, scaleYAnim, upAnim, upColor);
- upSet.setDuration(100);
- upSet.setInterpolator(new DecelerateInterpolator());
- ObjectAnimator downAnim = ObjectAnimator.ofFloat(itemView, "translationZ", 0);
- ObjectAnimator scaleXDownAnim = ObjectAnimator.ofFloat(itemView, "scaleX", 1.0f);
- ObjectAnimator scaleYDownAnim = ObjectAnimator.ofFloat(itemView, "scaleY", 1.0f);
- ObjectAnimator downColor = ObjectAnimator.ofArgb(itemView, "backgroundColor", 0);
- downSet = new AnimatorSet();
- downSet.playSequentially(scaleXDownAnim, scaleYDownAnim, downAnim, downColor);
- downSet.setDuration(100);
- downSet.setInterpolator(new DecelerateInterpolator());
- }
- @Override
- public void onItemSelected() {
- itemView.clearAnimation();
- upSet.start();
- }
- @Override
- public void onItemClear() {
- itemView.clearAnimation();
- downSet.start();
- }
- }
- }
- recyclerView.setHasFixedSize(true);
- recyclerView.setAdapter(adapter);
- recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
- ItemTouchHelper.Callback callback = new SimpleItemTouchHelperCallback(adapter);
- mItemTouchHelper = new ItemTouchHelper(callback);
- mItemTouchHelper.attachToRecyclerView(recyclerView);
来看下效果:
参考链接:https://medium.com/@ipaulpro/drag-and-swipe-with-recyclerview-b9456d2b1aaf
原作者源码地址:https://github.com/iPaulPro/Android-ItemTouchHelper-Demo