大致意思就是处理拖动的工具(swipe-drag&drop),同时通过回调监听用户的交互,处理事件。
使用方法:
mItemTouchHelper = new ItemTouchHelper(new DragItemCallBack(this)); mItemTouchHelper.attachToRecyclerView(mRecyclerView);
其中,DragItemCallBack继承ItemTouchHelper.CallBack,拖动的主要逻辑就是通过重写其中的方法来实现的。
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选中的方法给我们使用,这样就可以直接使用了。
定义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