欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android开发在RecyclerView上面实现"拖放"和"滑动删除"-2

程序员文章站 2023-11-26 09:35:10
上篇给大家介绍了android一步步带你在recyclerview上面实现"拖放"和"滑动删除"功能 效果如下: 拖动手柄 在设计一个支持"拖放"的列表时, 通常...

上篇给大家介绍了android一步步带你在recyclerview上面实现"拖放"和"滑动删除"功能

效果如下:

Android开发在RecyclerView上面实现"拖放"和"滑动删除"-2

拖动手柄

在设计一个支持"拖放"的列表时, 通常提供一个在触摸时初始化拖拽的"拖动手柄". 因其可发现性和可用性而被material guidelines所推荐, 尤其是列表处于"可编辑模式"时.

Android开发在RecyclerView上面实现"拖放"和"滑动删除"-2

首先更新item的布局(item_main.xml):

<framelayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:id="@+id/item"
 android:layout_width="match_parent"
 android:layout_height="?listpreferreditemheight"
 android:clickable="true"
 android:focusable="true"
 android:foreground="?selectableitembackground">
 <textview
 android:id="@+id/text"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_gravity="center_vertical"
 android:layout_marginleft="16dp"
 android:textappearance="?android:attr/textappearancemedium" />
 <imageview
 android:id="@+id/handle"
 android:layout_width="?listpreferreditemheight"
 android:layout_height="match_parent"
 android:layout_gravity="center_vertical|right"
 android:scaletype="center"
 android:src="@drawable/ic_reorder_grey_500_24dp" />
</framelayout>

用作"拖放手柄"的图片可以在material design icon找到, 也可以方便地通过android material design icon generator plugin添加.

我们曾经提到过, 可以通过代码itemtouchhelper.startdrag(recyclerview.viewholder)来开启拖动. 所以我们要做的就是更新viewholder来包含新的手柄视图, 并设置一个简单的触摸事件接口, 以触发startdrag()方法.

我们需要定义一个接口来传递拖动事件.

public interface onstartdraglistener {
 /**
 * called when a view is requesting a start of a drag.
 *
 * @param viewholder the holder of the view to drag.
 */
 void onstartdrag(recyclerview.viewholder viewholder);
}

然后, 在itemviewholder中实现化手柄视图.

public final imageview handleview;
public itemviewholder(view itemview) {
 super(itemview);
 // ...
 handleview = (imageview) itemview.findviewbyid(r.id.handle);
}

并且更新adapter.

private final onstartdraglistener mdragstartlistener;
public recyclerlistadapter(onstartdraglistener dragstartlistener) {
 mdragstartlistener = dragstartlistener;
 // ...
}
@override
public void onbindviewholder(final itemviewholder holder, 
 int position) {
 // ...
 holder.handleview.setontouchlistener(new ontouchlistener() {
 @override
 public boolean ontouch(view v, motionevent event) {
 if (motioneventcompat.getactionmasked(event) == 
  motionevent.action_down) {
 mdragstartlistener.onstartdrag(holder);
 }
 return false;
 }
 });
}

完整的adapter应该看起来像这个.

剩下的是把onstartdraglistener添加到fragment.

public class recyclerlistfragment extends fragment implements 
 onstartdraglistener {
 // ...
 @override
 public void onviewcreated(view view, bundle icicle) {
 super.onviewcreated(view, icicle);
 recyclerlistadapter a = new recyclerlistadapter(this);
 // ...
 }
 @override
 public void onstartdrag(recyclerview.viewholder viewholder) {
 mitemtouchhelper.startdrag(viewholder);
 }
}

但你运行之后, 可以看到这样的效果:

Android开发在RecyclerView上面实现"拖放"和"滑动删除"-2

标示选中视图

在上一篇的基础示例中, 被拖拽的item事实上是被选中的, 但是没有可视化的标示. 由于显著的理由, 这是不受欢迎的, 但也很容易修复. 事实上, 在itemtouchhelper的帮助下, 只要你的viewholder的itemview设置了background集合(selector), 就会得到相应的效果. 在lollipop及之后的版本, item view的elevation在拖拽和滑动期间会增加. 而在之前的版本中, 滑动时会有fade效果. 看起来就像:

Android开发在RecyclerView上面实现"拖放"和"滑动删除"-2

效果看起来不错, 但你可能想要更多的控制. 一种方法是, 无论任何时候viewholder被选中或者清空, 让item自己处理这种改变. 由此, itemtouchhelper.callback提供了两种回调.

onselectedchanged(recyclerview.viewholder, int). 每一次viewholder的状态, 变成drag(action_state_drag)或者swipe(action_state_swipe)时, 该方法就会被调用. 这时候是将viewholder的状态变成active的完美时刻.

clearview(recyclerview, recyclerview.viewholder). 在被拖拽的viewholder放下时, 或者是滑动操作取消或者完成时(action_state_idle), 这里会是将itemview状态设置为idle的最好的地方.

那么, 我们就把两者绑定在一起实现.

首先, 创建一个接口, 让目标viewholder实现:

/**
 * notifies a view holder of relevant callbacks from 
 * {@link itemtouchhelper.callback}.
 */
public interface itemtouchhelperviewholder {
 /**
 * called when the {@link itemtouchhelper} first registers an 
 * item as being moved or swiped.
 * implementations should update the item view to indicate 
 * it's active state.
 */
 void onitemselected();
 /**
 * called when the {@link itemtouchhelper} has completed the 
 * move or swipe, and the active item state should be cleared.
 */
 void onitemclear();
}

接着, 触发相应的回调方法:

@override
public void onselectedchanged(recyclerview.viewholder viewholder, 
 int actionstate) {
 // we only want the active item
 if (actionstate != itemtouchhelper.action_state_idle) {
 if (viewholder instanceof itemtouchhelperviewholder) {
 itemtouchhelperviewholder itemviewholder = 
  (itemtouchhelperviewholder) viewholder;
 itemviewholder.onitemselected();
 }
 }
 super.onselectedchanged(viewholder, actionstate);
}
@override
public void clearview(recyclerview recyclerview, 
 recyclerview.viewholder viewholder) {
 super.clearview(recyclerview, viewholder);

 if (viewholder instanceof itemtouchhelperviewholder) {
 itemtouchhelperviewholder itemviewholder = 
 (itemtouchhelperviewholder) viewholder;
 itemviewholder.onitemclear();
 }
}

现在, 剩下的是让itemviewholder实现itemtouchhelperviewholder:

public class itemviewholder extends recyclerview.viewholder 
 implements itemtouchhelperviewholder {
 // ...
 @override
 public void onitemselected() {
 itemview.setbackgroundcolor(color.ltgray);
 }
 @override
 public void onitemclear() {
 itemview.setbackgroundcolor(0);
 }
}

考虑到这仅仅是一个示例, 我们仅仅是在item处于active状态时设置了灰色背景, 当item被清空时, 把灰色背景删除了. 如果你的adapter和itemtouchhelper紧密结对的话, 轻易地放弃这个设置也行, 转而可以直接在itemtouchhelper.callback转变item的状态.

grid布局

如果你想在本项目中转而使用gridlayoutmanager, 你会发现并没有正常地起作用. 原因以及修复原因都很简单: 必须告诉itemtouchhelper我们想要支持左右拖拽. 在simpletouchhelpercallback中, 我们已经声明:

@override
public int getmovementflags(recyclerview recyclerview, 
 recyclerview.viewholder viewholder) {
 int dragflags = itemtouchhelper.up | itemtouchhelper.down;
 int swipeflags = itemtouchhelper.start | itemtouchhelper.end;
 return makemovementflags(dragflags, swipeflags);
}

支持grid布局所要求的唯一改变是dragflags中添加左右方向:

int dragflags = itemtouchhelper.up | itemtouchhelper.down | 
 itemtouchhelper.left | itemtouchhelper.right;

但是, 对于grid而言, "滑动删除"并不是一个天然的功能模式, 所以你也许会写一些如下的代码:

@override
public int getmovementflags(recyclerview recyclerview, 
 recyclerview.viewholder viewholder) {
 int dragflags = itemtouchhelper.up | itemtouchhelper.down | 
  itemtouchhelper.left | itemtouchhelper.right;
 int swipeflags = 0;
 return makemovementflags(dragflags, swipeflags);
}

grid上面的上下左右"拖放"效果看起来如下:

Android开发在RecyclerView上面实现"拖放"和"滑动删除"-2

自定义滑动动画

对我们而言, 在viewholder拖拽和滑动的时候, itemtouchhelper.callback为我们提供了一个十分方便的方式来完全控制viewholder的动画. 因为itemtouchhelper是一个itemdecoration, 我们可以用类似的方式, 详细地了解视图的绘制(hook into the view drawing).

下面是一个简单的例子, 来覆盖默认的滑动动画, 来展示一个线性的fade效果.

@override
public void onchilddraw(canvas c, recyclerview recyclerview, 
 viewholder viewholder, float dx, float dy, 
 int actionstate, boolean iscurrentlyactive) {
 if (actionstate == itemtouchhelper.action_state_swipe) {
 float width = (float) viewholder.itemview.getwidth();
 float alpha = 1.0f - math.abs(dx) / width;
 viewholder.itemview.setalpha(alpha);
 viewholder.itemview.settranslationx(dx); 
 } else {
 super.onchilddraw(c, recyclerview, viewholder, dx, dy, 
  actionstate, iscurrentlyactive);
 }
}

参数dx和dy代表选中视图的当前位置,

  • -1.0f表示完完全全的从itemtouchhelper.end到itemtouchhelper.start的滑动.
  • 1.0f表示完完全全的从itemtouchhelper.start到itemtouchhelper.end的滑动.

 对于任何没有处理的actionstate你都可以调用super的方法, 从而使用默认的动画.

总结

我们仅仅了解了自定义itemtouchhelper所能做的事情中有趣的部分, 我希望能够在一篇文章中包含更多内容, 但是考虑到文章长度, 还是分开比较好.

以上所述是小编给大家介绍的android开发在recyclerview上面实现"拖放"和"滑动删除功能(二),希望对大家有所帮助