上一篇博客用TabLayout实现了类似CSDN客户端首页选项卡的滑动效果:
仿CSDN客户端首页(一)—-TabLayout实现选项卡滑动效果
然后发现了CSDN首页拖拽排序效果,先上自己完成的效果图:
这里是在之前的基础上进行修改的:点击右侧的箭头,出现Tabs的列表,按住Item右侧图标,可以进行上下拖动,进行排序,排序以后再次点击箭头,首页Tabs顺序也会改变;点击列表的某一个Item,会跳转到对应的界面。整体上效果还是和CSDN客户端很像的。
OK,一起看看怎么实现的吧。
1.界面布局:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
app:tabTextAppearance="@style/MyTabLayoutTextAppearanceInverse"
tools:context="com.example.tangyangkai.testdemo.TabLayoutActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_marginRight="5dp"
android:orientation="horizontal"
android:weightSum="11">
<android.support.design.widget.TabLayout
android:id="@+id/tab"
android:layout_width="0dp"
android:layout_height="50dp"
android:layout_weight="10"></android.support.design.widget.TabLayout>
<View
android:layout_width="1dp"
android:layout_height="match_parent"
android:layout_marginLeft="5dp"
android:layout_toLeftOf="@+id/tab_check"
android:background="@color/font_text" />
<RelativeLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1">
<CheckBox
android:id="@+id/tab_check"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_marginBottom="15dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginTop="15dp"
android:background="@drawable/down"
android:button="@null"
android:checked="false" />
</RelativeLayout>
</LinearLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/tabs_ll"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white"
android:orientation="vertical"
android:visibility="gone"></LinearLayout>
<LinearLayout
android:id="@+id/viewpager_ll"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:visibility="visible">
<android.support.v4.view.ViewPager
android:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white" />
</LinearLayout>
</RelativeLayout>
</LinearLayout>
主界面还是延续之前的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,根据传递过来的参数,显示不同的界面。
可参考:从BaseActivity与BaseFragment的封装谈起
当点击checkbox跳转过来的时候,传递一个不一样的参数,显示recyclerview。然后就是适配器的代码。
3.recyclerview的点击事件:
这里的处理我之前有一篇博客说的很详细:
Android接口回调处理item点击事件
这种接口回调的方式本质就是为了拿到被点击item的position,在Fragment获取到position以后,我们需要在Activity里面根据position进行切换界面的操作。activity与fragment的通信,我用的是Eventbus来进行处理的:
Android Fragment与Activity之间的相互通信(二)
@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.Adapter<RecyclerView.ViewHolder> implements 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的添加,删除,排序等效果,今天就到这里,收工~~~