当前位置: 移动技术网 > IT编程>移动开发>Android > Android开发在RecyclerView上面实现"拖放"和"滑动删除"-2

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

2019年07月24日  | 移动技术网IT编程  | 我要评论

51mp3ring,4001867606,城市上上签

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

效果如下:

拖动手柄

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

首先更新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);
 }
}

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

标示选中视图

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

效果看起来不错, 但你可能想要更多的控制. 一种方法是, 无论任何时候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上面的上下左右"拖放"效果看起来如下:

自定义滑动动画

对我们而言, 在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上面实现"拖放"和"滑动删除功能(二),希望对大家有所帮助

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网