RecycleView 仿支付宝实现item拖动效果

RecycleView 仿支付宝实现item拖动效果

先看效果图:

RecycleView 仿支付宝实现item拖动效果_第1张图片

仿支付宝首页展示,长按item可以拖动,删除,点击等。使用ItemTouchHelper即可快速实现这种效果(RecycleView真的是相当强大)。

ItemTouchHelper

首先看它在api中的定义:
RecycleView 仿支付宝实现item拖动效果_第2张图片

大致意思就是处理拖动的工具(swipe-drag&drop),同时通过回调监听用户的交互,处理事件。
使用方法:

mItemTouchHelper = new ItemTouchHelper(new  DragItemCallBack(this));     mItemTouchHelper.attachToRecyclerView(mRecyclerView);

其中,DragItemCallBack继承ItemTouchHelper.CallBack,拖动的主要逻辑就是通过重写其中的方法来实现的。

  • getMovementFlags(RecyclerView recyclerView, ViewHolder, ViewHolder)
  • onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target)
  • onSwiped(ViewHolder, int)

getMovementFlags方法返回每隔行动(闲置,拖动,刷)的方向,比如拖动的方向定义:

@Override
public int getMovementFlags(RecyclerView recyclerView,  RecyclerView.ViewHolder viewHolder) {
        int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
        return makeMovementFlags(dragFlags, 0);
    }

onMove方法里的两个ViewHolder就是移动前和移动后的Item,通过getAdapterPosition()就能获取移动的位置。在实际应用中,我们往往需要点击item来处理逻辑,所以我们先写一个回调,把移动和点击的逻辑写在一起:

public interface RecycleCallBack {
    //item的点击事件
    void itemOnClick(int position,View view);

    void onMove(int from,int to);
}

回来onMove方法:

 @Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
       int start = viewHolder.getAdapterPosition();
       int end = target.getAdapterPosition();
       mCallBack.onMove(viewHolder.getAdapterPosition(), target.getAdapterPosition());
        return true;
    }

onSwip方法在暂时不需要(要实现左右滑动删除时可以使用)。另外,它也准备了监听item选中的方法给我们使用,这样就可以直接使用了。RecycleView 仿支付宝实现item拖动效果_第3张图片

定义item选中和未选中的回调方法:

public interface DragHolderCallBack {

    void onSelect();

    void onClear();
}

在ItemTouchHelper.CallBaCK的使用:

 @Override
    public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
        super.onSelectedChanged(viewHolder, actionState);
        if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) {
            if (viewHolder instanceof DragHolderCallBack) {
                DragHolderCallBack holder = (DragHolderCallBack) viewHolder;
                holder.onSelect();
            }
        }
    }

    @Override
    public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
        super.clearView(recyclerView, viewHolder);
        DragHolderCallBack holder = (DragHolderCallBack) viewHolder;
        if (!recyclerView.isComputingLayout()) {
            if (viewHolder instanceof DragHolderCallBack)
                holder.onClear();
        }
    }

需要注意的是,在clearView中的这段代码!recyclerView.isComputingLayout(),这个是防止RecycleView还在计算视图的时候,改变了item的状态。否则会报出异常:cannot call this method while recyclerview is computing a layout or scrolling。
完整的DragItemCallBack方法:

public class DragItemCallBack extends ItemTouchHelper.Callback {

    private RecycleCallBack mCallBack;

    public DragItemCallBack(RecycleCallBack callBack) {
        this.mCallBack = callBack;
    }

    @Override
    public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
        int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
        int swipeFlags = ItemTouchHelper.START | ItemTouchHelper.END;
        int action = ItemTouchHelper.ACTION_STATE_IDLE | ItemTouchHelper.ACTION_STATE_DRAG;
//        return makeFlag(action,dragFlags);
        return makeMovementFlags(dragFlags, 0);
    }

    @Override
    public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
        int start = viewHolder.getAdapterPosition();
        int end = target.getAdapterPosition();
        mCallBack.onMove(viewHolder.getAdapterPosition(), target.getAdapterPosition());
        return true;
    }

    @Override
    public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
    }

    @Override
    public boolean isLongPressDragEnabled() {
        return true;
    }

    @Override
    public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
        super.onSelectedChanged(viewHolder, actionState);
        if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) {
            if (viewHolder instanceof DragHolderCallBack) {
                DragHolderCallBack holder = (DragHolderCallBack) viewHolder;
                holder.onSelect();
            }
        }
    }

    @Override
    public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
        super.clearView(recyclerView, viewHolder);
        DragHolderCallBack holder = (DragHolderCallBack) viewHolder;
        if (!recyclerView.isComputingLayout()) {
            if (viewHolder instanceof DragHolderCallBack)
                holder.onClear();
        }
    }
}

在adapter中,只要实现DragHolderCallBack接口即可:

public class DragAdapter extends RecyclerView.Adapter<DragAdapter.DragHolder> {

    private List list;

    private RecycleCallBack mRecycleClick;
    public SparseArray show = new SparseArray<>();

    public DragAdapter(RecycleCallBack click, List data) {
        this.list = data;
        this.mRecycleClick = click;
    }

    public void setData(List data) {
        this.list = data;
    }

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

    @Override
    public void onBindViewHolder(final DragHolder holder, final int position) {
        holder.text.setText(list.get(position));
        if (null == show.get(position))
            holder.del.setVisibility(View.INVISIBLE);
        else
            holder.del.setVisibility(View.VISIBLE);
    }

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


    public class DragHolder extends RecyclerView.ViewHolder implements
            View.OnClickListener, DragHolderCallBack {

        public TextView text;
        public ImageView del;
        public RelativeLayout item;
        private RecycleCallBack mClick;

        public DragHolder(View itemView, RecycleCallBack click) {
            super(itemView);
            mClick = click;
            item = (RelativeLayout) itemView.findViewById(R.id.item);
            text = (TextView) itemView.findViewById(R.id.text);
            del = (ImageView) itemView.findViewById(R.id.del);
            item.setOnClickListener(this);
            del.setOnClickListener(this);
        }

        @Override
        public void onSelect() {
            itemView.setSelected(true);
            show.clear();
            show.put(getAdapterPosition(), getAdapterPosition());
            del.setVisibility(View.VISIBLE);
        }

        @Override
        public void onClear() {
            itemView.setSelected(false);
            notifyDataSetChanged();
        }

        @Override
        public void onClick(View v) {
            if (null != mClick) {
                show.clear();
                mClick.itemOnClick(getAdapterPosition(), v);
            }
        }
    }
}

show是为了保存当前点击item位置,为了删除效果,这个只是为了演示,可以选中其他更合适的方法。

我选择是在Activity中处理逻辑,同样可以选择在其他地方处理,只要实现RecycleCallBack借口就可以了。

public class MainActivity extends AppCompatActivity implements RecycleCallBack {

    private RecyclerView mRecyclerView;
    private DragAdapter mAdapter;
    private ArrayList mList;
    private ItemTouchHelper mItemTouchHelper;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mList = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            mList.add("" + i);
        }
        mRecyclerView = (RecyclerView) findViewById(R.id.recycle);
        mRecyclerView.setLayoutManager(new GridLayoutManager(this, 3));
        mAdapter = new DragAdapter(this, mList);
        mItemTouchHelper = new ItemTouchHelper(new DragItemCallBack(this));
        mItemTouchHelper.attachToRecyclerView(mRecyclerView);
        mRecyclerView.setAdapter(mAdapter);
    }

    @Override
    public void itemOnClick(int position, View view) {
        if (view.getId() == R.id.del) {
            mList.remove(position);
            mAdapter.setData(mList);
            mAdapter.notifyItemRemoved(position);
        } else {
            mAdapter.notifyDataSetChanged();
            Toast.makeText(MainActivity.this, "当前点击的是" + mList.get(position), Toast.LENGTH_SHORT).show();
        }
    }

    @Override
    public void onMove(int from, int to) {
        synchronized (this) {
            if (from > to) {
                int count = from - to;
                for (int i = 0; i < count; i++) {
                    Collections.swap(mList, from - i, from - i - 1);
                }
            }
            if (from < to) {
                int count = to - from;
                for (int i = 0; i < count; i++) {
                    Collections.swap(mList, from + i, from + i + 1);
                }
            }
            mAdapter.notifyItemMoved(from, to);
            mAdapter.show.clear();
            mAdapter.show.put(to, to);
            mAdapter.setData(mList);
        }
    }
}

需要注意的是:

synchronized (this) {
            if (from > to) {
                int count = from - to;
                for (int i = 0; i < count; i++) {
                    Collections.swap(mList, from - i, from - i - 1);
                }
            }
            if (from < to) {
                int count = to - from;
                for (int i = 0; i < count; i++) {
                    Collections.swap(mList, from + i, from + i + 1);
                }
            }
            mAdapter.notifyItemMoved(from, to);
            mAdapter.show.clear();
            mAdapter.show.put(to, to);
            mAdapter.setData(mList);
        }

注意:Collections.swap(mList, from - i, from - i - 1)方法只是对调了两个对象在list中的位置。而实际界面的显示是不一样的,所以要保证list的排列和显示的位置一样。

最后,完整代码地址:https://github.com/CangJieZ/DragRecycleView.git

你可能感兴趣的:(android)