RecycleView 仿支付寶實作item拖動效果
先看效果圖:
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsISM0ITOyMDMyIDNwcDM2EDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
仿支付寶首頁展示,長按item可以拖動,删除,點選等。使用ItemTouchHelper即可快速實作這種效果(RecycleView真的是相當強大)。
ItemTouchHelper
首先看它在api中的定義:
大緻意思就是處理拖動的工具(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選中的方法給我們使用,這樣就可以直接使用了。
定義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