Android之仿B612咔叽滤镜列表操作

本文主要记录模仿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 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值