本文共 6500 字,大约阅读时间需要 21 分钟。
上一篇博客用TabLayout实现了类似CSDN客户端首页选项卡的滑动效果:
然后发现了CSDN首页拖拽排序效果,先上自己完成的效果图:OK,一起看看怎么实现的吧。
1.界面布局:
主界面还是延续之前的TabLayout+ViewPager来实现的,右侧用的一个checkbox来控制视图的显示与隐藏。
2.主界面代码实现:
recyclerview = (RecyclerView) view.findViewById(R.id.sorts_recyclerview); if (type == TABS) { sortTxt.setVisibility(View.VISIBLE); sortView.setVisibility(View.VISIBLE); txt.setVisibility(View.GONE); recyclerview.setVisibility(View.VISIBLE); initSortViews(); } else { sortTxt.setVisibility(View.GONE); sortView.setVisibility(View.GONE); txt.setVisibility(View.VISIBLE); recyclerview.setVisibility(View.GONE); } } //加载排序布局 private void initSortViews() { adapter = new TabsAdapter(getActivity(), this); adapter.setListener(new TabsAdapter.onItemClickListener() { @Override public void onItemClick(int position) { Event item = new Event(position); EventBus.getDefault().post(item); } }); LinearLayoutManager manger = new LinearLayoutManager(getActivity()); recyclerview.setLayoutManager(manger); recyclerview.setAdapter(adapter); }
这里复用的是同一个Fragment,根据传递过来的参数,显示不同的界面。
可参考:当点击checkbox跳转过来的时候,传递一个不一样的参数,显示recyclerview。然后就是适配器的代码。
3.recyclerview的点击事件:
这里的处理我之前有一篇博客说的很详细:
这种接口回调的方式本质就是为了拿到被点击item的position,在Fragment获取到position以后,我们需要在Activity里面根据position进行切换界面的操作。activity与fragment的通信,我用的是Eventbus来进行处理的:@Subscribe public void onEventMainThread(Event item) { viewpager.setCurrentItem(item.getPosition()); }
目的就是在Activity中让viewpager切换到指定的位置
4.recyclerview的拖拽排序事件
这里使用了RecyclerView的ItemTouchHelper类来实现了Item的拖动和删除功能,ItemTouchHelper是v7包下的一个类,专门用来配合RecyclerView实现滑动删除和拖拽功能的类。我们看看怎么使用。public class SimpleItemTouchHelperCallback extends ItemTouchHelper.Callback { private onMoveAndSwipedListener mAdapter; public SimpleItemTouchHelperCallback(onMoveAndSwipedListener listener) { mAdapter = listener; } /** * 这个方法是用来设置我们拖动的方向以及侧滑的方向的 */ @Override public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) { //如果是ListView样式的RecyclerView //设置拖拽方向为上下 final int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN; //将方向参数设置进去 return makeMovementFlags(dragFlags, 0); } /** * 当我们拖动item时会回调此方法 */ @Override public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) { //如果两个item不是一个类型的,我们让他不可以拖拽 if (viewHolder.getItemViewType() != target.getItemViewType()) { return false; } //回调adapter中的onItemMove方法 mAdapter.onItemMove(viewHolder.getAdapterPosition(), target.getAdapterPosition()); return true; } /** * 当我们侧滑item时会回调此方法 */ @Override public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) { }
(1)自定义一个类继承实现ItemTouchHelper.Callback接口,实现里面的三个方法,分别是设置拖动与侧滑的方向,拖动时回调的方法,侧滑时回调的方法。
我这里没有进行侧滑的处理,所以将 makeMovementFlags(dragFlags, swipeFlags)中第二个参数设置为0,表示不进行侧滑操作。 (2)如果我们设置了非0的dragFlags ,那么当item被拖拽的时候会不断的回调onMove方法,所以我们需要同时Adapter做出相应的改变,对mItems数据做出交换的操作,因此我们需要一个回调接口来继续回调Adapter中的方法。public interface onMoveAndSwipedListener { boolean onItemMove(int fromPosition , int toPosition);}
我们让TabsAdapter实现此接口,并且重写里面的方法
public class TabsAdapter extends RecyclerView.Adapterimplements onMoveAndSwipedListener { }
重写拖动的方法,其实就是交换集合中指定元素的位置:
@Override public boolean onItemMove(int fromPosition, int toPosition) { //交换mItems数据的位置 Collections.swap(TabLayoutActivity.tabTitle, fromPosition, toPosition); //交换RecyclerView列表中item的位置 notifyItemMoved(fromPosition, toPosition); return true; }
再回到我们的SimpleItemTouchHelperCallback,在构造方法中将实现了onMoveAndSwipedListener接口的TabsAdapter 传进来。然后我们就在onMove()方法里获取当前拖拽的item和已经被拖拽到所处位置的item的ViewHolder,有了这2个ViewHolder,我们就可以拿到对应的position,然后调用传递过来的adapter中的onItemMove方法,这样adapter就会进行改变。
(3)这样recyclerview的拖拽就实现了,我们希望拖拽的Item在拖拽的过程中背景颜色加深,这样就需要继续重写下面两个方法:
/**当状态改变时回调此方法*/ @Override public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) { if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) { viewHolder.itemView.setBackgroundColor(Color.LTGRAY); } super.onSelectedChanged(viewHolder, actionState); } /**当用户拖拽完或者侧滑完一个item时回调此方法,用来清除施加在item上的一些状态*/ @Override public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) { super.clearView(recyclerView, viewHolder); viewHolder.itemView.setBackgroundColor(0); }
(4)我们要是希望拖动右侧图标也可以进行拖拽,那么我们需要实现ItemTouchHelper的StartDrag(ViewHolder viewHolder)方法。
现在问题就是取得按住图标时对应的viewholder就行,其实这个实现方法和获取点击item的position一样,接口回调即可实现。tabViewHolder.imgs.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { //如果按下 if (MotionEventCompat.getActionMasked(event) == MotionEvent.ACTION_DOWN) { //回调RecyclerListFragment中的startDrag方法 //让mItemTouchHelper执行拖拽操作 dragListener.startDrag(holder); } return false; } });
拿到holder以后,让fragment实现这个接口里的方法,进行操作即可。
5.fragment的相关代码:
public class TabLayoutFragment extends Fragment implements TabsAdapter.onStartDragListener { }
实现拖动图标时候拖拽事件的接口
//关联ItemTouchHelper和RecyclerView ItemTouchHelper.Callback callback = new SimpleItemTouchHelperCallback(adapter); mItemTouchHelper = new ItemTouchHelper(callback); mItemTouchHelper.attachToRecyclerView(recyclerview);
然后就是关联我们的ItemTouchHelper和RecyclerView,并且记得在实现方法里面加上操作:
@Override public void startDrag(RecyclerView.ViewHolder viewHolder) { mItemTouchHelper.startDrag(viewHolder); }
6.activity的相应代码:
为了演示效果,我这里使用的是一个静态集合,所以在fragment排序以后,数据顺序已经改变,我要做的就是在点击checkbox的时候,通知activity的tablayout的标题进行改变。adapter.setTabTitle(tabTitle); tab.setupWithViewPager(viewpager);
其实就是重新设置了一下标题,并进行关联。可以看到,还是达到了预期效果。
过几天试着模仿类似网易和微博那种tabs的添加,删除,排序等效果,今天就到这里,收工~~~