仿任务面板 跨多个RecyclerView的Item拖动 支持缩小后拖动


640?wx_fmt=other 点击关注了解更多精彩内容!!


640?wx_fmt=png

魏子翔,2017年加入去哪儿网技术团队,目前在平台事业部/大前端技术中心,主要负责Android平台的基础框架和跨端混合应用方向研发。关注前沿技术,热爱分享,拥抱开源。


相信看完优雅的实现多类型 RecyclerView Adapter 这篇文章后,大家对本库基本用法已经有了初步认识,本文主要讲解跨 RecyclerView 的 Item 拖动,并支持缩放的功能,主要是防办公软件的面板,本功能的实现大量参考了系统的 ItemTouchHelper 的源码。 Github地址:MultiItem。

效果截图

640?wx_fmt=gif

640?wx_fmt=gif

用法

拖动功能

拖动功能使用比较简单,拖动功能相关流程和控制都会回调到 ItemDragListener 监听中。 实例化拖动辅助类,设置监听,并在DispatchTouchEvent 中调用 DragHelper. onTouch(ev) 方法。


 
  1. @Override

  2. protected void onCreate(Bundle savedInstanceState) {

  3.    ...

  4.    //ItemDragHelper,需要传入外层的横向滚动的RecyclerView

  5.    dragHelper = new ItemDragHelper(horizontalRecycler);

  6.    //为dragHelper设置拖动监听,基本都有默认实现,可根据具体业务继承重写方法

  7.    dragHelper.setOnItemDragListener(new OnBaseDragListener());

  8. }

  9. @Override

  10. public boolean dispatchTouchEvent(MotionEvent ev) {

  11.    //需要保证在Activity或者外层的ViewGroup中重写此方法

  12.    //调用dragHelper.onTouch():true消耗掉事件;false则执行super

  13.    return dragHelper.onTouch(ev) || super.dispatchTouchEvent(ev);

  14. }

为垂直列表注册数据源,必须实现 ItemData 接口。


 
  1. //注册的数据源(TextDragBean)类,必须实现`ItemData`接口

  2. baseItemAdapter.register(TextDragBean.class, new TextViewDragManager());

开启拖动功能。


 
  1. dragHelper.startDrag(viewHolder);

拖动监听,此处只复写了拖动结束回调的接口。


 
  1. class OnBaseDragListener extends OnItemDragListener {

  2.    @Override

  3.    public void onDragFinish(RecyclerView recyclerView, int itemRecyclerPos, int itemPos) {

  4.        super.onDragFinish(recyclerView, itemRecyclerPos, itemPos);

  5.        String text = String.format("拖动起始第%s个列表的第%s项 结束第%s个列表的第%s项 \n\n拖动数据:%s", originalRecyclerPosition,

  6.                originalItemPosition, itemRecyclerPos, itemPos, dragItemData);

  7.        Toast.makeText(PanelActivity.this, text, Toast.LENGTH_SHORT).show();

  8.    }

  9. }

通用Item拖动控制功能

主要包括对 Item 的拖动、移动、切换的控制功能,数据源需要实现 ItemDrag 接口,这样在拖动过程中 OnItemDragListener 会根据不同的状态进行对应的控制。 当然你也可以通过 OnBaseDragListener 的不同回调方法,根据业务实现定制化的控制,这里我们看下 ItemDrag 接口:


 
  1. /**

  2. * 数据源Item拖动接口,实现一些move change等定制化的通用控制

  3. * Created by free46000 on 2017/4/3.

  4. */

  5. public interface ItemDrag {

  6.    /**

  7.     * 是否可以在自己的Recycler中move

  8.     *

  9.     * @return boolean

  10.     */

  11.    boolean isCanMove();

  12.    /**

  13.     * 是否可以切换到其他Recycler

  14.     *

  15.     * @return boolean

  16.     */

  17.    boolean isCanChangeRecycler();

  18.    /**

  19.     * 是否可以开启拖动

  20.     *

  21.     * @return boolean

  22.     */

  23.    boolean isCanDrag();

  24. }

双击缩小功能

实例化辅助类并为辅助类设置需要的视图对象。


 
  1. //实例化缩放功能辅助类

  2. scaleHelper = new ViewScaleHelper();

  3. //设置最外层的Content视图

  4. scaleHelper.setContentView(contentView);

  5. //设置横向的Recycler列表视图

  6. scaleHelper.setHorizontalView(horizontalRecycler);

添加外层的垂直视图到辅助类,进行缩放统一管理。


 
  1. scaleHelper.addVerticalView(verticalView);

在 OnItemDragListener 的回调中返回当前缩放级别,配合完成缩放后的拖动功能。


 
  1. class OnBaseDragListener extends OnItemDragListener {

  2.    @Override

  3.    public float getScale() {

  4.        return scaleHelper.isInScaleMode() ? scaleHelper.getScale() : super.getScale();

  5.    }

  6. }

开启或关闭缩放功能。


 
  1. //开启或关闭缩放模式

  2. scaleHelper.toggleScaleModel();

  3. //开启缩放模式

  4. scaleHelper.startScaleModel();

  5. //关闭缩放模式

  6. scaleHelper.stopScaleModel();

其他定制化用法

定制化业务主要通过复写 ItemDragListener 的相关方法实现,下面简单列举一些可定制的功能: getScale() 缩放比例详见 ViewScaleHelper# getScale()。 Item 和列表选中移动等流程控制相关方法(包含移动前的确认回调),详见源码及注释。 getHorizontalScrollMaxSpeedgetVerticalScrollMaxSpeed 最大水平垂直滚动速度。 getHorizontalLimit getVerticalLimit 计算水平垂直滚动距离。 calcHorizontalScrollDistancecalcVerticalScrollDistance 最大水平垂直滚动速度。 onDrawFloatView(View floatView) 浮动视图动画处理。 getMoveLimit() 两个 Item 是否进行 move 边界值。 上面所介绍定制化用法并不是全部,如有需要更深层次定制可以提交 Issues 或留言。

主要流程解析

备注:以下所说触摸位置都为相对屏幕位置,这样方便后续计算。

生成浮动视图

被拖动 Item 视图设为不可见状态,浮动视图采用 WindowManager + ImageView 展示被拖动 Item 视图的 Bitmap。

计算横向和竖向的RecyclerView滚动

大量参考了 ItemTouchHelper 的源码; 根据用户触摸位置计算是否需要滚动,和滚动的方向与距离。详见 ItemDragListener的CalcXXXScrollDistance() CalcScrollXXX Direct(); 采用定时 Runnable 形式,保证持续的滚动; 滚动时调用 Item 位置计算方法,使得在滚动过程中也可以更换 Item 位置。

Item位置更换计算

根据触摸位置 HorizontalRecycler.findChild ViewUnder(x, y) 找到垂直 RecyclerView 的位置,若找到位置继续; 根据上一次垂直 RecyclerView 所在的位置,判断是否为第一次选中或者是切换 RecyclerView 的操作,此处可通过 ItemDragListener 回调拦截此次操作的结果; 如果需要切换 RecyclerView 的位置,此时需要对被拖动的 Item 进行 Remove,并在新的 RecyclerView 中 Add 进去; 根据触摸位置 RecyclerView.findChildView Under(itemX, itemY) 找到 ItemView 的位置; 根据上一次 ItemView 所在的位置,判断是否需要移动 ItemView 位置的操作,此处可通过 ItemDragListener 回调拦截此次操作的结果; 如果需要移动 ItemView 位置则需要把 RecyclerView 滚动到合适的位置,防 RecyclerView 乱跳。

视图缩放

视图缩放采用的是把外层的视图还有横向列表视图扩大,并对横向列表视图进行 SetScaleX SetScaleY 的操作,然后对缩放后的视图宽度进行赋值,防止一些充满屏幕的布局,影响缩放效果,等停止缩放的时候还原即可。

总结

这个功能实现起来比较仓促,并没有过多的考虑,算是一个 Beta 版本。 以上整理了用法,并对主要流程做了简单的解析,如果大家有兴趣可以结合源码理解原理,如有不明白,或者实现不够优雅的地方,欢迎大家指出。

640?wx_fmt=jpeg

640?wx_fmt=jpeg

©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页