Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 【Android】一步集成側滑(刪除)菜單

【Android】一步集成側滑(刪除)菜單

編輯:關於Android編程

代碼傳送門:喜歡的話,隨手點個star。多謝
https://github.com/mcxtzhang/SwipeDelMenuLayout

重要的話 開頭說,not for the RecyclerView or ListView, for the Any ViewGroup.

本控件不依賴任何父布局,不是針對 RecyclerView、ListView,而是任意的ViewGroup裡的childView都可以使用側滑(刪除)菜單。

概述

本控件從撸出來在項目使用至今已經過去7個月,距離第一次將它push至github上,也已經2月+。

期間有很多朋友在評論、issue裡提出了一些改進意見,例如支持設置滑動方向(左右)、高仿QQ的交互、支持GridLayoutManager等,以及一些bug。已經被我全部實、修復。並且將其打包至jitpack,引入更方便。和第一版相比,改動挺多的。故將其整理,新發一版。

那麼本文先從如何使用它講起,然後介紹它包含的特性、支持的屬性。最後就幾個難點和沖突的解決進行講解。

代碼傳送門:喜歡的話,隨手點個star。多謝
https://github.com/mcxtzhang/SwipeDelMenuLayout

先上四個gif給各位看官感受一下最新版的魅力(以下版本都順便展示了可選的雙向滑動)

Android Special Version (無阻塞式,側滑菜單展開時,依然可以展開其他側滑菜單,同時上一個菜單會自動關閉):

\

GridLayoutManager (和上圖的代碼比,只需修改RecyclerView的LayoutManager。):

LinearLayout (不需任何修改,連LinearLayout也可以簡單的實現側滑菜單):

iOS interaction (阻塞式交互,高仿QQ,側滑菜單展開式,屏蔽其他ITEM所有操作):<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwPjxpbWcgYWx0PQ=="" src="/uploadfile/Collfiles/20161115/201611150954101350.gif" title="\" />

使用:

Step 1. 在項目根build.gradle文件中增加JitPack倉庫依賴。

    allprojects {
        repositories {
            ...
            maven { url "https://jitpack.io" }
        }
    }

Step 2. Add the dependency

    dependencies {
             compile 'com.github.mcxtzhang:SwipeDelMenuLayout:V1.2.1'
    }

Step 3. 在需要側滑刪除的ContentItem外面套上本控件,在本控件內依次排列ContentItem、菜單即可:
至此 您就可以使用高仿IOS、QQ 側滑刪除菜單功能了
(側滑菜單的點擊事件等是通過設置的id取到,與其他控件一致,不再贅述)

Demo裡,我的ContentItem是一個TextView,那麼我就在其外嵌套本控件,並且以側滑菜單出現的順序,依次排列菜單控件即可。




    

    

支持屬性:

1 通過 isIos 變量控制是否是IOS阻塞式交互,默認是打開的。
2 通過 isSwipeEnable 變量控制是否開啟右滑菜單,默認打開。(某些場景,復用item,沒有編輯權限的用戶不能右滑)
3 通過開關 isLeftSwipe支持左滑右滑

有兩種方式設置:
一:xml:

二: java代碼:

//這句話關掉IOS阻塞式交互效果 並依次打開左滑右滑  禁用掉側滑菜單
((SwipeMenuLayout) holder.itemView).setIos(false).setLeftSwipe(position % 2 == 0 ? true : false).setSwipeEnable(false);

支持特性:

不會同時展開2+個側滑菜單。(可見界面上最多只會出現一個側滑菜單)。 側滑過程中,禁止父控件上下滑動。 多指同時滑動,屏蔽後觸摸的幾根手指。 增加viewChache 的 get()方法,可以用在:當點擊外部空白處時,關閉正在展開的側滑菜單。 以第一個子Item(即ContentItem)的寬度為控件寬度

每次更新的checklist:

由於持續迭代,會發生完成一個feature、fix一個bug後,導致新的bug。
so,整理一份checkList,供每次迭代後驗證,都通過,才會push到github庫上。

項目 備注 驗證 isIos 切換至IOS阻塞交互模式、Android特色無阻塞交互模式 以下feature都可正常工作   isSwipeEnable 是否支持關閉側滑功能   isLeftSwipe 是否支持雙向滑動   ContentItem內容可單擊     ContentItem內容可長按     側滑菜單顯示時,ContentItem不可點擊     側滑菜單顯示時,ContentItem不可長按     側滑菜單顯示時,側滑菜單可以點擊     側滑菜單顯示時,點擊ContentItem區域關閉菜單     側滑過程中,屏蔽長按事件     通過滑動關閉菜單,不應該觸發ContentItem點擊事件    

難點、沖突的解決:

1 ContentItem的長按和本控件側滑的沖突。
一開始我還是老思路,一直都是通過判斷手指起始落點的坐標,判斷手指落點處於何位置,屏蔽一些操作。當本控件功能越來越龐大,計算開始復雜,也容易出錯。但也跌跌撞撞的撐了過來,但就在今天,我想到了另一種思路:通過禁用子View的longClickable屬性來完成這個功能。於是我重構這部分代碼,先利用git 查看之前的提交,去除掉那部分代碼,並
在側滑菜單展開smoothExpand()、關閉smoothClose()的函數中分別加入:

        //2016 11 13 add 側滑菜單展開,屏蔽content長按
        if (null != mContentView) {
            mContentView.setLongClickable(true);
        }
        //2016 11 13 add 側滑菜單展開,屏蔽content長按
        if (null != mContentView) {
            mContentView.setLongClickable(true);
        }

代碼就這麼簡單,幾小時前我才想到這麼簡單的解決方法,這也是促使我新撸一遍文章,記錄本控件的一些改動的緣由之一。

2 如何支持任意父控件
這算是本控件最酷炫的魅力之一吧,我詳細描述了實現過程。
總結起來,我利用了一個static變量,保存了當前正在展開的View,
在進行各項操作時就可預先判斷,如若會引起沖突,例如界面上出現兩個側滑菜單,
便自動關閉上一個菜單。

代碼如下:

    //存儲的是當前正在展開的View
    private static SwipeMenuLayout mViewCache;

dispatchTouchEvent()的ActionDown裡:

                    //如果down,view和cacheview不一樣,則立馬讓它還原。且把它置為null
                    if (mViewCache != null) {
                        if (mViewCache != this) {
                            mViewCache.smoothClose();
                        }
                        //只要有一個側滑菜單處於打開狀態, 就不給外層布局上下滑動了
                        getParent().requestDisallowInterceptTouchEvent(true);
                    }

在側滑菜單展開smoothExpand()、關閉smoothClose()的函數:

    //展開就加入ViewCache:
    mViewCache = SwipeMenuLayout.this;
    //關閉置為null
    mViewCache = null;

onDetachedFromWindow()裡:

    //每次ViewDetach的時候,判斷一下 ViewCache是不是自己,如果是自己,關閉側滑菜單,且ViewCache設置為null,
    // 理由:1 防止內存洩漏(ViewCache是一個靜態變量)
    // 2 側滑刪除後自己後,這個View被Recycler回收,復用,下一個進入屏幕的View的狀態應該是普通狀態,而不是展開狀態。
    @Override
    protected void onDetachedFromWindow() {
        if (this == mViewCache) {
            mViewCache.smoothClose();
            mViewCache = null;
        }
        super.onDetachedFromWindow();
    }

3 解決多指滑動沖突:
利用一個布爾值 flag,在ActionDown時判斷是否繼續接受觸摸事件:
代碼如下:

    //防止多只手指一起滑我的flag 在每次down裡判斷, touch事件結束清空
    private static boolean isTouching;

dispatchTouchEvent()的ActionDown裡:

    if (isTouching) {//如果有別的指頭摸過了,那麼就return false。這樣後續的move..等事件也不會再來找這個View了。
        return false;
    } else {
        isTouching = true;//第一個摸的指頭,趕緊改變標志,宣誓主權。
    }

ActionUp裡:

    isTouching = false;//沒有手指在摸我了

4 支持GridLayoutManager
畢竟項目中在網格布局中使用側滑菜單還屬少數,所以一開始我將場景簡單化,給本控件設置的寬度都是父控件的寬度-padding。後來有童鞋提出希望支持網格布局,一開始我思路也走了彎路,我還想著構建一個MatchParent的MeasureSpec,然後傳給父控件(GridView、RecyclerView)用於測量呢。
正確思路是,取第一個子View,即ContentView的寬度用作本控件的寬度即可,這樣在layout側滑菜單時,自然而然將側滑菜單layout在了不可見的區域,只有通過滑動才能顯示它。
代碼也沒啥好說的:
onMeasure()中:


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //2016 11 09 add,適配GridLayoutManager,將以第一個子Item(即ContentItem)的寬度為控件寬度
        int contentWidth = 0;
        int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            View childView = getChildAt(i);
            if (childView.getVisibility() != GONE) {
                measureChildWithMargins(childView, widthMeasureSpec, 0, heightMeasureSpec, 0);
                final MarginLayoutParams lp = (MarginLayoutParams) childView.getLayoutParams();
                mHeight = Math.max(mHeight, childView.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
                if (i > 0) {//第一個布局是Left item,從第二個開始才是RightMenu
                    mRightMenuWidths += childView.getMeasuredWidth();
                } else {
                    contentWidth = childView.getMeasuredWidth();
                }
            }
        }
        setMeasuredDimension(contentWidth, mHeight);//寬度取第一個Item(Content)的寬度
    }

onLayout()中,順序layoutchildView即可。

總結

以上是本控件近期or大家感興趣的一些點的詳解,更詳細的講解可去上篇文章觀看,或者去github上下載源碼浏覽。
代碼傳送門:喜歡的話,隨手點個star。多謝
https://github.com/mcxtzhang/SwipeDelMenuLayout

大家使用中如有問題,多多反饋。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved