MultiItem擴展 仿任務面板 跨多個RecyclerView的Item拖動 支持縮小後拖動

本文是MultiItem系列的擴展文章,跨RecyclerView的Item拖動,並支持縮放的功能,主要防辦公軟件的面板,本功能的實現大量參考了ItemTouchHelper的源碼。
MutliItem主要解決多類型RecyclerView Adapter問題,在正常使用中作到了Adapter零編碼,解放了複雜的Adapter類,提升擴展性。java

源碼地址

Github地址:github.com/free46000/M…,請你們多多關注,更多更新會首先在GitHub上體現,也會在第一時間在本平臺發佈。 git

系列文章

效果截圖

跨Recycler拖動

縮放後跨Recycler拖動

用法

拖動功能

拖動功能使用比較簡單,拖動功能相關流程和控制都會回調到ItemDragListener監聽中。
實例化拖動輔助類,設置監聽,並在dispatchTouchEvent中調用dragHelper.onTouch(ev)方法github

@Override
protected void onCreate(Bundle savedInstanceState) {
    ...
    //ItemDragHelper,須要傳入外層的橫向滾動的RecyclerView
    dragHelper = new ItemDragHelper(horizontalRecycler);
    //爲dragHelper設置拖動監聽,基本都有默認實現,可根據具體業務繼承重寫方法
    dragHelper.setOnItemDragListener(new OnBaseDragListener());
}

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    //須要保證在Activity或者外層的ViewGroup中重寫此方法
    //調用dragHelper.onTouch():true消耗掉事件;false則執行super
    return dragHelper.onTouch(ev) || super.dispatchTouchEvent(ev);
}複製代碼

爲垂直列表註冊數據源,必須實現ItemData接口ide

//註冊的數據源(TextDragBean)類,必須實現`ItemData`接口
baseItemAdapter.register(TextDragBean.class, new TextViewDragManager());複製代碼

開啓拖動功能佈局

dragHelper.startDrag(viewHolder);複製代碼

拖動監聽,此處只複寫了拖動結束回調的接口post

class OnBaseDragListener extends OnItemDragListener {

    @Override
    public void onDragFinish(RecyclerView recyclerView, int itemRecyclerPos, int itemPos) {
        super.onDragFinish(recyclerView, itemRecyclerPos, itemPos);
        String text = String.format("拖動起始第%s個列表的第%s項 結束第%s個列表的第%s項 \n\n拖動數據:%s", originalRecyclerPosition,
                originalItemPosition, itemRecyclerPos, itemPos, dragItemData);
        Toast.makeText(PanelActivity.this, text, Toast.LENGTH_SHORT).show();
    }
}複製代碼

通用Item拖動控制功能

主要包括對Item的拖動、移動、切換的控制功能,數據源須要實現ItemDrag接口,這樣在拖動過程當中OnItemDragListener會根據不一樣的狀態進行對應的控制。
固然你也能夠經過OnBaseDragListener的不一樣回調方法,根據業務實現定製化的控制,這裏咱們看下ItemDrag接口:動畫

/** * 數據源Item拖動接口,實現一些move change等定製化的通用控制 * Created by free46000 on 2017/4/3. */
public interface ItemDrag {
    /** * 是否能夠在本身的Recycler中move * * @return boolean */
    boolean isCanMove();

    /** * 是否能夠切換到其餘Recycler * * @return boolean */
    boolean isCanChangeRecycler();

    /** * 是否能夠開啓拖動 * * @return boolean */
    boolean isCanDrag();
}複製代碼

雙擊縮小功能

實例化輔助類併爲輔助類設置須要的視圖對象this

//實例化縮放功能輔助類
scaleHelper = new ViewScaleHelper();
//設置最外層的Content視圖
scaleHelper.setContentView(contentView);
//設置橫向的Recycler列表視圖
scaleHelper.setHorizontalView(horizontalRecycler);複製代碼

添加外層的垂直視圖到輔助類,進行縮放統一管理編碼

scaleHelper.addVerticalView(verticalView);複製代碼

OnItemDragListener的回調中返回當前縮放級別,配合完成縮放後的拖動功能spa

class OnBaseDragListener extends OnItemDragListener {
    @Override
    public float getScale() {
        return scaleHelper.isInScaleMode() ? scaleHelper.getScale() : super.getScale();
    }
}複製代碼

開啓或關閉縮放功能

//開啓或關閉縮放模式
scaleHelper.toggleScaleModel();
//開啓縮放模式
scaleHelper.startScaleModel();
//關閉縮放模式
scaleHelper.stopScaleModel();複製代碼

其餘定製化用法

定製化業務主要經過複寫ItemDragListener的相關方法實現,下面簡單列舉一些可定製的功能:

  • getScale() 縮放比例詳見ViewScaleHelper#getScale()
  • Item和列表選中移動等流程控制相關方法(包含移動前的確認回調),詳見源碼及註釋
  • getHorizontalScrollMaxSpeed getVerticalScrollMaxSpeed 最大水平垂直滾動速度
  • getHorizontalLimit getVerticalLimit 計算水平垂直滾動距離
  • calcHorizontalScrollDistance calcVerticalScrollDistance 最大水平垂直滾動速度
  • onDrawFloatView(View floatView) 浮動視圖動畫處理
  • getMoveLimit()兩個Item是否進行move邊界值

上面所介紹定製化用法並非所有,若有須要更深層次定製能夠提交issues或留言

主要流程解析

備註:如下所說觸摸位置都爲相對屏幕位置,這樣方便後續計算

生成浮動視圖

被拖動Item視圖設爲不可見狀態,浮動視圖採用WindowManager + ImageView展現被拖動Item視圖的Bitmap

計算橫向和豎向的RecyclerView滾動

  • 大量參考了ItemTouchHelper的源碼
  • 根據用戶觸摸位置計算是否須要滾動,和滾動的方向與距離 詳見ItemDragListenercalcXXXScrollDistance() calcScrollXXXDirect()
  • 採用定時Runnable形式,保證持續的滾動
  • 滾動時調用Item位置計算方法,使得在滾動過程當中也能夠更換Item位置

Item位置更換計算

  • 根據觸摸位置horizontalRecycler.findChildViewUnder(x, y)找到垂直recyclerView的位置,若找到位置繼續
  • 根據上一次垂直recyclerView所在的位置,判斷是否爲第一次選中或者是切換recyclerView的操做,此處可經過itemDragListener回調攔截這次操做的結果
  • 若是須要切換recyclerView的位置,此時須要對被拖動的Item進行remove,並在新的recyclerViewadd進去
  • 根據觸摸位置recyclerView.findChildViewUnder(itemX, itemY)找到itemView的位置
  • 根據上一次itemView所在的位置,判斷是否須要移動itemView位置的操做,此處可經過itemDragListener回調攔截這次操做的結果
  • 若是須要移動動itemView位置則須要把recyclerView滾動到合適的位置,防recyclerView亂跳

視圖縮放

視圖縮放採用的是把外層的視圖還有橫向列表視圖擴大,並對橫向列表視圖進行setScaleX setScaleY的操做,而後對縮放後的視圖寬度進行賦值,防止一些充滿屏幕的佈局,影響縮放效果,等中止縮放的時候還原便可。

總結

這個功能實現起來比較倉促,並無過多的考慮,算是一個beta版本。以上整理了用法,並對主要流程作了簡單的解析,若是你們有興趣能夠結合源碼理解原理,若有不明白,或者實現不夠優雅的地方,歡迎你們指出。

相關文章
相關標籤/搜索