本文主要记录模仿B612咔叽 6.2.0 版本里的滤镜操作

B612咔叽 6.2.0 版本里的滤镜的操作特点- 长按某一个滤镜收藏(点击应用这个是肯定的)
 - 收藏后列表最左边出现红色的竖条块,点击可以定位到收藏第一个
 - 收藏里的Item 可以随意拖动更换位置
 - 普通的Item 可以向上拖动,取消收藏,上下移动没有限制,左右移动是有限制的
 - 收藏里的Item 可以向上拖动,取消收藏,此处需要移除item
 - 取消收藏的按钮,有一个变色,向上移动超过某个距离后松手,就表示需要删除收藏

上面的需求都是本次模仿需要实现的,考虑到ItemTouchHelper 是官方给提供一个操作RecyclerView Item的类,大概找了一下,没有需要的全部功能,只能在这个类的基础上进行改造,先看看效果(最后的移除动画处理的不是很好)

RecycleView TouchHelper 阅读理解

  • setup touch callback
mRecyclerView.addOnItemTouchListener(mOnItemTouchListener);
mRecyclerView.addOnChildAttachStateChangeListener(this);
new GestureDetectorCompat(Context context, OnGestureListener listener);
  • mOnItemTouchListener 回调里面
    onInterceptTouchEvent里面 记录 用户手指按下的点位置信息
    如果当前有选中的View,就拦截掉这个事件. ViewGroup里的onInterceptTouchEvent默认值是false这样才能把事件传给View里的onTouchEvent.
  • [*] onTouchEvent里 MotionEvent.ACTION_MOVE 事件
    • updateDxDy(),计算出当前手指的位置相对按下时偏移的距离,想要限制移动的范围,在这里对偏移距离做控制,
    • moveIfNecessary(), 移动选中Item,如果移动的位置距离过长需要移动Item 的位置,会调用 Callback.onMove ,在里面更换Item的次序
    • scrollIfNecessary(), scroll recycleView
  • MotionEvent.ACTION_CANCEL | MotionEvent.ACTION_UP
    • select(); select a item 负责位置移动的动画
  • onLongPress() 如果支持长按,也是调用select();进行移动
  • clearView(RecyclerView recyclerView, ViewHolder viewHolder) 只会在 RecoverAnimation 的 onAnimationEnd时调用,很多动画运行一半都会被下一个动画顶掉而取消,只有松开手的最后一个动画才会跑完整个生命周期
  • RecyclerView.OnChildAttachStateChangeListener 这里用于绘制 Item 移动变化时的 item 位置大小

重写了TouchHelper 类,看看测试代码
- RecycleViewActivity.class

import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.TextView;import com.benqu.wuta.R;
import com.benqu.wuta.activities.base.BaseActivity;import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;/*** Created by slack* on 17/7/15 上午10:53** 模仿 B612咔叽 滤镜 里 Item 的操作* 1.收藏里可以随意调换位置* 2.正常的Item 移动位置的范围是有限制的[上下左右]*/public class RecycleViewActivity extends BaseActivity {@BindView(R.id.test_recyclerView_horizontal)RecyclerView mRecyclerViewHorizontal;private RecyclerViewAdapter mAdapterHorizontal;//创建适配器对象private ItemData mItemData;@BindView(R.id.test_touch_del_view)TextView mTouchDelView;@BindView(R.id.test_add_like_view)TextView mAddLikeView;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_test_recyclerview_layout);ButterKnife.bind(this);initDatas();//初始化数据initHorizontal();}@OnClick(R.id.test_add_like_view)void onLikeFirstViewClick(View view) {mRecyclerViewHorizontal.smoothScrollToPosition(0);}private void initHorizontal() {mRecyclerViewHorizontal.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false));mAdapterHorizontal = new RecyclerViewAdapter(mRecyclerViewHorizontal, mItemData);mAdapterHorizontal.setTouchCallback(mCallback);mRecyclerViewHorizontal.setAdapter(mAdapterHorizontal);}private RecyclerViewAdapter.TouchCallback mCallback = new RecyclerViewAdapter.TouchCallback() {@Overridepublic void onTouchSelected(boolean show) {mTouchDelView.setVisibility(show ? View.VISIBLE : View.INVISIBLE);}@Overridepublic void onTouchMoveUpToLine(boolean inside) {showDelView(inside);}@Overridepublic void onAddToLike() {mAddLikeView.setVisibility(View.VISIBLE);mAddLikeView.animate().alpha(0.5f).setDuration(1500).withEndAction(new Runnable() {@Overridepublic void run() {mAddLikeView.animate().alpha(1.0f).setDuration(0).start();mAddLikeView.setVisibility(View.GONE);}}).start();}@Overridepublic void onLongClicked(boolean like) {if(like) {showDelView(true);} else {//}}};private void showDelView(boolean like) {int color;if(like) {color = getResources().getColor(R.color.red_80);} else {color = getResources().getColor(R.color.red_50);}mTouchDelView.setBackgroundColor(color);}private void initDatas() {mItemData = new ItemData();for (int i = 0; i < 50; i++) {mItemData.add("Number:" + i);}}
  • R.layout.activity_test_recyclerview_layout
<FrameLayout 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:layout_gravity="bottom"><FrameLayout
        android:layout_width="match_parent"android:layout_height="200dp"android:layout_gravity="bottom"android:background="@color/black_50"android:gravity="center"><TextView
            android:visibility="gone"android:id="@+id/test_touch_del_view"android:layout_width="match_parent"android:layout_height="40dp"android:background="@color/red_50"android:text="删除"android:textSize="20sp"android:gravity="center"/><android.support.v7.widget.RecyclerView
            android:id="@+id/test_recyclerView_horizontal"android:layout_width="match_parent"android:layout_height="match_parent"/><TextView
            android:id="@+id/test_add_like_view"android:visibility="gone"android:layout_width="30dp"android:layout_height="100dp"android:layout_marginTop="60dp"android:text="|||"android:gravity="center"android:textColor="@color/white"android:background="@color/red_50"/></FrameLayout></FrameLayout>

- ItemData.class

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;/*** Created by slack* on 17/7/15 下午2:44** like,like,like,like...' normal,normal,normal...,,*/public class ItemData {private final List<Item> itemList = new ArrayList<>();private final List<Item> itemNormalList = new ArrayList<>();private final List<Item> itemLikeList = new ArrayList<>();private void formItemList(boolean force) {if(force || itemList.isEmpty()) {itemList.clear();itemList.addAll(itemLikeList);itemList.addAll(itemNormalList);}}public int size() {formItemList(false);return itemList.size();}public boolean empty() {formItemList(false);return itemList.isEmpty();}public void add(String name) {itemNormalList.add(new Item(name));}public String get(int pos) {formItemList(false);if(isLegal(pos)) {return itemList.get(pos).name;}return "";}/*** @param pos  pos in the itemList*/public void addLike(int pos) {if(isLegal(pos)) {Item item = itemList.get(pos);if(!itemLikeList.contains(item)) {itemLikeList.add(item);formItemList(true);}}}public void insertLike(int pos) {if(isLegal(pos)) {Item item = itemList.get(pos);if(!itemLikeList.contains(item)) {itemLikeList.add(0, item.copy());formItemList(true);}}}public int likeSize() {return itemLikeList.size();}/*** @param pos pos* @return return true : remove from likeList need notify remove, return false ,just not in like list or remove failed*/public boolean inLikeList(int pos) {return pos < itemLikeList.size();}public void removeLike(int pos) {if(isLike(pos)) {Item item = itemList.get(pos);itemLikeList.remove(item);formItemList(true);}}public int findLikePositionInLikeList(int pos) {if(isLike(pos)) {Item item = itemList.get(pos);int index = itemLikeList.indexOf(item);return index;}return likeSize();}public int findLikePositionInList(int pos) {if(isLike(pos)) {Item item = itemList.get(pos);int index = itemNormalList.indexOf(item);if(index > -1) {return index + likeSize();}}return likeSize();}public boolean isLike(int pos) {if (isLegal(pos)) {Item item = itemList.get(pos);return itemLikeList.contains(item);}return false;}public void swap(int fromPosition, int toPosition) {if (fromPosition < toPosition) {//分别把中间所有的item的位置重新交换for (int i = fromPosition; i < toPosition; i++) {Collections.swap(itemLikeList, i, i + 1);}} else {for (int i = fromPosition; i > toPosition; i--) {Collections.swap(itemLikeList, i, i - 1);}}formItemList(true);}public boolean isLegal(int pos) {return pos >= 0 && pos < size();}static class Item {Item(String name) {this.name = name;}String name;Item copy() {return new Item(this.name);}@Overridepublic boolean equals(Object obj) {if(obj == null) {return false;}if(obj instanceof  Item) {return Objects.equals(((Item) obj).name, this.name);}return false;}}}
  • RecyclerViewAdapter.class
import android.graphics.Canvas;
import android.os.Handler;
import android.support.annotation.NonNull;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;import com.benqu.wuta.R;
import com.benqu.wuta.helper.ScreenHelper;
import com.benqu.wuta.test.recycleView.helper.ItemTouchHelper;/*** Created by slack* on 17/7/15 下午6:37*/public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.VH> {private ItemData mItemData;private ItemTouchHelper mItemTouchHelper;private TouchCallback mTouchCallback;private Handler mHandler = new Handler();private int mCurrentSelect = -1;RecyclerViewAdapter(RecyclerView recyclerView, ItemData data) {this.mItemData = data;initTouchListener(recyclerView);}public void setTouchCallback(TouchCallback callback) {mTouchCallback = callback;}private void initTouchListener(RecyclerView recyclerView){recyclerView.setItemAnimator(new DefaultItemAnimator());recyclerView.setHasFixedSize(true);//0则不执行拖动或者滑动/*** ItemTouchHelper为我们提供了一个SimpleCallback继承自Callback的抽象类,简化了好多操作,* 我们只需实现SimpleCallback对应的方法即可,创建SimpleCallback对象会默认实现两个方法onMove和onSwiped,* 分别表示滑动和拖拽对应的实现*/ItemTouchHelper.Callback mCallback = new ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT,0) {/*** 是否需要移动 return true need move, return false not move*/@Overridepublic boolean needMove(@NonNull RecyclerView.ViewHolder viewHolder) {int toPosition = viewHolder.getAdapterPosition();return toPosition < mItemData.likeSize();}/*** @param recyclerView* @param viewHolder 拖动的ViewHolder* @param target 目标位置的ViewHolder* @return*///拖动模块@Overridepublic boolean onMove(RecyclerView recyclerView,@NonNull RecyclerView.ViewHolder viewHolder,@NonNull  RecyclerView.ViewHolder target) {int fromPosition = viewHolder.getAdapterPosition();//得到拖动ViewHolder的positionint toPosition = target.getAdapterPosition();//得到目标ViewHolder的positionmItemData.swap(fromPosition, toPosition);notifyItemMoved(fromPosition, toPosition);Log.i("slack", "onMove...");//返回true表示执行拖动return true;}@Overridepublic boolean isLongPressDragEnabled() {return super.isLongPressDragEnabled();}@Overridepublic void onLongPressed(@NonNull RecyclerView.ViewHolder viewHolder) {super.onLongPressed(viewHolder);onLongClicked(viewHolder);}//删除模块@Overridepublic void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {}@Overridepublic void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) {//左右滑动时改变Item的透明度final float alpha = 1 - Math.abs(dX) / (float) viewHolder.itemView.getWidth();viewHolder.itemView.setAlpha(alpha);viewHolder.itemView.setTranslationX(dX);}
//                Log.i("slack", "onChildDraw...");}@Overridepublic void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {super.onSelectedChanged(viewHolder, actionState);//当选中Item时候会调用该方法,重写此方法可以实现选中时候的一些动画逻辑Log.i("slack", "onSelectedChanged..." + actionState);if(actionState == ItemTouchHelper.ACTION_STATE_DRAG) {onTouchSelected(viewHolder);}if(viewHolder != null) {final int w = ScreenHelper.helper.dpToPx(50);final int bottom = ScreenHelper.helper.dpToPx(60);final int top = ScreenHelper.helper.dpToPx(80);if(needMove(viewHolder)) {mItemTouchHelper.updateMoveLimit(false, w, w, top, bottom);} else if(isNormalItem(viewHolder)){mItemTouchHelper.updateMoveLimit(true, 0, 0, 0, 0);} else {mItemTouchHelper.updateMoveLimit(true, w, w, top, bottom);}}}@Overridepublic void onMoveUpToLine(RecyclerView.ViewHolder viewHolder, boolean inside) {Log.i("slack", "onMoveUpToLine..." + inside);onTouchMoveUpToLine(viewHolder, inside);}@Overridepublic boolean onNeedDelete(RecyclerView.ViewHolder viewHolder) {return needDelete() && needMove(viewHolder);}@Overridepublic void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {super.clearView(recyclerView, viewHolder);//当动画已经结束的时候调用该方法,重写此方法可以实现恢复Item的初始状态Log.i("slack", "clearView...");onTouchFinish(viewHolder);}@Overridepublic void onTouchUp(RecyclerView.ViewHolder  viewHolder) {Log.i("slack", "onTouchFinish...");}};mItemTouchHelper = new ItemTouchHelper(mCallback);//通过调用mCallback将itemTouchHelper和recyclerview进行绑定mItemTouchHelper.attachToRecyclerView(recyclerView);}private void onTouchSelected(RecyclerView.ViewHolder viewHolder){if(!isNormalItem(viewHolder)) {removeTimeCount();if (mTouchCallback != null) {mTouchCallback.onTouchSelected(true);}viewHolder.itemView.animate().scaleX(1.1f).scaleY(1.1f).start();}}/***   itemLike1,itemLike2,itemLike3,... item1,   item2,   item2,...* 1:              pos*               posInLike                   posInNormal** 2:                                            pos*               posInLike                   posInNormal*/private void removeItemIfNecessary() {if(needDelete()) {int pos = mLastViewHolder.getAdapterPosition();int posInLike = mItemData.findLikePositionInLikeList(pos);int posInNormal = mItemData.findLikePositionInList(pos) + 1;mItemData.removeLike(pos);mLastViewHolder.itemView.setVisibility(View.GONE);notifyItemRemoved(posInLike);notifyItemRangeChanged(posInLike, posInNormal);if(mTimeCount != null) {mTimeCount.run();}}}private boolean needDelete() {return mLastInside && mLastViewHolder != null;}private void onLongClicked(RecyclerView.ViewHolder viewHolder) {int pos = viewHolder.getAdapterPosition();boolean like = mItemData.isLike(pos);if(mTouchCallback != null) {mTouchCallback.onLongClicked(like);}if(!like) {// insert one item ,however current item in the window will not movemItemData.insertLike(pos);notifyItemInserted(0);notifyItemRangeChanged(0, pos + 1);if(mTouchCallback != null) {mTouchCallback.onAddToLike();}} else {((VH) viewHolder).updateSelectView(true);}((VH) viewHolder).updateLikeView(true);}private TimeCount mTimeCount;private boolean onTouchFinish(RecyclerView.ViewHolder viewHolder){removeTimeCount();removeItemIfNecessary();mTimeCount = new TimeCount(viewHolder);mHandler.postDelayed(mTimeCount, 2000);viewHolder.itemView.animate().scaleX(1.0f).scaleY(1.0f).start();((VH) viewHolder).updateSelectView(true);mLastViewHolder = null;mLastInside = false;return false;}/*** 三种Item : 都有点击事件* 1. 正常的 item, 不可移动,未收藏* 2. 收藏的 item, 在最前面的收藏列表里 在收藏列表里可以随意更换位置,向上滑动移除收藏,移除该item* 3. 收藏的 item, 在普通item的列表里, 移动的位置有限制,向上滑动移除收藏,但是不移除该item*/private boolean isNormalItem(RecyclerView.ViewHolder viewHolder) {int pos = viewHolder.getAdapterPosition();return !mItemData.isLike(pos);}private void removeTimeCount() {if(mTimeCount != null) {mHandler.removeCallbacks(mTimeCount);mTimeCount.run();}}private class TimeCount implements Runnable{private RecyclerView.ViewHolder viewHolder;TimeCount(RecyclerView.ViewHolder v) {viewHolder = v;}@Overridepublic void run() {if(mTouchCallback != null) {mTouchCallback.onTouchSelected(false);}((VH) viewHolder).updateSelectView(false);}};private boolean mLastInside;private RecyclerView.ViewHolder mLastViewHolder;private void onTouchMoveUpToLine(RecyclerView.ViewHolder viewHolder, boolean inside){mLastViewHolder = viewHolder;mLastInside = inside;if(mTouchCallback != null) {mTouchCallback.onTouchMoveUpToLine(inside);}}@Overridepublic VH onCreateViewHolder(ViewGroup parent, int viewType) {View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_test_recycler, parent, false);if(view == null) {return null;}return new VH(view);}@Overridepublic void onBindViewHolder(VH holder, int position) {holder.itemView.setTranslationX(0);holder.itemView.setTranslationY(0);holder.itemView.animate().scaleX(1.0f).scaleY(1.0f).start();holder.itemView.setVisibility(View.VISIBLE);holder.mTextView.setText(mItemData.get(position));holder.updateSelectView(position == mCurrentSelect);holder.updateLikeView(mItemData.isLike(position));int likes = mItemData.likeSize();if(likes > 0 && position == likes) {holder.mSplitView.setVisibility(View.VISIBLE);} else {holder.mSplitView.setVisibility(View.GONE);}holder.itemView.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {Log.i("slack", "onClick...");}});}@Overridepublic int getItemCount() {return mItemData.size();}static class VH extends RecyclerView.ViewHolder {TextView mTextView;ImageView mSelectView;View mLikeView, mSplitView;VH(View itemView) {super(itemView);mTextView = (TextView) itemView.findViewById(R.id.tv_content);mSelectView = (ImageView) itemView.findViewById(R.id.select_view);mLikeView = itemView.findViewById(R.id.like_view);mSplitView = itemView.findViewById(R.id.split_view);}void updateSelectView(boolean show) {mSelectView.setVisibility(show ? View.VISIBLE : View.INVISIBLE);}void updateLikeView(boolean like) {if(like) {mLikeView.setVisibility(View.VISIBLE);} else {mLikeView.setVisibility(View.GONE);}}}interface TouchCallback{void onTouchSelected(boolean show);void onTouchMoveUpToLine(boolean inside);void onAddToLike();void onLongClicked(boolean like);}
}
  • helper.ItemTouchHelper.class 主要内容都是复制 RecyclerView.ItemTouchHelper.class
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.animation.AnimatorCompatHelper;
import android.support.v4.animation.AnimatorListenerCompat;
import android.support.v4.animation.AnimatorUpdateListenerCompat;
import android.support.v4.animation.ValueAnimatorCompat;
import android.support.v4.view.GestureDetectorCompat;
import android.support.v4.view.MotionEventCompat;
import android.support.v4.view.VelocityTrackerCompat;
import android.support.v4.view.ViewCompat;
import android.support.v7.recyclerview.R;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.RecyclerView.OnItemTouchListener;
import android.support.v7.widget.RecyclerView.ViewHolder;
import android.util.Log;
import android.view.GestureDetector;
import android.view.HapticFeedbackConstants;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.view.animation.Interpolator;import java.util.ArrayList;
import java.util.List;/*** This is a utility class to add swipe to dismiss and drag & drop support to RecyclerView.* <p>* It works with a RecyclerView and a Callback class, which configures what type of interactions* are enabled and also receives events when user performs these actions.* <p>* Depending on which functionality you support, you should override* {@link Callback#onMove(RecyclerView, ViewHolder, ViewHolder)} and / or* {@link Callback#onSwiped(ViewHolder, int)}.* <p>* This class is designed to work with any LayoutManager but for certain situations, it can be* optimized for your custom LayoutManager by extending methods in the* {@link ItemTouchHelper.Callback} class or implementing {@link ItemTouchHelper.ViewDropHandler}* interface in your LayoutManager.* <p>* By default, ItemTouchHelper moves the items' translateX/Y properties to reposition them. On* platforms older than Honeycomb, ItemTouchHelper uses canvas translations and View's visibility* property to move items in response to touch events. You can customize these behaviors by* overriding {@link Callback#onChildDraw(Canvas, RecyclerView, ViewHolder, float, float, int,* boolean)}* or {@link Callback#onChildDrawOver(Canvas, RecyclerView, ViewHolder, float, float, int,* boolean)}.* <p/>* Most of the time, you only need to override <code>onChildDraw</code> but due to limitations of* platform prior to Honeycomb, you may need to implement <code>onChildDrawOver</code> as well.*/
public class ItemTouchHelper extends RecyclerView.ItemDecorationimplements RecyclerView.OnChildAttachStateChangeListener {/*** Up direction, used for swipe & drag control.*/public static final int UP = 1;/*** Down direction, used for swipe & drag control.*/public static final int DOWN = 1 << 1;/*** Left direction, used for swipe & drag control.*/public static final int LEFT = 1 << 2;/*** Right direction, used for swipe & drag control.*/public static final int RIGHT = 1 << 3;// If you change these relative direction values, update Callback#convertToAbsoluteDirection,// Callback#convertToRelativeDirection./*** Horizontal start direction. Resolved to LEFT or RIGHT depending on RecyclerView's layout* direction. Used for swipe & drag control.*/public static final int START = LEFT << 2;/*** Horizontal end direction. Resolved to LEFT or RIGHT depending on RecyclerView's layout* direction. Used for swipe & drag control.*/public static final int END = RIGHT << 2;/*** ItemTouchHelper is in idle state. At this state, either there is no related motion event by* the user or latest motion events have not yet triggered a swipe or drag.*/public static final int ACTION_STATE_IDLE = 0;/*** A View is currently being swiped.*/public static final int ACTION_STATE_SWIPE = 1;/*** A View is currently being dragged.*/public static final int ACTION_STATE_DRAG = 2;/*** Animation type for views which are swiped successfully.*/public static final int ANIMATION_TYPE_SWIPE_SUCCESS = 1 << 1;/*** Animation type for views which are not completely swiped thus will animate back to their* original position.*/public static final int ANIMATION_TYPE_SWIPE_CANCEL = 1 << 2;/*** Animation type for views that were dragged and now will animate to their final position.*/public static final int ANIMATION_TYPE_DRAG = 1 << 3;static final String TAG = "ItemTouchHelper";static final boolean DEBUG = true;static final int ACTIVE_POINTER_ID_NONE = -1;static final int DIRECTION_FLAG_COUNT = 8;private static final int ACTION_MODE_IDLE_MASK = (1 << DIRECTION_FLAG_COUNT) - 1;static final int ACTION_MODE_SWIPE_MASK = ACTION_MODE_IDLE_MASK << DIRECTION_FLAG_COUNT;static final int ACTION_MODE_DRAG_MASK = ACTION_MODE_SWIPE_MASK << DIRECTION_FLAG_COUNT;/*** The unit we are using to track velocity*/private static final int PIXELS_PER_SECOND = 1000;/*** Views, whose state should be cleared after they are detached from RecyclerView.* This is necessary after swipe dismissing an item. We wait until animator finishes its job* to clean these views.*/final List<View> mPendingCleanup = new ArrayList<View>();/*** Re-use array to calculate dx dy for a ViewHolder*/private final float[] mTmpPosition = new float[2];/*** Currently selected view holder*/ViewHolder mSelected = null;/*** The reference coordinates for the action start. For drag & drop, this is the time long* press is completed vs for swipe, this is the initial touch point.*/float mInitialTouchX;float mInitialTouchY;/*** Set when ItemTouchHelper is assigned to a RecyclerView.*/float mSwipeEscapeVelocity;/*** Set when ItemTouchHelper is assigned to a RecyclerView.*/float mMaxSwipeVelocity;/*** The diff between the last event and initial touch.*/float mDx;float mDy;/*** The coordinates of the selected view at the time it is selected. We record these values* when action starts so that we can consistently position it even if LayoutManager moves the* View.*/float mSelectedStartX;float mSelectedStartY;/*** The pointer we are tracking.*/int mActivePointerId = ACTIVE_POINTER_ID_NONE;/*** Developer callback which controls the behavior of ItemTouchHelper.*/Callback mCallback;/*** Current mode.*/int mActionState = ACTION_STATE_IDLE;/*** The direction flags obtained from unmasking* {@link Callback#getAbsoluteMovementFlags(RecyclerView, ViewHolder)} for the current* action state.*/int mSelectedFlags;/*** When a View is dragged or swiped and needs to go back to where it was, we create a Recover* Animation and animate it to its location using this custom Animator, instead of using* framework Animators.* Using framework animators has the side effect of clashing with ItemAnimator, creating* jumpy UIs.*/List<RecoverAnimation> mRecoverAnimations = new ArrayList<RecoverAnimation>();private int mSlop;RecyclerView mRecyclerView;/*** When user drags a view to the edge, we start scrolling the LayoutManager as long as View* is partially out of bounds.*/final Runnable mScrollRunnable = new Runnable() {@Overridepublic void run() {if (mSelected != null && scrollIfNecessary()) {if (mSelected != null) { //it might be lost during scrollingmoveIfNecessary(mSelected);}mRecyclerView.removeCallbacks(mScrollRunnable);ViewCompat.postOnAnimation(mRecyclerView, this);}}};/*** Used for detecting fling swipe*/VelocityTracker mVelocityTracker;//re-used list for selecting a swap targetprivate List<ViewHolder> mSwapTargets;//re used for for sorting swap targetsprivate List<Integer> mDistances;/*** If drag & drop is supported, we use child drawing order to bring them to front.*/private RecyclerView.ChildDrawingOrderCallback mChildDrawingOrderCallback = null;/*** This keeps a reference to the child dragged by the user. Even after user stops dragging,* until view reaches its final position (end of recover animation), we keep a reference so* that it can be drawn above other children.*/View mOverdrawChild = null;/*** We cache the position of the overdraw child to avoid recalculating it each time child* position callback is called. This value is invalidated whenever a child is attached or* detached.*/int mOverdrawChildPosition = -1;/*** Used to detect long press.*/GestureDetectorCompat mGestureDetector;private final OnItemTouchListener mOnItemTouchListener= new OnItemTouchListener() {@Overridepublic boolean onInterceptTouchEvent(RecyclerView recyclerView, MotionEvent event) {mGestureDetector.onTouchEvent(event);if (DEBUG) {Log.d(TAG, "intercept: x:" + event.getX() + ",y:" + event.getY() + ", " + event);}final int action = MotionEventCompat.getActionMasked(event);if (action == MotionEvent.ACTION_DOWN) {mActivePointerId = event.getPointerId(0);mInitialTouchX = event.getX();mInitialTouchY = event.getY();obtainVelocityTracker();if (mSelected == null) {final RecoverAnimation animation = findAnimation(event);if (animation != null) {mInitialTouchX -= animation.mX;mInitialTouchY -= animation.mY;endRecoverAnimation(animation.mViewHolder, true);if (mPendingCleanup.remove(animation.mViewHolder.itemView)) {mCallback.clearView(mRecyclerView, animation.mViewHolder);}select(animation.mViewHolder, animation.mActionState);updateDxDy(event, mSelectedFlags, 0);}}} else if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {mActivePointerId = ACTIVE_POINTER_ID_NONE;select(null, ACTION_STATE_IDLE);} else if (mActivePointerId != ACTIVE_POINTER_ID_NONE) {// in a non scroll orientation, if distance change is above threshold, we// can select the itemfinal int index = event.findPointerIndex(mActivePointerId);if (DEBUG) {Log.d(TAG, "pointer index " + index);}if (index >= 0) {checkSelectForSwipe(action, event, index);}}if (mVelocityTracker != null) {mVelocityTracker.addMovement(event);}return mSelected != null;}@Overridepublic void onTouchEvent(RecyclerView recyclerView, MotionEvent event) {mGestureDetector.onTouchEvent(event);if (DEBUG) {Log.d(TAG,"on touch: x:" + mInitialTouchX + ",y:" + mInitialTouchY + ", :" + event);}if (mVelocityTracker != null) {mVelocityTracker.addMovement(event);}if (mActivePointerId == ACTIVE_POINTER_ID_NONE) {return;}final int action = MotionEventCompat.getActionMasked(event);final int activePointerIndex = event.findPointerIndex(mActivePointerId);if (activePointerIndex >= 0) {checkSelectForSwipe(action, event, activePointerIndex);}ViewHolder viewHolder = mSelected;if (viewHolder == null) {return;}switch (action) {case MotionEvent.ACTION_MOVE: {// Find the index of the active pointer and fetch its positionif (activePointerIndex >= 0) {updateDxDy(event, mSelectedFlags, activePointerIndex);moveIfNecessary(viewHolder);mRecyclerView.removeCallbacks(mScrollRunnable);mScrollRunnable.run();mRecyclerView.invalidate();}break;}case MotionEvent.ACTION_CANCEL:if (mVelocityTracker != null) {mVelocityTracker.clear();}// fall throughcase MotionEvent.ACTION_UP:select(null, ACTION_STATE_IDLE);mActivePointerId = ACTIVE_POINTER_ID_NONE;break;case MotionEvent.ACTION_POINTER_UP: {final int pointerIndex = MotionEventCompat.getActionIndex(event);final int pointerId = event.getPointerId(pointerIndex);if (pointerId == mActivePointerId) {// This was our active pointer going up. Choose a new// active pointer and adjust accordingly.final int newPointerIndex = pointerIndex == 0 ? 1 : 0;mActivePointerId = event.getPointerId(newPointerIndex);updateDxDy(event, mSelectedFlags, pointerIndex);}break;}}}@Overridepublic void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {if (!disallowIntercept) {return;}select(null, ACTION_STATE_IDLE);}};/*** Temporary rect instance that is used when we need to lookup Item decorations.*/private Rect mTmpRect;/*** When user started to drag scroll. Reset when we don't scroll*/private long mDragScrollStartTimeInMs;/*** Creates an ItemTouchHelper that will work with the given Callback.* <p>* You can attach ItemTouchHelper to a RecyclerView via* {@link #attachToRecyclerView(RecyclerView)}. Upon attaching, it will add an item decoration,* an onItemTouchListener and a Child attach / detach listener to the RecyclerView.** @param callback The Callback which controls the behavior of this touch helper.*/public ItemTouchHelper(Callback callback) {mCallback = callback;}private static boolean hitTest(View child, float x, float y, float left, float top) {return x >= left &&x <= left + child.getWidth() &&y >= top &&y <= top + child.getHeight();}/*** Attaches the ItemTouchHelper to the provided RecyclerView. If TouchHelper is already* attached to a RecyclerView, it will first detach from the previous one. You can call this* method with {@code null} to detach it from the current RecyclerView.** @param recyclerView The RecyclerView instance to which you want to add this helper or*                     {@code null} if you want to remove ItemTouchHelper from the current*                     RecyclerView.*/public void attachToRecyclerView(@Nullable RecyclerView recyclerView) {if (mRecyclerView == recyclerView) {return; // nothing to do}if (mRecyclerView != null) {destroyCallbacks();}mRecyclerView = recyclerView;if (mRecyclerView != null) {final Resources resources = recyclerView.getResources();mSwipeEscapeVelocity = resources.getDimension(R.dimen.item_touch_helper_swipe_escape_velocity);mMaxSwipeVelocity = resources.getDimension(R.dimen.item_touch_helper_swipe_escape_max_velocity);setupCallbacks();}}private void setupCallbacks() {ViewConfiguration vc = ViewConfiguration.get(mRecyclerView.getContext());mSlop = vc.getScaledTouchSlop();mRecyclerView.addItemDecoration(this);mRecyclerView.addOnItemTouchListener(mOnItemTouchListener);mRecyclerView.addOnChildAttachStateChangeListener(this);initGestureDetector();}private void destroyCallbacks() {mRecyclerView.removeItemDecoration(this);mRecyclerView.removeOnItemTouchListener(mOnItemTouchListener);mRecyclerView.removeOnChildAttachStateChangeListener(this);// clean all attachedfinal int recoverAnimSize = mRecoverAnimations.size();for (int i = recoverAnimSize - 1; i >= 0; i--) {final RecoverAnimation recoverAnimation = mRecoverAnimations.get(0);mCallback.clearView(mRecyclerView, recoverAnimation.mViewHolder);}mRecoverAnimations.clear();mOverdrawChild = null;mOverdrawChildPosition = -1;releaseVelocityTracker();}private void initGestureDetector() {if (mGestureDetector != null) {return;}mGestureDetector = new GestureDetectorCompat(mRecyclerView.getContext(),new ItemTouchHelperGestureListener());}private void getSelectedDxDy(float[] outPosition) {if ((mSelectedFlags & (LEFT | RIGHT)) != 0) {outPosition[0] = mSelectedStartX + mDx - mSelected.itemView.getLeft();} else {outPosition[0] = ViewCompat.getTranslationX(mSelected.itemView);}if ((mSelectedFlags & (UP | DOWN)) != 0) {outPosition[1] = mSelectedStartY + mDy - mSelected.itemView.getTop();} else {outPosition[1] = ViewCompat.getTranslationY(mSelected.itemView);}}@Overridepublic void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {float dx = 0, dy = 0;if (mSelected != null) {getSelectedDxDy(mTmpPosition);dx = mTmpPosition[0];dy = mTmpPosition[1];}mCallback.onDrawOver(c, parent, mSelected,mRecoverAnimations, mActionState, dx, dy);}@Overridepublic void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {// we don't know if RV changed something so we should invalidate this index.mOverdrawChildPosition = -1;float dx = 0, dy = 0;if (mSelected != null) {getSelectedDxDy(mTmpPosition);dx = mTmpPosition[0];dy = mTmpPosition[1];}mCallback.onDraw(c, parent, mSelected,mRecoverAnimations, mActionState, dx, dy);}/*** Starts dragging or swiping the given View. Call with null if you want to clear it.** @param selected    The ViewHolder to drag or swipe. Can be null if you want to cancel the*                    current action* @param actionState The type of action*/void select(ViewHolder selected, int actionState) {if (selected == mSelected && actionState == mActionState) {return;}mDragScrollStartTimeInMs = Long.MIN_VALUE;final int prevActionState = mActionState;// prevent duplicate animationsendRecoverAnimation(selected, true);mActionState = actionState;if (actionState == ACTION_STATE_DRAG) {// we remove after animation is complete. this means we only elevate the last drag// child but that should perform good enough as it is very hard to start dragging a// new child before the previous one settles.mOverdrawChild = selected.itemView;addChildDrawingOrderCallback();}int actionStateMask = (1 << (DIRECTION_FLAG_COUNT + DIRECTION_FLAG_COUNT * actionState))- 1;boolean preventLayout = false;if (mSelected != null) {final ViewHolder prevSelected = mSelected;if (prevSelected.itemView.getParent() != null) {final int swipeDir = prevActionState == ACTION_STATE_DRAG ? 0: swipeIfNecessary(prevSelected);releaseVelocityTracker();// find where we should animate tofinal float targetTranslateX, targetTranslateY;int animationType;boolean delete = mCallback.onNeedDelete(mSelected);switch (swipeDir) {case LEFT:case RIGHT:case START:case END:targetTranslateY = 0;targetTranslateX = Math.signum(mDx) * mRecyclerView.getWidth();break;case UP:case DOWN:targetTranslateX = 0;targetTranslateY = Math.signum(mDy) * mRecyclerView.getHeight();break;default:if(delete) {//  TODO: 17/7/13  缩小 位置targetTranslateX = 0;targetTranslateY = -mLimitUp;} else {targetTranslateX = 0;targetTranslateY = 0;}}if (prevActionState == ACTION_STATE_DRAG) {animationType = ANIMATION_TYPE_DRAG;} else if (swipeDir > 0) {animationType = ANIMATION_TYPE_SWIPE_SUCCESS;} else {animationType = ANIMATION_TYPE_SWIPE_CANCEL;}getSelectedDxDy(mTmpPosition);final float currentTranslateX = mTmpPosition[0];final float currentTranslateY = mTmpPosition[1];final RecoverAnimation rv = new RecoverAnimation(prevSelected, animationType,prevActionState, currentTranslateX, currentTranslateY,targetTranslateX, targetTranslateY, delete ? 0.5f : 1) {@Overridepublic void onAnimationEnd(ValueAnimatorCompat animation) {super.onAnimationEnd(animation);if (this.mOverridden) {return;}if (swipeDir <= 0) {// this is a drag or failed swipe. recover immediatelymCallback.clearView(mRecyclerView, prevSelected);// full cleanup will happen on onDrawOver} else {// wait until remove animation is complete.mPendingCleanup.add(prevSelected.itemView);mIsPendingCleanup = true;if (swipeDir > 0) {// Animation might be ended by other animators during a layout.// We defer callback to avoid editing adapter during a layout.postDispatchSwipe(this, swipeDir);}}// removed from the list after it is drawn for the last timeif (mOverdrawChild == prevSelected.itemView) {removeChildDrawingOrderCallbackIfNecessary(prevSelected.itemView);}}};final long duration = mCallback.getAnimationDuration(mRecyclerView, animationType,targetTranslateX - currentTranslateX, targetTranslateY - currentTranslateY);rv.setDuration(duration);mRecoverAnimations.add(rv);rv.start();preventLayout = true;} else {removeChildDrawingOrderCallbackIfNecessary(prevSelected.itemView);mCallback.clearView(mRecyclerView, prevSelected);}mSelected = null;}if (selected != null) {mSelectedFlags =(mCallback.getAbsoluteMovementFlags(mRecyclerView, selected) & actionStateMask)>> (mActionState * DIRECTION_FLAG_COUNT);mSelectedStartX = selected.itemView.getLeft();mSelectedStartY = selected.itemView.getTop();mSelected = selected;if (actionState == ACTION_STATE_DRAG) {mSelected.itemView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);}}final ViewParent rvParent = mRecyclerView.getParent();if (rvParent != null) {rvParent.requestDisallowInterceptTouchEvent(mSelected != null);}if (!preventLayout) {mRecyclerView.getLayoutManager().requestSimpleAnimationsInNextLayout();}mCallback.onSelectedChanged(mSelected, mActionState);mRecyclerView.invalidate();}void postDispatchSwipe(final RecoverAnimation anim, final int swipeDir) {// wait until animations are complete.mRecyclerView.post(new Runnable() {@Overridepublic void run() {if (mRecyclerView != null && mRecyclerView.isAttachedToWindow() &&!anim.mOverridden &&anim.mViewHolder.getAdapterPosition() != RecyclerView.NO_POSITION) {final RecyclerView.ItemAnimator animator = mRecyclerView.getItemAnimator();// if animator is running or we have other active recover animations, we try// not to call onSwiped because DefaultItemAnimator is not good at merging// animations. Instead, we wait and batch.if ((animator == null || !animator.isRunning(null))&& !hasRunningRecoverAnim()) {mCallback.onSwiped(anim.mViewHolder, swipeDir);} else {mRecyclerView.post(this);}}}});}boolean hasRunningRecoverAnim() {final int size = mRecoverAnimations.size();for (int i = 0; i < size; i++) {if (!mRecoverAnimations.get(i).mEnded) {return true;}}return false;}/*** If user drags the view to the edge, trigger a scroll if necessary.*/boolean scrollIfNecessary() {if (mSelected == null) {mDragScrollStartTimeInMs = Long.MIN_VALUE;return false;}final long now = System.currentTimeMillis();final long scrollDuration = mDragScrollStartTimeInMs== Long.MIN_VALUE ? 0 : now - mDragScrollStartTimeInMs;RecyclerView.LayoutManager lm = mRecyclerView.getLayoutManager();if (mTmpRect == null) {mTmpRect = new Rect();}int scrollX = 0;int scrollY = 0;lm.calculateItemDecorationsForChild(mSelected.itemView, mTmpRect);if (lm.canScrollHorizontally()) {int curX = (int) (mSelectedStartX + mDx);final int leftDiff = curX - mTmpRect.left - mRecyclerView.getPaddingLeft();if (mDx < 0 && leftDiff < 0) {scrollX = leftDiff;} else if (mDx > 0) {final int rightDiff =curX + mSelected.itemView.getWidth() + mTmpRect.right- (mRecyclerView.getWidth() - mRecyclerView.getPaddingRight());if (rightDiff > 0) {scrollX = rightDiff;}}}if (lm.canScrollVertically()) {int curY = (int) (mSelectedStartY + mDy);final int topDiff = curY - mTmpRect.top - mRecyclerView.getPaddingTop();if (mDy < 0 && topDiff < 0) {scrollY = topDiff;} else if (mDy > 0) {final int bottomDiff = curY + mSelected.itemView.getHeight() + mTmpRect.bottom -(mRecyclerView.getHeight() - mRecyclerView.getPaddingBottom());if (bottomDiff > 0) {scrollY = bottomDiff;}}}if (scrollX != 0) {scrollX = mCallback.interpolateOutOfBoundsScroll(mRecyclerView,mSelected.itemView.getWidth(), scrollX,mRecyclerView.getWidth(), scrollDuration);}if (scrollY != 0) {scrollY = mCallback.interpolateOutOfBoundsScroll(mRecyclerView,mSelected.itemView.getHeight(), scrollY,mRecyclerView.getHeight(), scrollDuration);}if (scrollX != 0 || scrollY != 0) {if (mDragScrollStartTimeInMs == Long.MIN_VALUE) {mDragScrollStartTimeInMs = now;}mRecyclerView.scrollBy(scrollX, scrollY);return true;}mDragScrollStartTimeInMs = Long.MIN_VALUE;return false;}private List<ViewHolder> findSwapTargets(ViewHolder viewHolder) {if (mSwapTargets == null) {mSwapTargets = new ArrayList<ViewHolder>();mDistances = new ArrayList<Integer>();} else {mSwapTargets.clear();mDistances.clear();}final int margin = mCallback.getBoundingBoxMargin();final int left = Math.round(mSelectedStartX + mDx) - margin;final int top = Math.round(mSelectedStartY + mDy) - margin;final int right = left + viewHolder.itemView.getWidth() + 2 * margin;final int bottom = top + viewHolder.itemView.getHeight() + 2 * margin;final int centerX = (left + right) / 2;final int centerY = (top + bottom) / 2;final RecyclerView.LayoutManager lm = mRecyclerView.getLayoutManager();final int childCount = lm.getChildCount();for (int i = 0; i < childCount; i++) {View other = lm.getChildAt(i);if (other == viewHolder.itemView) {continue;//myself!}if (other.getBottom() < top || other.getTop() > bottom|| other.getRight() < left || other.getLeft() > right) {continue;}final ViewHolder otherVh = mRecyclerView.getChildViewHolder(other);if (mCallback.canDropOver(mRecyclerView, mSelected, otherVh)) {// find the index to addfinal int dx = Math.abs(centerX - (other.getLeft() + other.getRight()) / 2);final int dy = Math.abs(centerY - (other.getTop() + other.getBottom()) / 2);final int dist = dx * dx + dy * dy;int pos = 0;final int cnt = mSwapTargets.size();for (int j = 0; j < cnt; j++) {if (dist > mDistances.get(j)) {pos++;} else {break;}}mSwapTargets.add(pos, otherVh);mDistances.add(pos, dist);}}return mSwapTargets;}/*** Checks if we should swap w/ another view holder.*/void moveIfNecessary(ViewHolder viewHolder) {if (mRecyclerView.isLayoutRequested()) {return;}if (mActionState != ACTION_STATE_DRAG) {return;}// 上移通知 line 条件if(mDy < -mLimitUpNotifyLine) {mCallback.onMoveUpToLine(viewHolder, true);} else {mCallback.onMoveUpToLine(viewHolder, false);}final float threshold = mCallback.getMoveThreshold(viewHolder);final int x = (int) (mSelectedStartX + mDx);final int y = (int) (mSelectedStartY + mDy);if (Math.abs(y - viewHolder.itemView.getTop()) < viewHolder.itemView.getHeight() * threshold&& Math.abs(x - viewHolder.itemView.getLeft())< viewHolder.itemView.getWidth() * threshold) {return;}List<ViewHolder> swapTargets = findSwapTargets(viewHolder);if (swapTargets.size() == 0) {return;}// may swap.ViewHolder target = mCallback.chooseDropTarget(viewHolder, swapTargets, x, y);if (target == null) {mSwapTargets.clear();mDistances.clear();return;}if(!mCallback.needMove(target)) {return;}final int toPosition = target.getAdapterPosition();final int fromPosition = viewHolder.getAdapterPosition();if (mCallback.onMove(mRecyclerView, viewHolder, target)) {// keep target visiblemCallback.onMoved(mRecyclerView, viewHolder, fromPosition,target, toPosition, x, y);}}@Overridepublic void onChildViewAttachedToWindow(View view) {}@Overridepublic void onChildViewDetachedFromWindow(View view) {removeChildDrawingOrderCallbackIfNecessary(view);final ViewHolder holder = mRecyclerView.getChildViewHolder(view);if (holder == null) {return;}if (mSelected != null && holder == mSelected) {select(null, ACTION_STATE_IDLE);} else {endRecoverAnimation(holder, false); // this may push it into pending cleanup list.if (mPendingCleanup.remove(holder.itemView)) {mCallback.clearView(mRecyclerView, holder);}}}/*** Returns the animation type or 0 if cannot be found.*/int endRecoverAnimation(ViewHolder viewHolder, boolean override) {final int recoverAnimSize = mRecoverAnimations.size();for (int i = recoverAnimSize - 1; i >= 0; i--) {final RecoverAnimation anim = mRecoverAnimations.get(i);if (anim.mViewHolder == viewHolder) {anim.mOverridden |= override;if (!anim.mEnded) {anim.cancel();}mRecoverAnimations.remove(i);return anim.mAnimationType;}}return 0;}@Overridepublic void getItemOffsets(Rect outRect, View view, RecyclerView parent,RecyclerView.State state) {outRect.setEmpty();}void obtainVelocityTracker() {if (mVelocityTracker != null) {mVelocityTracker.recycle();}mVelocityTracker = VelocityTracker.obtain();}private void releaseVelocityTracker() {if (mVelocityTracker != null) {mVelocityTracker.recycle();mVelocityTracker = null;}}private ViewHolder findSwipedView(MotionEvent motionEvent) {final RecyclerView.LayoutManager lm = mRecyclerView.getLayoutManager();if (mActivePointerId == ACTIVE_POINTER_ID_NONE) {return null;}final int pointerIndex = motionEvent.findPointerIndex(mActivePointerId);final float dx = motionEvent.getX(pointerIndex) - mInitialTouchX;final float dy = motionEvent.getY(pointerIndex) - mInitialTouchY;final float absDx = Math.abs(dx);final float absDy = Math.abs(dy);if (absDx < mSlop && absDy < mSlop) {return null;}if (absDx > absDy && lm.canScrollHorizontally()) {return null;} else if (absDy > absDx && lm.canScrollVertically()) {return null;}View child = findChildView(motionEvent);if (child == null) {return null;}return mRecyclerView.getChildViewHolder(child);}/*** Checks whether we should select a View for swiping.*/boolean checkSelectForSwipe(int action, MotionEvent motionEvent, int pointerIndex) {if (mSelected != null || action != MotionEvent.ACTION_MOVE|| mActionState == ACTION_STATE_DRAG || !mCallback.isItemViewSwipeEnabled()) {return false;}if (mRecyclerView.getScrollState() == RecyclerView.SCROLL_STATE_DRAGGING) {return false;}final ViewHolder vh = findSwipedView(motionEvent);if (vh == null) {return false;}final int movementFlags = mCallback.getAbsoluteMovementFlags(mRecyclerView, vh);final int swipeFlags = (movementFlags & ACTION_MODE_SWIPE_MASK)>> (DIRECTION_FLAG_COUNT * ACTION_STATE_SWIPE);if (swipeFlags == 0) {return false;}// mDx and mDy are only set in allowed directions. We use custom x/y here instead of// updateDxDy to avoid swiping if user moves more in the other directionfinal float x = motionEvent.getX(pointerIndex);final float y = motionEvent.getY(pointerIndex);// Calculate the distance movedfinal float dx = x - mInitialTouchX;final float dy = y - mInitialTouchY;// swipe target is chose w/o applying flags so it does not really check if swiping in that// direction is allowed. This why here, we use mDx mDy to check slope value again.final float absDx = Math.abs(dx);final float absDy = Math.abs(dy);if (absDx < mSlop && absDy < mSlop) {return false;}if (absDx > absDy) {if (dx < 0 && (swipeFlags & LEFT) == 0) {return false;}if (dx > 0 && (swipeFlags & RIGHT) == 0) {return false;}} else {if (dy < 0 && (swipeFlags & UP) == 0) {return false;}if (dy > 0 && (swipeFlags & DOWN) == 0) {return false;}}mDx = mDy = 0f;mActivePointerId = motionEvent.getPointerId(0);select(vh, ACTION_STATE_SWIPE);return true;}View findChildView(MotionEvent event) {// first check elevated views, if none, then call RVfinal float x = event.getX();final float y = event.getY();if (mSelected != null) {final View selectedView = mSelected.itemView;if (hitTest(selectedView, x, y, mSelectedStartX + mDx, mSelectedStartY + mDy)) {return selectedView;}}for (int i = mRecoverAnimations.size() - 1; i >= 0; i--) {final RecoverAnimation anim = mRecoverAnimations.get(i);final View view = anim.mViewHolder.itemView;if (hitTest(view, x, y, anim.mX, anim.mY)) {return view;}}return mRecyclerView.findChildViewUnder(x, y);}/*** Starts dragging the provided ViewHolder. By default, ItemTouchHelper starts a drag when a* View is long pressed. You can disable that behavior by overriding* {@link ItemTouchHelper.Callback#isLongPressDragEnabled()}.* <p>* For this method to work:* <ul>* <li>The provided ViewHolder must be a child of the RecyclerView to which this* ItemTouchHelper* is attached.</li>* <li>{@link ItemTouchHelper.Callback} must have dragging enabled.</li>* <li>There must be a previous touch event that was reported to the ItemTouchHelper* through RecyclerView's ItemTouchListener mechanism. As long as no other ItemTouchListener* grabs previous events, this should work as expected.</li>* </ul>* <p>* For example, if you would like to let your user to be able to drag an Item by touching one* of its descendants, you may implement it as follows:* <pre>*     viewHolder.dragButton.setOnTouchListener(new View.OnTouchListener() {*         public boolean onTouch(View v, MotionEvent event) {*             if (MotionEventCompat.getActionMasked(event) == MotionEvent.ACTION_DOWN) {*                 mItemTouchHelper.startDrag(viewHolder);*             }*             return false;*         }*     });* </pre>* <p>** @param viewHolder The ViewHolder to start dragging. It must be a direct child of*                   RecyclerView.* @see ItemTouchHelper.Callback#isItemViewSwipeEnabled()*/public void startDrag(ViewHolder viewHolder) {if (!mCallback.hasDragFlag(mRecyclerView, viewHolder)) {Log.e(TAG, "Start drag has been called but swiping is not enabled");return;}if (viewHolder.itemView.getParent() != mRecyclerView) {Log.e(TAG, "Start drag has been called with a view holder which is not a child of "+ "the RecyclerView which is controlled by this ItemTouchHelper.");return;}obtainVelocityTracker();mDx = mDy = 0f;select(viewHolder, ACTION_STATE_DRAG);}/*** Starts swiping the provided ViewHolder. By default, ItemTouchHelper starts swiping a View* when user swipes their finger (or mouse pointer) over the View. You can disable this* behavior* by overriding {@link ItemTouchHelper.Callback}* <p>* For this method to work:* <ul>* <li>The provided ViewHolder must be a child of the RecyclerView to which this* ItemTouchHelper is attached.</li>* <li>{@link ItemTouchHelper.Callback} must have swiping enabled.</li>* <li>There must be a previous touch event that was reported to the ItemTouchHelper* through RecyclerView's ItemTouchListener mechanism. As long as no other ItemTouchListener* grabs previous events, this should work as expected.</li>* </ul>* <p>* For example, if you would like to let your user to be able to swipe an Item by touching one* of its descendants, you may implement it as follows:* <pre>*     viewHolder.dragButton.setOnTouchListener(new View.OnTouchListener() {*         public boolean onTouch(View v, MotionEvent event) {*             if (MotionEventCompat.getActionMasked(event) == MotionEvent.ACTION_DOWN) {*                 mItemTouchHelper.startSwipe(viewHolder);*             }*             return false;*         }*     });* </pre>** @param viewHolder The ViewHolder to start swiping. It must be a direct child of*                   RecyclerView.*/public void startSwipe(ViewHolder viewHolder) {if (!mCallback.hasSwipeFlag(mRecyclerView, viewHolder)) {Log.e(TAG, "Start swipe has been called but dragging is not enabled");return;}if (viewHolder.itemView.getParent() != mRecyclerView) {Log.e(TAG, "Start swipe has been called with a view holder which is not a child of "+ "the RecyclerView controlled by this ItemTouchHelper.");return;}obtainVelocityTracker();mDx = mDy = 0f;select(viewHolder, ACTION_STATE_SWIPE);}RecoverAnimation findAnimation(MotionEvent event) {if (mRecoverAnimations.isEmpty()) {return null;}View target = findChildView(event);for (int i = mRecoverAnimations.size() - 1; i >= 0; i--) {final RecoverAnimation anim = mRecoverAnimations.get(i);if (anim.mViewHolder.itemView == target) {return anim;}}return null;}private float mLimitUpNotifyLine = 150;public void updateMoveLimit(float line) {mLimitUpNotifyLine = line;}private float mLimitLeft = 200;private float mLimitRight = 200;private float mLimitUp= 100;private float mLimitDown = 100;private boolean mMoveLimit;public void updateMoveLimit(boolean limit) {mMoveLimit = limit;}/**** @param limit boolean* @param left  >0* @param right >0* @param up >0* @param down >0*/public void updateMoveLimit(boolean limit, float left, float right, float up, float down) {mMoveLimit = limit;mLimitLeft = left;mLimitRight = right;mLimitUp = up;mLimitDown = down;}void updateDxDy(MotionEvent ev, int directionFlags, int pointerIndex) {final float x = ev.getX(pointerIndex);final float y = ev.getY(pointerIndex);// Calculate the distance movedmDx = x - mInitialTouchX;mDy = y - mInitialTouchY;if ((directionFlags & LEFT) == 0) {mDx = Math.max(0, mDx);}if ((directionFlags & RIGHT) == 0) {mDx = Math.min(0, mDx);}if ((directionFlags & UP) == 0) {mDy = Math.max(0, mDy);}if ((directionFlags & DOWN) == 0) {mDy = Math.min(0, mDy);}if(mMoveLimit) {if (mDx < 0) {mDx = Math.max(mDx, -mLimitLeft);} else {mDx = Math.min(mDx, mLimitRight);}}// 上下的限制是一定要有的,左右的限制是动态修改if (mDy < 0) {mDy = Math.max(mDy, -mLimitUp);} else {mDy = Math.min(mDy, mLimitDown);}if (DEBUG) {Log.d(TAG,"updateDxDy: dx: " + mDx + ",dy: " + mDy);}}private int swipeIfNecessary(ViewHolder viewHolder) {if (mActionState == ACTION_STATE_DRAG) {return 0;}final int originalMovementFlags = mCallback.getMovementFlags(mRecyclerView, viewHolder);final int absoluteMovementFlags = mCallback.convertToAbsoluteDirection(originalMovementFlags,ViewCompat.getLayoutDirection(mRecyclerView));final int flags = (absoluteMovementFlags& ACTION_MODE_SWIPE_MASK) >> (ACTION_STATE_SWIPE * DIRECTION_FLAG_COUNT);if (flags == 0) {return 0;}final int originalFlags = (originalMovementFlags& ACTION_MODE_SWIPE_MASK) >> (ACTION_STATE_SWIPE * DIRECTION_FLAG_COUNT);int swipeDir;if (Math.abs(mDx) > Math.abs(mDy)) {if ((swipeDir = checkHorizontalSwipe(viewHolder, flags)) > 0) {// if swipe dir is not in original flags, it should be the relative directionif ((originalFlags & swipeDir) == 0) {// convert to relativereturn Callback.convertToRelativeDirection(swipeDir,ViewCompat.getLayoutDirection(mRecyclerView));}return swipeDir;}if ((swipeDir = checkVerticalSwipe(viewHolder, flags)) > 0) {return swipeDir;}} else {if ((swipeDir = checkVerticalSwipe(viewHolder, flags)) > 0) {return swipeDir;}if ((swipeDir = checkHorizontalSwipe(viewHolder, flags)) > 0) {// if swipe dir is not in original flags, it should be the relative directionif ((originalFlags & swipeDir) == 0) {// convert to relativereturn Callback.convertToRelativeDirection(swipeDir,ViewCompat.getLayoutDirection(mRecyclerView));}return swipeDir;}}return 0;}private int checkHorizontalSwipe(ViewHolder viewHolder, int flags) {if ((flags & (LEFT | RIGHT)) != 0) {final int dirFlag = mDx > 0 ? RIGHT : LEFT;if (mVelocityTracker != null && mActivePointerId > -1) {mVelocityTracker.computeCurrentVelocity(PIXELS_PER_SECOND,mCallback.getSwipeVelocityThreshold(mMaxSwipeVelocity));final float xVelocity = VelocityTrackerCompat.getXVelocity(mVelocityTracker, mActivePointerId);final float yVelocity = VelocityTrackerCompat.getYVelocity(mVelocityTracker, mActivePointerId);final int velDirFlag = xVelocity > 0f ? RIGHT : LEFT;final float absXVelocity = Math.abs(xVelocity);if ((velDirFlag & flags) != 0 && dirFlag == velDirFlag &&absXVelocity >= mCallback.getSwipeEscapeVelocity(mSwipeEscapeVelocity) &&absXVelocity > Math.abs(yVelocity)) {return velDirFlag;}}final float threshold = mRecyclerView.getWidth() * mCallback.getSwipeThreshold(viewHolder);if ((flags & dirFlag) != 0 && Math.abs(mDx) > threshold) {return dirFlag;}}return 0;}private int checkVerticalSwipe(ViewHolder viewHolder, int flags) {if ((flags & (UP | DOWN)) != 0) {final int dirFlag = mDy > 0 ? DOWN : UP;if (mVelocityTracker != null && mActivePointerId > -1) {mVelocityTracker.computeCurrentVelocity(PIXELS_PER_SECOND,mCallback.getSwipeVelocityThreshold(mMaxSwipeVelocity));final float xVelocity = VelocityTrackerCompat.getXVelocity(mVelocityTracker, mActivePointerId);final float yVelocity = VelocityTrackerCompat.getYVelocity(mVelocityTracker, mActivePointerId);final int velDirFlag = yVelocity > 0f ? DOWN : UP;final float absYVelocity = Math.abs(yVelocity);if ((velDirFlag & flags) != 0 && velDirFlag == dirFlag &&absYVelocity >= mCallback.getSwipeEscapeVelocity(mSwipeEscapeVelocity) &&absYVelocity > Math.abs(xVelocity)) {return velDirFlag;}}final float threshold = mRecyclerView.getHeight() * mCallback.getSwipeThreshold(viewHolder);if ((flags & dirFlag) != 0 && Math.abs(mDy) > threshold) {return dirFlag;}}return 0;}private void addChildDrawingOrderCallback() {if (Build.VERSION.SDK_INT >= 21) {return;// we use elevation on Lollipop}if (mChildDrawingOrderCallback == null) {mChildDrawingOrderCallback = new RecyclerView.ChildDrawingOrderCallback() {@Overridepublic int onGetChildDrawingOrder(int childCount, int i) {if (mOverdrawChild == null) {return i;}int childPosition = mOverdrawChildPosition;if (childPosition == -1) {childPosition = mRecyclerView.indexOfChild(mOverdrawChild);mOverdrawChildPosition = childPosition;}if (i == childCount - 1) {return childPosition;}return i < childPosition ? i : i + 1;}};}mRecyclerView.setChildDrawingOrderCallback(mChildDrawingOrderCallback);}void removeChildDrawingOrderCallbackIfNecessary(View view) {if (view == mOverdrawChild) {mOverdrawChild = null;// only remove if we've addedif (mChildDrawingOrderCallback != null) {mRecyclerView.setChildDrawingOrderCallback(null);}}}/*** An interface which can be implemented by LayoutManager for better integration with* {@link ItemTouchHelper}.*/public static interface ViewDropHandler {/*** Called by the {@link ItemTouchHelper} after a View is dropped over another View.* <p>* A LayoutManager should implement this interface to get ready for the upcoming move* operation.* <p>* For example, LinearLayoutManager sets up a "scrollToPositionWithOffset" calls so that* the View under drag will be used as an anchor View while calculating the next layout,* making layout stay consistent.** @param view   The View which is being dragged. It is very likely that user is still*               dragging this View so there might be other*               {@link #prepareForDrop(View, View, int, int)} after this one.* @param target The target view which is being dropped on.* @param x      The <code>left</code> offset of the View that is being dragged. This value*               includes the movement caused by the user.* @param y      The <code>top</code> offset of the View that is being dragged. This value*               includes the movement caused by the user.*/public void prepareForDrop(View view, View target, int x, int y);}/*** 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.* <p>* To control which actions user can take on each view, you should override* {@link #getMovementFlags(RecyclerView, ViewHolder)} and return appropriate set* of direction flags. ({@link #LEFT}, {@link #RIGHT}, {@link #START}, {@link #END},* {@link #UP}, {@link #DOWN}). You can use* {@link #makeMovementFlags(int, int)} to easily construct it. Alternatively, you can use* {@link SimpleCallback}.* <p>* If user drags an item, ItemTouchHelper will call* {@link Callback#onMove(RecyclerView, ViewHolder, ViewHolder)* onMove(recyclerView, dragged, target)}.* Upon receiving this callback, you should move the item from the old position* ({@code dragged.getAdapterPosition()}) to new position ({@code target.getAdapterPosition()})* in your adapter and also call {@link RecyclerView.Adapter#notifyItemMoved(int, int)}.* To control where a View can be dropped, you can override* {@link #canDropOver(RecyclerView, ViewHolder, ViewHolder)}. When a* dragging View overlaps multiple other views, Callback chooses the closest View with which* dragged View might have changed positions. Although this approach works for many use cases,* if you have a custom LayoutManager, you can override* {@link #chooseDropTarget(ViewHolder, List, int, int)} to select a* custom drop target.* <p>* When a View is swiped, ItemTouchHelper animates it until it goes out of bounds, then calls* {@link #onSwiped(ViewHolder, int)}. At this point, you should update your* adapter (e.g. remove the item) and call related Adapter#notify event.*/@SuppressWarnings("UnusedParameters")public abstract static class Callback {public static final int DEFAULT_DRAG_ANIMATION_DURATION = 200;public static final int DEFAULT_SWIPE_ANIMATION_DURATION = 250;static final int RELATIVE_DIR_FLAGS = START | END |((START | END) << DIRECTION_FLAG_COUNT) |((START | END) << (2 * DIRECTION_FLAG_COUNT));private static final ItemTouchUIUtil sUICallback;private static final int ABS_HORIZONTAL_DIR_FLAGS = LEFT | RIGHT |((LEFT | RIGHT) << DIRECTION_FLAG_COUNT) |((LEFT | RIGHT) << (2 * DIRECTION_FLAG_COUNT));private static final Interpolator sDragScrollInterpolator = new Interpolator() {@Overridepublic float getInterpolation(float t) {return t * t * t * t * t;}};private static final Interpolator sDragViewScrollCapInterpolator = new Interpolator() {@Overridepublic float getInterpolation(float t) {t -= 1.0f;return t * t * t * t * t + 1.0f;}};/*** Drag scroll speed keeps accelerating until this many milliseconds before being capped.*/private static final long DRAG_SCROLL_ACCELERATION_LIMIT_TIME_MS = 2000;private int mCachedMaxScrollSpeed = -1;static {if (Build.VERSION.SDK_INT >= 21) {sUICallback = new ItemTouchUIUtilImpl.Lollipop();} else if (Build.VERSION.SDK_INT >= 11) {sUICallback = new ItemTouchUIUtilImpl.Honeycomb();} else {sUICallback = new ItemTouchUIUtilImpl.Gingerbread();}}/*** Returns the {@link ItemTouchUIUtil} that is used by the {@link Callback} class for* visual* changes on Views in response to user interactions. {@link ItemTouchUIUtil} has different* implementations for different platform versions.* <p>* By default, {@link Callback} applies these changes on* {@link ViewHolder#itemView}.* <p>* For example, if you have a use case where you only want the text to move when user* swipes over the view, you can do the following:* <pre>*     public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder){*         getDefaultUIUtil().clearView(((ItemTouchViewHolder) viewHolder).textView);*     }*     public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {*         if (viewHolder != null){*             getDefaultUIUtil().onSelected(((ItemTouchViewHolder) viewHolder).textView);*         }*     }*     public void onChildDraw(Canvas c, RecyclerView recyclerView,*             RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState,*             boolean isCurrentlyActive) {*         getDefaultUIUtil().onDraw(c, recyclerView,*                 ((ItemTouchViewHolder) viewHolder).textView, dX, dY,*                 actionState, isCurrentlyActive);*         return true;*     }*     public void onChildDrawOver(Canvas c, RecyclerView recyclerView,*             RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState,*             boolean isCurrentlyActive) {*         getDefaultUIUtil().onDrawOver(c, recyclerView,*                 ((ItemTouchViewHolder) viewHolder).textView, dX, dY,*                 actionState, isCurrentlyActive);*         return true;*     }* </pre>** @return The {@link ItemTouchUIUtil} instance that is used by the {@link Callback}*/public static ItemTouchUIUtil getDefaultUIUtil() {return sUICallback;}/*** Replaces a movement direction with its relative version by taking layout direction into* account.** @param flags           The flag value that include any number of movement flags.* @param layoutDirection The layout direction of the View. Can be obtained from*                        {@link ViewCompat#getLayoutDirection(View)}.* @return Updated flags which uses relative flags ({@link #START}, {@link #END}) instead* of {@link #LEFT}, {@link #RIGHT}.* @see #convertToAbsoluteDirection(int, int)*/public static int convertToRelativeDirection(int flags, int layoutDirection) {int masked = flags & ABS_HORIZONTAL_DIR_FLAGS;if (masked == 0) {return flags;// does not have any abs flags, good.}flags &= ~masked; //remove left / right.if (layoutDirection == ViewCompat.LAYOUT_DIRECTION_LTR) {// no change. just OR with 2 bits shifted mask and returnflags |= masked << 2; // START is 2 bits after LEFT, END is 2 bits after RIGHT.return flags;} else {// add RIGHT flag as STARTflags |= ((masked << 1) & ~ABS_HORIZONTAL_DIR_FLAGS);// first clean RIGHT bit then add LEFT flag as ENDflags |= ((masked << 1) & ABS_HORIZONTAL_DIR_FLAGS) << 2;}return flags;}/*** Convenience method to create movement flags.* <p>* For instance, if you want to let your items be drag & dropped vertically and swiped* left to be dismissed, you can call this method with:* <code>makeMovementFlags(UP | DOWN, LEFT);</code>** @param dragFlags  The directions in which the item can be dragged.* @param swipeFlags The directions in which the item can be swiped.* @return Returns an integer composed of the given drag and swipe flags.*/public static int makeMovementFlags(int dragFlags, int swipeFlags) {return makeFlag(ACTION_STATE_IDLE, swipeFlags | dragFlags) |makeFlag(ACTION_STATE_SWIPE, swipeFlags) | makeFlag(ACTION_STATE_DRAG,dragFlags);}/*** Shifts the given direction flags to the offset of the given action state.** @param actionState The action state you want to get flags in. Should be one of*                    {@link #ACTION_STATE_IDLE}, {@link #ACTION_STATE_SWIPE} or*                    {@link #ACTION_STATE_DRAG}.* @param directions  The direction flags. Can be composed from {@link #UP}, {@link #DOWN},*                    {@link #RIGHT}, {@link #LEFT} {@link #START} and {@link #END}.* @return And integer that represents the given directions in the provided actionState.*/public static int makeFlag(int actionState, int directions) {return directions << (actionState * DIRECTION_FLAG_COUNT);}/*** 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);/*** Converts a given set of flags to absolution direction which means {@link #START} and* {@link #END} are replaced with {@link #LEFT} and {@link #RIGHT} depending on the layout* direction.** @param flags           The flag value that include any number of movement flags.* @param layoutDirection The layout direction of the RecyclerView.* @return Updated flags which includes only absolute direction values.*/public int convertToAbsoluteDirection(int flags, int layoutDirection) {int masked = flags & RELATIVE_DIR_FLAGS;if (masked == 0) {return flags;// does not have any relative flags, good.}flags &= ~masked; //remove start / endif (layoutDirection == ViewCompat.LAYOUT_DIRECTION_LTR) {// no change. just OR with 2 bits shifted mask and returnflags |= masked >> 2; // START is 2 bits after LEFT, END is 2 bits after RIGHT.return flags;} else {// add START flag as RIGHTflags |= ((masked >> 1) & ~RELATIVE_DIR_FLAGS);// first clean start bit then add END flag as LEFTflags |= ((masked >> 1) & RELATIVE_DIR_FLAGS) >> 2;}return flags;}final int getAbsoluteMovementFlags(RecyclerView recyclerView,ViewHolder viewHolder) {final int flags = getMovementFlags(recyclerView, viewHolder);return convertToAbsoluteDirection(flags, ViewCompat.getLayoutDirection(recyclerView));}boolean hasDragFlag(RecyclerView recyclerView, ViewHolder viewHolder) {final int flags = getAbsoluteMovementFlags(recyclerView, viewHolder);return (flags & ACTION_MODE_DRAG_MASK) != 0;}boolean hasSwipeFlag(RecyclerView recyclerView,ViewHolder viewHolder) {final int flags = getAbsoluteMovementFlags(recyclerView, viewHolder);return (flags & ACTION_MODE_SWIPE_MASK) != 0;}/*** Return true if the current ViewHolder can be dropped over the the target ViewHolder.* <p>* This method is used when selecting drop target for the dragged View. After Views are* eliminated either via bounds check or via this method, resulting set of views will be* passed to {@link #chooseDropTarget(ViewHolder, List, int, int)}.* <p>* Default implementation returns true.** @param recyclerView The RecyclerView to which ItemTouchHelper is attached to.* @param current      The ViewHolder that user is dragging.* @param target       The ViewHolder which is below the dragged ViewHolder.* @return True if the dragged ViewHolder can be replaced with the target ViewHolder, false* otherwise.*/public boolean canDropOver(RecyclerView recyclerView, ViewHolder current,ViewHolder target) {return true;}public void onTouchUp(ViewHolder current) {}public boolean needMove(@NonNull RecyclerView.ViewHolder viewHolder) {return true;}/*** 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,@NonNull ViewHolder viewHolder,@NonNull ViewHolder target);/*** Returns whether ItemTouchHelper should start a drag and drop operation if an item is* long pressed.* <p>* Default value returns true but you may want to disable this if you want to start* dragging on a custom view touch using {@link #startDrag(ViewHolder)}.** @return True if ItemTouchHelper should start dragging an item when it is long pressed,* false otherwise. Default value is <code>true</code>.* @see #startDrag(ViewHolder)*/public boolean isLongPressDragEnabled() {return true;}public void onLongPressed(@NonNull ViewHolder viewHolder) {}/*** Returns whether ItemTouchHelper should start a swipe operation if a pointer is swiped* over the View.* <p>* Default value returns true but you may want to disable this if you want to start* swiping on a custom view touch using {@link #startSwipe(ViewHolder)}.** @return True if ItemTouchHelper should start swiping an item when user swipes a pointer* over the View, false otherwise. Default value is <code>true</code>.* @see #startSwipe(ViewHolder)*/public boolean isItemViewSwipeEnabled() {return true;}/*** When finding views under a dragged view, by default, ItemTouchHelper searches for views* that overlap with the dragged View. By overriding this method, you can extend or shrink* the search box.** @return The extra margin to be added to the hit box of the dragged View.*/public int getBoundingBoxMargin() {return 0;}/*** Returns the fraction that the user should move the View to be considered as swiped.* The fraction is calculated with respect to RecyclerView's bounds.* <p>* Default value is .5f, which means, to swipe a View, user must move the View at least* half of RecyclerView's width or height, depending on the swipe direction.** @param viewHolder The ViewHolder that is being dragged.* @return A float value that denotes the fraction of the View size. Default value* is .5f .*/public float getSwipeThreshold(ViewHolder viewHolder) {return .5f;}/*** Returns the fraction that the user should move the View to be considered as it is* dragged. After a view is moved this amount, ItemTouchHelper starts checking for Views* below it for a possible drop.** @param viewHolder The ViewHolder that is being dragged.* @return A float value that denotes the fraction of the View size. Default value is* .5f .*/public float getMoveThreshold(ViewHolder viewHolder) {return .5f;}/*** Defines the minimum velocity which will be considered as a swipe action by the user.* <p>* You can increase this value to make it harder to swipe or decrease it to make it easier.* Keep in mind that ItemTouchHelper also checks the perpendicular velocity and makes sure* current direction velocity is larger then the perpendicular one. Otherwise, user's* movement is ambiguous. You can change the threshold by overriding* {@link #getSwipeVelocityThreshold(float)}.* <p>* The velocity is calculated in pixels per second.* <p>* The default framework value is passed as a parameter so that you can modify it with a* multiplier.** @param defaultValue The default value (in pixels per second) used by the*                     ItemTouchHelper.* @return The minimum swipe velocity. The default implementation returns the* <code>defaultValue</code> parameter.* @see #getSwipeVelocityThreshold(float)* @see #getSwipeThreshold(ViewHolder)*/public float getSwipeEscapeVelocity(float defaultValue) {return defaultValue;}/*** Defines the maximum velocity ItemTouchHelper will ever calculate for pointer movements.* <p>* To consider a movement as swipe, ItemTouchHelper requires it to be larger than the* perpendicular movement. If both directions reach to the max threshold, none of them will* be considered as a swipe because it is usually an indication that user rather tried to* scroll then swipe.* <p>* The velocity is calculated in pixels per second.* <p>* You can customize this behavior by changing this method. If you increase the value, it* will be easier for the user to swipe diagonally and if you decrease the value, user will* need to make a rather straight finger movement to trigger a swipe.** @param defaultValue The default value(in pixels per second) used by the ItemTouchHelper.* @return The velocity cap for pointer movements. The default implementation returns the* <code>defaultValue</code> parameter.* @see #getSwipeEscapeVelocity(float)*/public float getSwipeVelocityThreshold(float defaultValue) {return defaultValue;}/*** Called by ItemTouchHelper to select a drop target from the list of ViewHolders that* are under the dragged View.* <p>* Default implementation filters the View with which dragged item have changed position* in the drag direction. For instance, if the view is dragged UP, it compares the* <code>view.getTop()</code> of the two views before and after drag started. If that value* is different, the target view passes the filter.* <p>* Among these Views which pass the test, the one closest to the dragged view is chosen.* <p>* This method is called on the main thread every time user moves the View. If you want to* override it, make sure it does not do any expensive operations.** @param selected    The ViewHolder being dragged by the user.* @param dropTargets The list of ViewHolder that are under the dragged View and*                    candidate as a drop.* @param curX        The updated left value of the dragged View after drag translations*                    are applied. This value does not include margins added by*                    {@link RecyclerView.ItemDecoration}s.* @param curY        The updated top value of the dragged View after drag translations*                    are applied. This value does not include margins added by*                    {@link RecyclerView.ItemDecoration}s.* @return A ViewHolder to whose position the dragged ViewHolder should be* moved to.*/public ViewHolder chooseDropTarget(ViewHolder selected,List<ViewHolder> dropTargets, int curX, int curY) {int right = curX + selected.itemView.getWidth();int bottom = curY + selected.itemView.getHeight();ViewHolder winner = null;int winnerScore = -1;final int dx = curX - selected.itemView.getLeft();final int dy = curY - selected.itemView.getTop();final int targetsSize = dropTargets.size();for (int i = 0; i < targetsSize; i++) {final ViewHolder target = dropTargets.get(i);if (dx > 0) {int diff = target.itemView.getRight() - right;if (diff < 0 && target.itemView.getRight() > selected.itemView.getRight()) {final int score = Math.abs(diff);if (score > winnerScore) {winnerScore = score;winner = target;}}}if (dx < 0) {int diff = target.itemView.getLeft() - curX;if (diff > 0 && target.itemView.getLeft() < selected.itemView.getLeft()) {final int score = Math.abs(diff);if (score > winnerScore) {winnerScore = score;winner = target;}}}if (dy < 0) {int diff = target.itemView.getTop() - curY;if (diff > 0 && target.itemView.getTop() < selected.itemView.getTop()) {final int score = Math.abs(diff);if (score > winnerScore) {winnerScore = score;winner = target;}}}if (dy > 0) {int diff = target.itemView.getBottom() - bottom;if (diff < 0 && target.itemView.getBottom() > selected.itemView.getBottom()) {final int score = Math.abs(diff);if (score > winnerScore) {winnerScore = score;winner = target;}}}}return winner;}/*** 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);/*** Called when the ViewHolder swiped or dragged by the ItemTouchHelper is changed.* <p/>* If you override this method, you should call super.** @param viewHolder  The new ViewHolder that is being swiped or dragged. Might be null if*                    it is cleared.* @param actionState One of {@link ItemTouchHelper#ACTION_STATE_IDLE},*                    {@link ItemTouchHelper#ACTION_STATE_SWIPE} or*                    {@link ItemTouchHelper#ACTION_STATE_DRAG}.* @see #clearView(RecyclerView, ViewHolder)*/public void onSelectedChanged(ViewHolder viewHolder, int actionState) {if (viewHolder != null) {sUICallback.onSelected(viewHolder.itemView);}}private int getMaxDragScroll(RecyclerView recyclerView) {if (mCachedMaxScrollSpeed == -1) {mCachedMaxScrollSpeed = recyclerView.getResources().getDimensionPixelSize(R.dimen.item_touch_helper_max_drag_scroll_per_frame);}return mCachedMaxScrollSpeed;}public void onMoveUpToLine(ViewHolder viewHolder, boolean inside){//}public boolean onNeedDelete(ViewHolder viewHolder) {return false;}/*** Called when {@link #onMove(RecyclerView, ViewHolder, ViewHolder)} returns true.* <p>* ItemTouchHelper does not create an extra Bitmap or View while dragging, instead, it* modifies the existing View. Because of this reason, it is important that the View is* still part of the layout after it is moved. This may not work as intended when swapped* Views are close to RecyclerView bounds or there are gaps between them (e.g. other Views* which were not eligible for dropping over).* <p>* This method is responsible to give necessary hint to the LayoutManager so that it will* keep the View in visible area. For example, for LinearLayoutManager, this is as simple* as calling {@link LinearLayoutManager#scrollToPositionWithOffset(int, int)}.* <p>* Default implementation calls {@link RecyclerView#scrollToPosition(int)} if the View's* new position is likely to be out of bounds.* <p>* It is important to ensure the ViewHolder will stay visible as otherwise, it might be* removed by the LayoutManager if the move causes the View to go out of bounds. In that* case, drag will end prematurely.** @param recyclerView The RecyclerView controlled by the ItemTouchHelper.* @param viewHolder   The ViewHolder under user's control.* @param fromPos      The previous adapter position of the dragged item (before it was*                     moved).* @param target       The ViewHolder on which the currently active item has been dropped.* @param toPos        The new adapter position of the dragged item.* @param x            The updated left value of the dragged View after drag translations*                     are applied. This value does not include margins added by*                     {@link RecyclerView.ItemDecoration}s.* @param y            The updated top value of the dragged View after drag translations*                     are applied. This value does not include margins added by*                     {@link RecyclerView.ItemDecoration}s.*/public void onMoved(final RecyclerView recyclerView,final ViewHolder viewHolder, int fromPos, final ViewHolder target, int toPos, int x,int y) {final RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();if (layoutManager instanceof ViewDropHandler) {((ViewDropHandler) layoutManager).prepareForDrop(viewHolder.itemView,target.itemView, x, y);return;}// if layout manager cannot handle it, do some guessworkif (layoutManager.canScrollHorizontally()) {final int minLeft = layoutManager.getDecoratedLeft(target.itemView);if (minLeft <= recyclerView.getPaddingLeft()) {recyclerView.scrollToPosition(toPos);}final int maxRight = layoutManager.getDecoratedRight(target.itemView);if (maxRight >= recyclerView.getWidth() - recyclerView.getPaddingRight()) {recyclerView.scrollToPosition(toPos);}}if (layoutManager.canScrollVertically()) {final int minTop = layoutManager.getDecoratedTop(target.itemView);if (minTop <= recyclerView.getPaddingTop()) {recyclerView.scrollToPosition(toPos);}final int maxBottom = layoutManager.getDecoratedBottom(target.itemView);if (maxBottom >= recyclerView.getHeight() - recyclerView.getPaddingBottom()) {recyclerView.scrollToPosition(toPos);}}}void onDraw(Canvas c, RecyclerView parent, ViewHolder selected,List<ItemTouchHelper.RecoverAnimation> recoverAnimationList,int actionState, float dX, float dY) {final int recoverAnimSize = recoverAnimationList.size();for (int i = 0; i < recoverAnimSize; i++) {final ItemTouchHelper.RecoverAnimation anim = recoverAnimationList.get(i);anim.update();final int count = c.save();onChildDraw(c, parent, anim.mViewHolder, anim.mX, anim.mY, anim.mActionState,false);c.restoreToCount(count);}if (selected != null) {final int count = c.save();onChildDraw(c, parent, selected, dX, dY, actionState, true);c.restoreToCount(count);}}void onDrawOver(Canvas c, RecyclerView parent, ViewHolder selected,List<ItemTouchHelper.RecoverAnimation> recoverAnimationList,int actionState, float dX, float dY) {final int recoverAnimSize = recoverAnimationList.size();for (int i = 0; i < recoverAnimSize; i++) {final ItemTouchHelper.RecoverAnimation anim = recoverAnimationList.get(i);final int count = c.save();onChildDrawOver(c, parent, anim.mViewHolder, anim.mX, anim.mY, anim.mActionState,false);c.restoreToCount(count);}if (selected != null) {final int count = c.save();onChildDrawOver(c, parent, selected, dX, dY, actionState, true);c.restoreToCount(count);}boolean hasRunningAnimation = false;for (int i = recoverAnimSize - 1; i >= 0; i--) {final RecoverAnimation anim = recoverAnimationList.get(i);if (anim.mEnded && !anim.mIsPendingCleanup) {recoverAnimationList.remove(i);} else if (!anim.mEnded) {hasRunningAnimation = true;}}if (hasRunningAnimation) {parent.invalidate();}}/*** Called by the ItemTouchHelper when the user interaction with an element is over and it* also completed its animation.* <p>* This is a good place to clear all changes on the View that was done in* {@link #onSelectedChanged(ViewHolder, int)},* {@link #onChildDraw(Canvas, RecyclerView, ViewHolder, float, float, int,* boolean)} or* {@link #onChildDrawOver(Canvas, RecyclerView, ViewHolder, float, float, int, boolean)}.** @param recyclerView The RecyclerView which is controlled by the ItemTouchHelper.* @param viewHolder   The View that was interacted by the user.*/public void clearView(RecyclerView recyclerView, ViewHolder viewHolder) {sUICallback.clearView(viewHolder.itemView);}/*** Called by ItemTouchHelper on RecyclerView's onDraw callback.* <p>* If you would like to customize how your View's respond to user interactions, this is* a good place to override.* <p>* Default implementation translates the child by the given <code>dX</code>,* <code>dY</code>.* ItemTouchHelper also takes care of drawing the child after other children if it is being* dragged. This is done using child re-ordering mechanism. On platforms prior to L, this* is* achieved via {@link android.view.ViewGroup#getChildDrawingOrder(int, int)} and on L* and after, it changes View's elevation value to be greater than all other children.)** @param c                 The canvas which RecyclerView is drawing its children* @param recyclerView      The RecyclerView to which ItemTouchHelper is attached to* @param viewHolder        The ViewHolder which is being interacted by the User or it was*                          interacted and simply animating to its original position* @param dX                The amount of horizontal displacement caused by user's action* @param dY                The amount of vertical displacement caused by user's action* @param actionState       The type of interaction on the View. Is either {@link*                          #ACTION_STATE_DRAG} or {@link #ACTION_STATE_SWIPE}.* @param isCurrentlyActive True if this view is currently being controlled by the user or*                          false it is simply animating back to its original state.* @see #onChildDrawOver(Canvas, RecyclerView, ViewHolder, float, float, int,* boolean)*/public void onChildDraw(Canvas c, RecyclerView recyclerView,ViewHolder viewHolder,float dX, float dY, int actionState, boolean isCurrentlyActive) {sUICallback.onDraw(c, recyclerView, viewHolder.itemView, dX, dY, actionState,isCurrentlyActive);}/*** Called by ItemTouchHelper on RecyclerView's onDraw callback.* <p>* If you would like to customize how your View's respond to user interactions, this is* a good place to override.* <p>* Default implementation translates the child by the given <code>dX</code>,* <code>dY</code>.* ItemTouchHelper also takes care of drawing the child after other children if it is being* dragged. This is done using child re-ordering mechanism. On platforms prior to L, this* is* achieved via {@link android.view.ViewGroup#getChildDrawingOrder(int, int)} and on L* and after, it changes View's elevation value to be greater than all other children.)** @param c                 The canvas which RecyclerView is drawing its children* @param recyclerView      The RecyclerView to which ItemTouchHelper is attached to* @param viewHolder        The ViewHolder which is being interacted by the User or it was*                          interacted and simply animating to its original position* @param dX                The amount of horizontal displacement caused by user's action* @param dY                The amount of vertical displacement caused by user's action* @param actionState       The type of interaction on the View. Is either {@link*                          #ACTION_STATE_DRAG} or {@link #ACTION_STATE_SWIPE}.* @param isCurrentlyActive True if this view is currently being controlled by the user or*                          false it is simply animating back to its original state.* @see #onChildDrawOver(Canvas, RecyclerView, ViewHolder, float, float, int,* boolean)*/public void onChildDrawOver(Canvas c, RecyclerView recyclerView,ViewHolder viewHolder,float dX, float dY, int actionState, boolean isCurrentlyActive) {sUICallback.onDrawOver(c, recyclerView, viewHolder.itemView, dX, dY, actionState,isCurrentlyActive);}/*** Called by the ItemTouchHelper when user action finished on a ViewHolder and now the View* will be animated to its final position.* <p>* Default implementation uses ItemAnimator's duration values. If* <code>animationType</code> is {@link #ANIMATION_TYPE_DRAG}, it returns* {@link RecyclerView.ItemAnimator#getMoveDuration()}, otherwise, it returns* {@link RecyclerView.ItemAnimator#getRemoveDuration()}. If RecyclerView does not have* any {@link RecyclerView.ItemAnimator} attached, this method returns* {@code DEFAULT_DRAG_ANIMATION_DURATION} or {@code DEFAULT_SWIPE_ANIMATION_DURATION}* depending on the animation type.** @param recyclerView  The RecyclerView to which the ItemTouchHelper is attached to.* @param animationType The type of animation. Is one of {@link #ANIMATION_TYPE_DRAG},*                      {@link #ANIMATION_TYPE_SWIPE_CANCEL} or*                      {@link #ANIMATION_TYPE_SWIPE_SUCCESS}.* @param animateDx     The horizontal distance that the animation will offset* @param animateDy     The vertical distance that the animation will offset* @return The duration for the animation*/public long getAnimationDuration(RecyclerView recyclerView, int animationType,float animateDx, float animateDy) {final RecyclerView.ItemAnimator itemAnimator = recyclerView.getItemAnimator();if (itemAnimator == null) {return animationType == ANIMATION_TYPE_DRAG ? DEFAULT_DRAG_ANIMATION_DURATION: DEFAULT_SWIPE_ANIMATION_DURATION;} else {return animationType == ANIMATION_TYPE_DRAG ? itemAnimator.getMoveDuration(): itemAnimator.getRemoveDuration();}}/*** Called by the ItemTouchHelper when user is dragging a view out of bounds.* <p>* You can override this method to decide how much RecyclerView should scroll in response* to this action. Default implementation calculates a value based on the amount of View* out of bounds and the time it spent there. The longer user keeps the View out of bounds,* the faster the list will scroll. Similarly, the larger portion of the View is out of* bounds, the faster the RecyclerView will scroll.** @param recyclerView        The RecyclerView instance to which ItemTouchHelper is*                            attached to.* @param viewSize            The total size of the View in scroll direction, excluding*                            item decorations.* @param viewSizeOutOfBounds The total size of the View that is out of bounds. This value*                            is negative if the View is dragged towards left or top edge.* @param totalSize           The total size of RecyclerView in the scroll direction.* @param msSinceStartScroll  The time passed since View is kept out of bounds.* @return The amount that RecyclerView should scroll. Keep in mind that this value will* be passed to {@link RecyclerView#scrollBy(int, int)} method.*/public int interpolateOutOfBoundsScroll(RecyclerView recyclerView,int viewSize, int viewSizeOutOfBounds,int totalSize, long msSinceStartScroll) {final int maxScroll = getMaxDragScroll(recyclerView);final int absOutOfBounds = Math.abs(viewSizeOutOfBounds);final int direction = (int) Math.signum(viewSizeOutOfBounds);// might be negative if other directionfloat outOfBoundsRatio = Math.min(1f, 1f * absOutOfBounds / viewSize);final int cappedScroll = (int) (direction * maxScroll *sDragViewScrollCapInterpolator.getInterpolation(outOfBoundsRatio));final float timeRatio;if (msSinceStartScroll > DRAG_SCROLL_ACCELERATION_LIMIT_TIME_MS) {timeRatio = 1f;} else {timeRatio = (float) msSinceStartScroll / DRAG_SCROLL_ACCELERATION_LIMIT_TIME_MS;}final int value = (int) (cappedScroll * sDragScrollInterpolator.getInterpolation(timeRatio));if (value == 0) {return viewSizeOutOfBounds > 0 ? 1 : -1;}return value;}}/*** A simple wrapper to the default Callback which you can construct with drag and swipe* directions and this class will handle the flag callbacks. You should still override onMove* or* onSwiped depending on your use case.* <p>* <pre>* ItemTouchHelper mIth = new ItemTouchHelper(*     new ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP | ItemTouchHelper.DOWN,*         ItemTouchHelper.LEFT) {*         public abstract boolean onMove(RecyclerView recyclerView,*             ViewHolder viewHolder, ViewHolder target) {*             final int fromPos = viewHolder.getAdapterPosition();*             final int toPos = target.getAdapterPosition();*             // move item in `fromPos` to `toPos` in adapter.*             return true;// true if moved, false otherwise*         }*         public void onSwiped(ViewHolder viewHolder, int direction) {*             // remove from adapter*         }* });* </pre>*/public abstract static class SimpleCallback extends Callback {private int mDefaultSwipeDirs;private int mDefaultDragDirs;/*** Creates a Callback for the given drag and swipe allowance. These values serve as* defaults* and if you want to customize behavior per ViewHolder, you can override* {@link #getSwipeDirs(RecyclerView, ViewHolder)}* and / or {@link #getDragDirs(RecyclerView, ViewHolder)}.** @param dragDirs  Binary OR of direction flags in which the Views can be dragged. Must be*                  composed of {@link #LEFT}, {@link #RIGHT}, {@link #START}, {@link*                  #END},*                  {@link #UP} and {@link #DOWN}.* @param swipeDirs Binary OR of direction flags in which the Views can be swiped. Must be*                  composed of {@link #LEFT}, {@link #RIGHT}, {@link #START}, {@link*                  #END},*                  {@link #UP} and {@link #DOWN}.*/public SimpleCallback(int dragDirs, int swipeDirs) {mDefaultSwipeDirs = swipeDirs;mDefaultDragDirs = dragDirs;}/*** Updates the default swipe directions. For example, you can use this method to toggle* certain directions depending on your use case.** @param defaultSwipeDirs Binary OR of directions in which the ViewHolders can be swiped.*/public void setDefaultSwipeDirs(int defaultSwipeDirs) {mDefaultSwipeDirs = defaultSwipeDirs;}/*** Updates the default drag directions. For example, you can use this method to toggle* certain directions depending on your use case.** @param defaultDragDirs Binary OR of directions in which the ViewHolders can be dragged.*/public void setDefaultDragDirs(int defaultDragDirs) {mDefaultDragDirs = defaultDragDirs;}/*** Returns the swipe directions for the provided ViewHolder.* Default implementation returns the swipe directions that was set via constructor or* {@link #setDefaultSwipeDirs(int)}.** @param recyclerView The RecyclerView to which the ItemTouchHelper is attached to.* @param viewHolder   The RecyclerView for which the swipe direction is queried.* @return A binary OR of direction flags.*/public int getSwipeDirs(RecyclerView recyclerView, ViewHolder viewHolder) {return mDefaultSwipeDirs;}/*** Returns the drag directions for the provided ViewHolder.* Default implementation returns the drag directions that was set via constructor or* {@link #setDefaultDragDirs(int)}.** @param recyclerView The RecyclerView to which the ItemTouchHelper is attached to.* @param viewHolder   The RecyclerView for which the swipe direction is queried.* @return A binary OR of direction flags.*/public int getDragDirs(RecyclerView recyclerView, ViewHolder viewHolder) {return mDefaultDragDirs;}@Overridepublic int getMovementFlags(RecyclerView recyclerView, ViewHolder viewHolder) {return makeMovementFlags(getDragDirs(recyclerView, viewHolder),getSwipeDirs(recyclerView, viewHolder));}}private class ItemTouchHelperGestureListener extends GestureDetector.SimpleOnGestureListener {ItemTouchHelperGestureListener() {}@Overridepublic boolean onDown(MotionEvent e) {return true;}@Overridepublic void onLongPress(MotionEvent e) {View child = findChildView(e);if (child != null) {ViewHolder vh = mRecyclerView.getChildViewHolder(child);if (vh != null) {if (!mCallback.hasDragFlag(mRecyclerView, vh)) {return;}int pointerId = e.getPointerId(0);// Long press is deferred.// Check w/ active pointer id to avoid selecting after motion// event is canceled.if (pointerId == mActivePointerId) {final int index = e.findPointerIndex(mActivePointerId);final float x = e.getX(index);final float y = e.getY(index);mInitialTouchX = x;mInitialTouchY = y;mDx = mDy = 0f;if (DEBUG) {Log.d(TAG,"onlong press: x:" + mInitialTouchX + ",y:" + mInitialTouchY);}if (mCallback.isLongPressDragEnabled()) {select(vh, ACTION_STATE_DRAG);mCallback.onLongPressed(vh);}}}}}}/*** RecoverAnimation will cancel, only the last will call onAnimationEnd();*/private class RecoverAnimation implements AnimatorListenerCompat {final float mStartDx;final float mStartDy;final float mTargetX;final float mTargetY;final ViewHolder mViewHolder;final int mActionState;private final ValueAnimatorCompat mValueAnimator;final int mAnimationType;public boolean mIsPendingCleanup;float mX;float mY;// if user starts touching a recovering view, we put it into interaction mode again,// instantly.boolean mOverridden = false;boolean mEnded = false;private float mFraction;private float mTargetSize = 1;private float mNormalHeight;private float mNormalWidth;public RecoverAnimation(ViewHolder viewHolder, int animationType,int actionState, float startDx, float startDy, float targetX, float targetY) {this(viewHolder, animationType, actionState, startDx, startDy, targetX, targetY, 1);}public RecoverAnimation(ViewHolder viewHolder, int animationType,int actionState, float startDx, float startDy,float targetX, float targetY, float targetSize) {mActionState = actionState;mAnimationType = animationType;mViewHolder = viewHolder;mStartDx = startDx;mStartDy = startDy;mTargetX = targetX;mTargetY = targetY;mTargetSize = targetSize;mValueAnimator = AnimatorCompatHelper.emptyValueAnimator();mValueAnimator.addUpdateListener(new AnimatorUpdateListenerCompat() {@Overridepublic void onAnimationUpdate(ValueAnimatorCompat animation) {setFraction(animation.getAnimatedFraction());}});mValueAnimator.setTarget(viewHolder.itemView);mValueAnimator.addListener(this);setFraction(0f);}public void setDuration(long duration) {mValueAnimator.setDuration(duration);}public void start() {mViewHolder.setIsRecyclable(false);if(mTargetSize != 1) {mViewHolder.itemView.post(new Runnable() {@Overridepublic void run() {mNormalHeight = mViewHolder.itemView.getHeight();mNormalWidth = mViewHolder.itemView.getWidth();mValueAnimator.start();}});} else {mValueAnimator.start();}}public void cancel() {mValueAnimator.cancel();}public void setFraction(float fraction) {mFraction = fraction;}/*** We run updates on onDraw method but use the fraction from animator callback.* This way, we can sync translate x/y values w/ the animators to avoid one-off frames.*/public void update() {if (mStartDx == mTargetX) {mX = ViewCompat.getTranslationX(mViewHolder.itemView);} else {mX = mStartDx + mFraction * (mTargetX - mStartDx);}if (mStartDy == mTargetY) {mY = ViewCompat.getTranslationY(mViewHolder.itemView);} else {mY = mStartDy + mFraction * (mTargetY - mStartDy);}// mFraction 0...1fif(mTargetSize < 1) {ViewGroup.LayoutParams params = mViewHolder.itemView.getLayoutParams();params.height = (int) (mNormalHeight - (mFraction * mTargetSize * mNormalHeight));params.width = (int) (mNormalWidth - (mFraction * mTargetSize * mNormalWidth));mViewHolder.itemView.setLayoutParams(params);} else if(mTargetSize > 1) {ViewGroup.LayoutParams params = mViewHolder.itemView.getLayoutParams();params.height = (int) (mNormalHeight + (mFraction * mTargetSize * mNormalHeight));params.width = (int) (mNormalWidth + (mFraction * mTargetSize * mNormalWidth));mViewHolder.itemView.setLayoutParams(params);}}@Overridepublic void onAnimationStart(ValueAnimatorCompat animation) {}@Overridepublic void onAnimationEnd(ValueAnimatorCompat animation) {if (!mEnded) {mViewHolder.setIsRecyclable(true);}mEnded = true;}@Overridepublic void onAnimationCancel(ValueAnimatorCompat animation) {setFraction(1f); //make sure we recover the view's state.}@Overridepublic void onAnimationRepeat(ValueAnimatorCompat animation) {}}
}
  • helper.ItemTouchUIUtil.class (直接复制源代码
    com.benqu.wuta.test.recycleView.helper.ItemTouchUIUtil.class)
  • helper.ItemTouchUIUtilImpl.class (直接复制源代码
    com.benqu.wuta.test.recycleView.helper.ItemTouchUIUtilImpl.class)

注:本文仅限个人学习交流使用

Android之仿B612咔叽滤镜列表操作相关推荐

  1. android高仿微信聊天消息列表自由复制文字,双击查看文本内容

    SelectTextHelper-高仿微信聊天消息列表自由复制文字,双击查看文本内容 掘金地址 github地址 SelectTextHelper打造一个全网最逼近微信聊天消息自由复制,双击查看文本内 ...

  2. Android中高仿快牙实现Socket列表的展示,获取安卓手机系统安装的应用和自己安装的应用相关信息

    在安卓中,我们看到了快牙.茄子快传等软件的功能的使用.实现端对端的进行传输数据,根据最近的需求了解和检测,并且在网上疯狂的进行搜索了一下.我研究了一下高仿茄子快传的开源代码,发现里面展示应用的列表发现 ...

  3. android 高仿拼多多,Android高仿拼多多分类列表

    package com.zkteam.discover.fresco; import android.content.Context; import android.graphics.Bitmap; ...

  4. Android 高仿QQ 好友分组列表

    实现的效果如下: 用ExpandableListView实现, 先看Activity的代码: public class BuddyActivity extends Activity {Expandab ...

  5. android+高仿+日历,项目源码--Android天气日历精致UI源码

    技术要点: 1. 天气日历精致UI 2. Android的Http通信技术 3. Android的天气信息解析 4. Android的日历信息的统计 5. Andorid的地理位置的管理 6.源码带有 ...

  6. android 高仿大众点评,高仿大众点评商家列表

    原生android,高仿大众点评商家列表; 废话不多说了,上代码,效果图 适配器 class MyAdapter extends BaseAdapter { protected final int m ...

  7. android 仿qq好友动态,Android UI仿QQ好友列表分组悬浮效果

    本文实例为大家分享了Android UI仿QQ好友列表分组悬浮效果的具体代码,供大家参考,具体内容如下 楼主是在平板上測试的.图片略微有点大,大家看看效果就好 接下来贴源代码: PinnedHeade ...

  8. android仿高德地图首页,Android BottomSheet 的使用(仿高德地图的列表效果)

    最近项目中突然要实现高德地图中列表的效果,刚开始一筹莫展,以为是自定义控件还是通过手势进行判断 ,果断蒙了,百度谷歌了一下最后发现原来谷歌早就就出来了这样的效果--android.support.de ...

  9. android 仿微信demo————微信顶部操作栏界面实现

    android 仿微信demo----微信启动界面实现 android 仿微信demo----注册功能实现(移动端) android 仿微信demo----注册功能实现(服务端) android 仿微 ...

最新文章

  1. 光伏产品发展之路:从反倾销到欧盟加收税款
  2. Java代码实现执行Linux服务器命令
  3. [时钟]配置日期时间并同步到硬件
  4. 64位 vs2010 和 opencv2.4.4 配置问题
  5. JAVA:Java二维数组处理-Bit型转Bitmap
  6. UliPad 初体验----python 开发利器
  7. java传递引用参数
  8. 步步为营 .NET 代码重构学习笔记 七
  9. MS CRM 2011 Audit
  10. 一图助你搞明白Spring应用上下文初始化流程!
  11. var模型可以用spss做吗_VAR模型一般几个变量
  12. 完全背包问题(模板)
  13. Introductory Combinatorics 5th Solutions Chapter1 summary
  14. Scrum立会报告+燃尽图(十月二十七日总第十八次)
  15. 如何用Python记录微信撤回的消息
  16. 作为程序员你应该会的软件
  17. ChatGPT助力之论文速成秘籍
  18. C语言的字母大小写转化
  19. 图的基本操作(C语言)
  20. 计算机组成的了LDAR,静态存储器实验报告

热门文章

  1. 公用计算机打不开扫雷,win7系统自带扫雷游戏打不开的解决方法
  2. 记录docker文件夹下的overlay2下文件被删,致使gitlab的持续集成(ci)构建过程异常的一次排查过程
  3. A、B、C三类IP地址范围有哪些?5张表格收藏好就够了!
  4. Android模拟器超出电脑屏幕的解决方法
  5. java-经纬度有关的计算(半径内的经纬度范围和两点间距离)
  6. 少儿编程启蒙课程1:方格纸编程 揭秘算法
  7. TestStand-数据类型
  8. 怎样卸载deepin系统_Deepin深度系统系统卸载教程 - 安装卸载 - Deepin深度系统用户手册...
  9. Manacher Algorithm马拉车算法详解
  10. ios识别人脸自动拍照_真的逆天?iOS 10相册识别人脸的秘密