天天看點

RecycleView 仿支付寶實作item拖動效果RecycleView 仿支付寶實作item拖動效果

RecycleView 仿支付寶實作item拖動效果

先看效果圖:

RecycleView 仿支付寶實作item拖動效果RecycleView 仿支付寶實作item拖動效果

仿支付寶首頁展示,長按item可以拖動,删除,點選等。使用ItemTouchHelper即可快速實作這種效果(RecycleView真的是相當強大)。

ItemTouchHelper

首先看它在api中的定義:

RecycleView 仿支付寶實作item拖動效果RecycleView 仿支付寶實作item拖動效果

大緻意思就是處理拖動的工具(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, );
    }
           

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拖動效果RecycleView 仿支付寶實作item拖動效果

定義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, );
    }

    @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<String> list;

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

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

    public void setData(List<String> 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<String> mList;
    private ItemTouchHelper mItemTouchHelper;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mList = new ArrayList<>();
        for (int i = ; i < ; i++) {
            mList.add("" + i);
        }
        mRecyclerView = (RecyclerView) findViewById(R.id.recycle);
        mRecyclerView.setLayoutManager(new GridLayoutManager(this, ));
        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 = ; i < count; i++) {
                    Collections.swap(mList, from - i, from - i - );
                }
            }
            if (from < to) {
                int count = to - from;
                for (int i = ; i < count; i++) {
                    Collections.swap(mList, from + i, from + i + );
                }
            }
            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 = ; i < count; i++) {
                    Collections.swap(mList, from - i, from - i - );
                }
            }
            if (from < to) {
                int count = to - from;
                for (int i = ; i < count; i++) {
                    Collections.swap(mList, from + i, from + i + );
                }
            }
            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