本文主要记录模仿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;
@Override
protected 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() {
@Override
public void onTouchSelected(boolean show) {
mTouchDelView.setVisibility(show ? View.VISIBLE : View.INVISIBLE);
}
@Override
public void onTouchMoveUpToLine(boolean inside) {
showDelView(inside);
}
@Override
public void onAddToLike() {
mAddLikeView.setVisibility(View.VISIBLE);
mAddLikeView.animate().alpha(0.5f).setDuration(1500).withEndAction(new Runnable() {
@Override
public void run() {
mAddLikeView.animate().alpha(1.0f).setDuration(0).start();
mAddLikeView.setVisibility(View.GONE);
}
}).start();
}
@Override
public 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);
}
@Override
public 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
*/
@Override
public boolean needMove(@NonNull RecyclerView.ViewHolder viewHolder) {
int toPosition = viewHolder.getAdapterPosition();
return toPosition < mItemData.likeSize();
}
/**
* @param recyclerView
* @param viewHolder 拖动的ViewHolder
* @param target 目标位置的ViewHolder
* @return
*/
//拖动模块
@Override
public boolean onMove(RecyclerView recyclerView,
@NonNull RecyclerView.ViewHolder viewHolder,
@NonNull RecyclerView.ViewHolder target) {
int fromPosition = viewHolder.getAdapterPosition();//得到拖动ViewHolder的position
int toPosition = target.getAdapterPosition();//得到目标ViewHolder的position
mItemData.swap(fromPosition, toPosition);
notifyItemMoved(fromPosition, toPosition);
Log.i("slack", "onMove...");
//返回true表示执行拖动
return true;
}
@Override
public boolean isLongPressDragEnabled() {
return super.isLongPressDragEnabled();
}
@Override
public void onLongPressed(@NonNull RecyclerView.ViewHolder viewHolder) {
super.onLongPressed(viewHolder);
onLongClicked(viewHolder);
}
//删除模块
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
}
@Override
public 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...");
}
@Override
public 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);
}
}
}
@Override
public void onMoveUpToLine(RecyclerView.ViewHolder viewHolder, boolean inside) {
Log.i("slack", "onMoveUpToLine..." + inside);
onTouchMoveUpToLine(viewHolder, inside);
}
@Override
public boolean onNeedDelete(RecyclerView.ViewHolder viewHolder) {
return needDelete() && needMove(viewHolder);
}
@Override
public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
super.clearView(recyclerView, viewHolder);
//当动画已经结束的时候调用该方法,重写此方法可以实现恢复Item的初始状态
Log.i("slack", "clearView...");
onTouchFinish(viewHolder);
}
@Override
public 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 move
mItemData.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;
}
@Override
public 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);
}
}
@Override
public 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);
}
@Override
public 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() {
@Override
public void onClick(View v) {
Log.i("slack", "onClick...");
}
});
}
@Override
public 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