最近项目中有一个需求,需要有一个列表内容管理,实时显示内容的同时,可以让item进行移动,并根据内容数量不同,进行不同布局展示,这个需求,一开始是想自己用自定义布局管理及自定义view实现的,无奈自己技术不够。后来,突发奇想,能不能使用recycleview实现,网上查了一下,发现recycleview自带布局Item管理,使用ItemToucherHelper即可轻松实现,不得不说,recycleview真的是比listview强太多了
根据item数量的不同,需要实现如下的布局
下面是实现的效果图
首先,先来讲一下ItemTouchHelper的使用,惯例,先上代码
private ItemTouchHelper itemTouchHelper = new ItemTouchHelper(new ItemTouchHelper.Callback() {
@Override
public int getMovementFlags(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
if (recyclerView.getLayoutManager() instanceof GridLayoutManager) {
final int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN |
ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
final int swipeFlags = 0;
return makeMovementFlags(dragFlags, swipeFlags);
} else {
final int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
final int swipeFlags = 0;
return makeMovementFlags(dragFlags, swipeFlags);
}
}
@Override
public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target) {
//得到当拖拽的viewHolder的Position
int fromPosition = viewHolder.getAdapterPosition();
//拿到当前拖拽到的item的viewHolder
int toPosition = target.getAdapterPosition();
itemMove(fromPosition,toPosition);
return true;
}
@Override
public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int i) {
}
});
itemTouchHelper.attachToRecyclerView(binding.rvControl);
private void itemMove(int fromPosition, int toPosition){
if (fromPosition < toPosition) {
for (int i = fromPosition; i < toPosition; i++) {
Collections.swap(showInstances, i, i + 1);
}
} else {
for (int i = fromPosition; i > toPosition; i--) {
Collections.swap(showInstances, i, i - 1);
}
}
showAdapter.notifyItemMoved(fromPosition, toPosition);
}
这里主要是看两个方法,getMovementFlags,这个方法主要用来控制Recycleview中Item具体可以移动的方向,而onMove方法是具体的Item移动后回调的方法,而回调中主要是先更新数据列表,然后调用recycleview中adapter的notifyItemMoved方法,用来通知item移动,而调用attachToRecyclerView,即可将itemTouchHelper和RecycleView绑定起来
接下来看如何设置RecycleView的布局,使其显示为上图的样子
首先,因为前四个item是按照列表形式布局的,因此,可以确定该recycleview的基础布局管理器为gridLayoutManager,而需要让第5个item占两行,则需要调用到layoutManager的setSpanSizeLookup方法
private void notifyShowAdapter(){
ControlLayoutManager gridLayoutManager ;
if (showInstances.size() < 3){
gridLayoutManager = new ControlLayoutManager(getContext(),1,showInstances.size());
}else if (showInstances.size() == 3){
gridLayoutManager = new ControlLayoutManager(getContext(),2,showInstances.size());
gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
@Override
public int getSpanSize(int position) {
if (position == 2)//判断条件,如大号item或者小号item
return 2;
return 1;
}
});
}else if (showInstances.size() == 4){
gridLayoutManager = new ControlLayoutManager(getContext(),2,showInstances.size());
gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
@Override
public int getSpanSize(int position) {
if (position == 2)//判断条件,如大号item或者小号item
return 2;
return 1;
}
});
}else {
gridLayoutManager = new ControlLayoutManager(getContext(),2,showInstances.size());
}
if (lastShowSize != showInstances.size()){//有增加或减少才更新
gridLayoutManager.setOrientation(GridLayoutManager.HORIZONTAL);
controlFragmentDB.setControlManager(gridLayoutManager);
showAdapter.setItemInstanceListDBS(showInstances);
showAdapter.notifyDataSetChanged();
}
lastShowSize = showInstances.size();
gridLayoutManager.setItemMove(false);
}
代码中针对3个item和5个item分别进行了处理
另外,因为item数量不同,item的大小也是不同的,所以需要在layoutManger中进行处理
public void addView(View child) {
super.addView(child);
logUtils.i("addView");
rvHeight = getHeight();//记录高度,不然onItemsMoved获取错误
LayoutParams layoutParams = (LayoutParams) child.getLayoutParams();
switch (totalSize){
case 1:
layoutParams.width = getWidth() - UIUtils.dptoPx(20);
layoutParams.height = getHeight() - UIUtils.dptoPx(20);
layoutParams.leftMargin = UIUtils.dptoPx(10);
layoutParams.rightMargin = UIUtils.dptoPx(10);
layoutParams.topMargin = UIUtils.dptoPx(10);
layoutParams.bottomMargin = UIUtils.dptoPx(10);
child.setLayoutParams(layoutParams);
break;
case 2:
layoutParams.width = (getWidth() - UIUtils.dptoPx(40)) /2 ;
layoutParams.height = getHeight() - UIUtils.dptoPx(20);
layoutParams.leftMargin = UIUtils.dptoPx(10);
layoutParams.rightMargin = UIUtils.dptoPx(10);
layoutParams.topMargin = UIUtils.dptoPx(10);
layoutParams.bottomMargin = UIUtils.dptoPx(10);
child.setLayoutParams(layoutParams);
break;
case 3:
layoutParams.width = (getWidth() - UIUtils.dptoPx(40)) /2 ;
if (getChildCount() == 3){
layoutParams.height = getHeight() - UIUtils.dptoPx(20);
} else if (!isItemMove){//移动布局时,不能修改普通item高度
layoutParams.height = (getHeight() - UIUtils.dptoPx(40)) /2;
}
layoutParams.leftMargin = UIUtils.dptoPx(10);
layoutParams.rightMargin = UIUtils.dptoPx(10);
layoutParams.topMargin = UIUtils.dptoPx(10);
layoutParams.bottomMargin = UIUtils.dptoPx(10);
child.setLayoutParams(layoutParams);
break;
case 5:
layoutParams.width = (getWidth() - UIUtils.dptoPx(30)) /3 ;
if (getChildCount() == 5){
layoutParams.height = getHeight() - UIUtils.dptoPx(20);
}else if (!isItemMove){//移动布局时,不能修改普通item高度
layoutParams.height = (getHeight() - UIUtils.dptoPx(40)) /2;
}
layoutParams.leftMargin = UIUtils.dptoPx(5);
layoutParams.rightMargin = UIUtils.dptoPx(5);
layoutParams.topMargin = UIUtils.dptoPx(10);
layoutParams.bottomMargin = UIUtils.dptoPx(10);
child.setLayoutParams(layoutParams);
break;
default:
layoutParams.width = (getWidth() - UIUtils.dptoPx(40)) /2 ;
layoutParams.height = (getHeight() - UIUtils.dptoPx(40)) /2;
layoutParams.leftMargin = UIUtils.dptoPx(10);
layoutParams.rightMargin = UIUtils.dptoPx(10);
layoutParams.topMargin = UIUtils.dptoPx(10);
layoutParams.bottomMargin = UIUtils.dptoPx(10);
child.setLayoutParams(layoutParams);
break;
}
}
在addView方法中,进行child的LayoutParams重新设置,此处需要注意,因为移动布局时,同样会调用该方法,而3个布局和5个布局的时候,最后一个item大小需要重新设置,所以需要做特殊处理
最后一步,3个Item和5个Item的布局,需要在移动布局的同时,更新布局大小,因此,需要在LayouManger中对onItemsMoved进行处理
public void onItemsMoved(RecyclerView recyclerView, int from, int to, int itemCount) {
super.onItemsMoved(recyclerView, from, to, itemCount);
//3个布局和5个布局的时候需要做特殊处理
isItemMove = true;//告知现在是移动布局
if ((totalSize == 5 && to == 4) || (totalSize == 3 && to == 2)){
View view = recyclerView.getChildAt(from);
LayoutParams layoutParams = (LayoutParams) view.getLayoutParams();
layoutParams.height = rvHeight - UIUtils.dptoPx(20);
view.setLayoutParams(layoutParams);
View view1 = recyclerView.getChildAt(to);
LayoutParams layoutParams1 = (LayoutParams) view1.getLayoutParams();
layoutParams1.height = (rvHeight - UIUtils.dptoPx(40)) /2;
view1.setLayoutParams(layoutParams1);
}else if ((totalSize == 5 && from == 4)|| (totalSize == 3 && from == 2)){
View view = recyclerView.getChildAt(from);
LayoutParams layoutParams = (LayoutParams) view.getLayoutParams();
layoutParams.height = (rvHeight - UIUtils.dptoPx(40)) /2;
view.setLayoutParams(layoutParams);
int index;
if (from == 4){
index = 3;
}else {
index = 1;
}
View view1 = recyclerView.getChildAt(index);
LayoutParams layoutParams1 = (LayoutParams) view1.getLayoutParams();
layoutParams1.height = rvHeight - UIUtils.dptoPx(20);
view1.setLayoutParams(layoutParams1);
}
}
这样基本就能实现上述需求了,这个需求真的是花了我好长的时间,之前一直都不知道recycleview还有itemTouchHelper这种黑科技,如果代码中有写的不对的地方,欢迎大家多多指教