您好,登錄后才能下訂單哦!
SmartSwipe是一個Android側滑處理框架,它封裝了對控件側滑事件(上/下/左/右4個方向滑動的手勢事件)的捕獲、分發及多點交替滑動的處理,基于SmartSwipe我們可以為控件添加各種你想要的側滑效果。
先來看看它能做些什么吧!
如果已經了解SmartSwipe的功能,只是想了解他的實現原理 可跳過第一節,直接看第二節的原理介紹。 如果覺得文章還不錯,我會定期分享Android知識點及解析,還會不斷更新的BATJ面試專題,歡迎大家前來探討交流, 如有好的文章也歡迎投稿。
//仿手機QQ的手勢滑動返回SmartSwipeBack.activityStayBack(application,?null);????? //仿微信帶聯動效果的透明側滑返回SmartSwipeBack.activitySlidingBack(application,?null);?? //側滑開門樣式關閉activitySmartSwipeBack.activityDoorBack(application,?null);????? //側滑百葉窗樣式關閉activitySmartSwipeBack.activityShuttersBack(application,?null);? //仿小米MIUI系統的貝塞爾曲線返回效果SmartSwipeBack.activityBezierBack(application,?null);
效果圖:
側滑返回效果
//為控件添加仿iOS的彈性留白效果://當縱向不能滾動(或滾動到頂/底)時,若繼續拖動,則UI呈現彈性留白效果,釋放后平滑恢復SmartSwipe.wrap(view) ????.addConsumer(new?SpaceConsumer()) ????.enableVertical();
效果圖:
//為控件添加仿MIUI的彈性拉伸效果://當縱向不能滾動(或滾動到頂/底)時,若繼續拖動,則UI呈現彈性拉伸效果,釋放后平滑恢復SmartSwipe.wrap(view) ????.addConsumer(new?StretchConsumer()) ????.enableVertical();
效果圖:
//xxxMode第二個參數為false,表示工作方向為縱向:下拉刷新&上拉加載更多 //如果第二個參數設置為true,則表示工作方向為橫向:右拉刷新&左拉加載更多 SmartSwipeRefresh.drawerMode(view,?false).setDataLoader(loader); SmartSwipeRefresh.behindMode(view,?false).setDataLoader(loader); SmartSwipeRefresh.scaleMode(view,?false).setDataLoader(loader); SmartSwipeRefresh.translateMode(view,?false).setDataLoader(loader);
樣式 | 效果圖 |
---|---|
drawerMode | drawerMode |
behindMode | behindMode |
scaleMode | scaleMode |
translateMode | translateMode |
SmartSwipe.wrap(view)??? //添加抽屜效果,其效果與DrawerLayout相似 ????//??DrawerLayout只支持左右2個方向,而DrawerConsumer支持上下左右4個方向 ????.addConsumer(new?DrawerConsumer())?? ????//設置橫向(左右兩側)的抽屜為同一個view(常見的側滑顯示刪除按鈕的功能) ????.setHorizontalDrawerView(buttonsViewGroup)? ????.setScrimColor(0x2F000000)?//設置遮罩的顏色 ????.setShadowColor(0x80000000)?//設置邊緣的陰影顏色 ????;
效果圖:
SmartSwipe.wrap(view) ????.addConsumer(new?SlidingConsumer()) ????.setRelativeMoveFactor(0.3F)?//聯動系數 ????.setHorizontalDrawerView(buttonsView) ????.setScrimColor(0x2F000000) ????;
效果圖:
SmartSwipe.wrap(coverView) ????.addConsumer(new?ShuttersConsumer())?//百葉窗效果 ????.setScrimColor(0xAF000000) ????.enableAllDirections() ????.addListener(new?SimpleSwipeListener()?{????????@Override ????????public?void?onSwipeOpened(SmartSwipeWrapper?wrapper,?SwipeConsumer?consumer,?int?direction)?{????????????//封面打開后自動隱藏或移除 ????????????wrapper.setVisibility(View.GONE); ????????} ????});
效果圖:
SmartSwipe.wrap(coverView) ????.addConsumer(new?DoorConsumer())?//開門效果 ????.setScrimColor(0xAF000000) ????.enableAllDirections() ????.addListener(new?SimpleSwipeListener()?{????????@Override ????????public?void?onSwipeOpened(SmartSwipeWrapper?wrapper,?SwipeConsumer?consumer,?int?direction)?{????????????//封面打開后自動隱藏或移除 ????????????wrapper.setVisibility(View.GONE); ????????} ????});
效果圖:
ViewDragHelper是Android官方支持庫中有一個工具類。它可以幫助我們處理控件的拖拽:先創建一個自定義ViewGroup,將被拖動的控件添加到這個自定義ViewGroup中,并用ViewDragHelper來處理控件的拖拽。
ViewDragHelper的主要作用是:攔截父容器的touch事件,捕獲一個子控件來進行拖拽,通過改變這個子控件的left和top來將其在父容器中重新定位,從而達到拖拽的效果。
在官方支持庫中,滑動抽屜相關的SlidingPaneLayout和DrawerLayout,以及CoordinatorLayout布局相關的BottomSheetBehavior和SwipeDismissBehavior,都能看到ViewDragHelper的身影。
但是,ViewDragHelper的名稱也表明它就是用來處理拖拽的,拖拽的對象必須是一個子View,在拖拽的過程中需要改變子控件的left和top,對于一些沒有子View被拖拽的側滑效果(例如:MIUI系統的貝塞爾曲線側滑返回效果、手機QQ的側滑返回效果及MIUI官方app中的普遍使用了的彈性拉伸效果等等),卻有點力有不逮。
針對側滑這個手勢,我們能不能將它的概念抽象一下,到底側滑指的是什么呢?
狹義側滑:從屏幕的某個邊緣開始向著遠離該邊緣的方向滑動
廣義側滑:手指在屏幕上按下之后向著某個方向滑動
我的理解是,廣義側滑包含狹義側滑,只不過是觸發區域是否在屏幕邊緣的區別罷了。
既然側滑手勢能被明確地抽象出來,那么我們是否可以借鑒ViewDragHelper的事件攔截思路將它做這樣的封裝?
對被側滑控件的touch事件進行攔截分析,確認是否將其捕獲作為側滑手勢 然后計算好側滑的實時位移(手指滑動的位移,而不是不依賴于View的left與top) 再通過策略模式(Strategy?Pattern)使用不同的策略不斷消費側滑的位移來進行側滑效果的UI呈現。
答案是肯定的!
SmartSwipe在ViewDragHelper的基礎上,將它對子View的捕獲及移動處理改造成對父View自身觸摸事件的定性(能否及是否捕獲)、定向(捕獲的事件所觸發的側滑方向)及定位(事件捕獲之后在側滑方向上移動的距離),并將側滑距離交由SwipeConsumer來消費,SwipeConsumer根據側滑距離的變化對控件布局進行相應的改變。
SmartSwipe的封裝思路如下:
用一個ViewGroup將需要處理側滑事件的控件View包裹起來(被包裹起來的控件作為它的contentView
)
可以為這個ViewGroup添加一些附屬控件(如:滑動抽屜)
攔截這個ViewGroup的touch事件,并將touch事件轉換為側滑距離交給SwipeConsumer進行消費
SwipeConsumer根據側滑距離的變化對控件布局進行相應的改變
通過繼承SwipeConsumer,用不同的方式來改變控件布局(例如:對contentView
及附屬控件的位置、縮放、透明等進行改變),從而實現各種側滑的效果。
于是,側滑的手勢事件識別及滑動距離計算的工作在框架內部就統一完成了,至于根據側滑距離來實現各種不同的UI呈現效果,就可以很方便地通過繼承SwipeConsumer來實現了。
以框架內置的仿MIUI系統應用中彈性拉伸效果的實現為例
根據側滑距離,對contentView進行縮放和平移,從而實現彈性拉伸效果
代碼如下:
public?class?StretchConsumer?extends?SwipeConsumer?{ ????@Override ????public?void?onDetachFromWrapper()?{ ????????super.onDetachFromWrapper(); ????????View?contentView?=?mWrapper.getContentView(); ????????if?(contentView?!=?null)?{ ????????????contentView.setScaleX(1); ????????????contentView.setScaleY(1); ????????????contentView.setTranslationX(0); ????????????contentView.setTranslationY(0); ????????} ????} ????@Override ????public?void?onDisplayDistanceChanged(int?distanceXToDisplay, ????????int?distanceYToDisplay,?int?dx,?int?dy)?{ ????????View?contentView?=?mWrapper.getContentView(); ????????if?(contentView?!=?null)?{ ????????????if?(((distanceXToDisplay?>=?0)?&&?isLeftEnable())?|| ????????????????????((distanceXToDisplay?<=?0)?&&?isRightEnable()))?{ ????????????????contentView.setScaleX(1?+ ????????????????????(Math.abs((float)?distanceXToDisplay)?/?mWidth)); ????????????????contentView.setTranslationX(distanceXToDisplay?/?2F); ????????????} ????????????if?(((distanceYToDisplay?>=?0)?&&?isTopEnable())?|| ????????????????????((distanceYToDisplay?<=?0)?&&?isBottomEnable()))?{ ????????????????contentView.setScaleY(1?+ ????????????????????(Math.abs((float)?distanceYToDisplay)?/?mHeight)); ????????????????contentView.setTranslationY(distanceYToDisplay?/?2F); ????????????} ????????} ????} }
以上就是實現彈性拉伸效果的全部代碼,很簡單,不是嗎?
它的使用方式同樣簡單:
SmartSwipe.wrap(view)?//指定目標控件 ????.addConsumer(new?StretchConsumer())?//添加彈性拉伸效果 ????.enableVertical();?//指定工作方向為:上、下2個方向
再來看看仿手機QQ側滑返回的效果如何實現
手機QQ側滑時UI沒有任何變化 在手指釋放時,根據滑動的方向和速率來決定是否finish當前Activity
代碼如下:
public?class?StayConsumer?extends?SwipeConsumer?{ ????private?int?mMinVelocity?=?1000; ????public?StayConsumer()?{?//不能通過滑動距離判斷是否需要打開 ????????setOpenDistance(Integer.MAX_VALUE).setMaxSettleDuration(0);?//打開時無需動畫,時間置為0 ????} ????@Override ????protected?void?onDisplayDistanceChanged(int?distanceXToDisplay, ????????int?distanceYToDisplay,?int?dx,?int?dy)?{?//滑動時不需要對contentView做任何改變 ????} ????@Override ????public?void?onSwipeReleased(float?xVelocity,?float?yVelocity)?{?//在釋放時,根據速率和方向來決定是否打開 ????????if?(Math.abs(xVelocity)?>?Math.abs(yVelocity))?{ ????????????if?(((mDirection?==?DIRECTION_LEFT)?&&?(xVelocity?>=?mMinVelocity))?|| ????????????????????((mDirection?==?DIRECTION_RIGHT)?&& ????????????????????(xVelocity?<=?-mMinVelocity)))?{?//置為打開狀態 ????????????????mCurSwipeDistanceX?=?getSwipeOpenDistance(); ????????????????mProgress?=?1; ????????????} ????????}?else?{ ????????????if?(((mDirection?==?DIRECTION_TOP)?&&?(yVelocity?>=?mMinVelocity))?|| ????????????????????((mDirection?==?DIRECTION_BOTTOM)?&& ????????????????????(yVelocity?<=?-mMinVelocity)))?{?//置為打開狀態 ????????????????mCurSwipeDistanceY?=?getSwipeOpenDistance(); ????????????????mProgress?=?1; ????????????} ????????} ????????super.onSwipeReleased(xVelocity,?yVelocity); ????} ????public?int?getMinVelocity()?{ ????????return?mMinVelocity; ????}?//支持使用者設置最低速率的閾值 ????public?StayConsumer?setMinVelocity(int?minVelocity)?{ ????????if?(minVelocity?>?0)?{ ????????????this.mMinVelocity?=?minVelocity; ????????} ????????return?this; ????} }
是不是也很簡單!
碼字不易,如果覺得文章還不錯的朋友點贊+關注+轉發,我會定期分享Android知識點及解析,還會不斷更新的BATJ面試專題以及互聯網趣事,歡迎大家前來探討交流,如有好的文章也歡迎投稿。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。