標籤:script 公眾 lock 屏蔽 value 源碼 win bug 點擊
本篇文章已授權公眾號 guolin_blog (郭霖)獨家發布
轉載請標明出處:
http://blog.csdn.net/zxt0601/article/details/53157090
本文出自:【張旭童的部落格】(http://blog.csdn.net/zxt0601)
代碼傳送門:喜歡的話,隨手點個star。多謝
https://github.com/mcxtzhang/SwipeDelMenuLayout
重要的話 開頭說,not for the RecyclerView or ListView, for the Any ViewGroup.
本控制項不依賴任何父布局,不是針對 RecyclerView、ListView,而是任意的ViewGroup裡的childView都可以使用側滑(刪除)菜單。
簡單,0耦合,支援任意ViewGroup。
概述
本控制項從擼出來在項目使用至今已經過去7個月,距離第一次將它push至github上,也已經2月+。(之前,我發表過一篇文章。傳送門:http://blog.csdn.net/zxt0601/article/details/52303781, 裡面詳細描述了本控制項V1.0版本是怎麼實現的。)
期間有很多朋友在評論、issue裡提出了一些改進意見,例如支援設定滑動方向(左右)、高仿QQ的互動、支援GridLayoutManager等,以及一些bug。已經被我全部實、修複。並且將其打包至jitpack,引入更方便。和第一版相比,改動挺多的。故將其整理,新發一版。
那麼本文先從如何使用它講起,然後介紹它包含的特性、支援的屬性。最後就幾個痛點和衝突的解決進行講解。
代碼傳送門:喜歡的話,隨手點個star。多謝
https://github.com/mcxtzhang/SwipeDelMenuLayout
先上幾個gif給各位看官感受一下最新版的魅力(以下版本都順便展示了可選的雙向滑動)
本控制項最大魅力就是0耦合,所以先上配合我另一個庫組裝的效果(ItemDecorationIndexBar + SwipeMenuLayout):
(ItemDecorationIndexBar : https://github.com/mcxtzhang/ItemDecorationIndexBar)
Android Special Version (無阻塞式,側滑菜單展開時,依然可以展開其他側滑菜單,同時上一個菜單會自動關閉):
GridLayoutManager (和的代碼比,只需修改RecyclerView的LayoutManager。):
LinearLayout (不需任何修改,連LinearLayout也可以簡單的實現側滑菜單):
iOS interaction (阻塞式互動,高仿QQ,側滑菜單展開式,屏蔽其他ITEM所有操作):
使用:
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,那麼我就在其外嵌套本控制項,並且以側滑菜單出現的順序,依次排列菜單控制項即可。
<?xml version="1.0" encoding="utf-8"?><com.mcxtzhang.swipemenulib.SwipeMenuLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="100dp" android:clickable="true" android:paddingBottom="1dp"> <TextView android:id="@+id/content" android:layout_width="match_parent" android:layout_height="match_parent" android:background="?android:attr/selectableItemBackground" android:gravity="center" android:text="項目中我是任意複雜的原ContentItem布局"/> <!-- 以下都是側滑菜單的內容依序排列 --> <Button android:id="@+id/btnTop" android:layout_width="60dp" android:layout_height="match_parent" android:background="#d9dee4" android:text="置頂" android:textColor="@android:color/white"/> <Button android:id="@+id/btnUnRead" android:layout_width="120dp" android:layout_height="match_parent" android:background="#ecd50a" android:clickable="true" android:text="標記未讀" android:textColor="@android:color/white"/> <Button android:id="@+id/btnDelete" android:layout_width="60dp" android:layout_height="match_parent" android:background="@color/red_ff4a57" android:text="刪除" android:textColor="@android:color/white"/></com.mcxtzhang.swipemenulib.SwipeMenuLayout>
支援屬性:
1 通過 isIos 變數控制是否是IOS阻塞式互動,預設是開啟的。
2 通過 isSwipeEnable 變數控制是否開啟右滑菜單,預設開啟。(某些情境,複用item,沒有編輯許可權的使用者不能右滑)
3 通過開關 isLeftSwipe支援左滑右滑
有兩種方式設定:
一:xml:
<com.mcxtzhang.swipemenulib.SwipeMenuLayout xmlns:app="http://schemas.android.com/apk/res-auto" app:ios="false" app:leftSwipe="true" app:swipeEnable="true">
二: 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上下載源碼瀏覽。
上文地址:
http://blog.csdn.net/zxt0601/article/details/52303781
代碼傳送門:喜歡的話,隨手點個star。多謝
https://github.com/mcxtzhang/SwipeDelMenuLayout
大家使用中如有問題,多多反饋。
【Android】史上最簡單,一步整合側滑(刪除)菜單,高仿QQ、IOS