側滑手勢在Android App應用得很是普遍,常見的使用場景包括:滑動抽屜、側滑刪除、側滑返回、下拉刷新以及側滑封面等。因爲這些使用場景實在是太通用了,各路大神們八仙過海各顯神通,每種側滑場景都開源出了不少很是實用的框架,讓咱們的業務開發便利了不少。html
目前,咱們須要爲每種場景引入不一樣的側滑框架,因爲App中的側滑場景不少,咱們項目中也就須要引入多個側滑框架,而每一個框架的使用方式各有不一樣,須要單獨學習,團隊的學習成本較高。java
那麼問題來了,有沒有一種框架能解決全部側滑需求呢?android
在剛開始學習面向對象編程概念的時候咱們就知道一個道理:解決一個軟件問題,首先要將它抽象出來。git
針對側滑這個手勢,咱們能不能將它的概念抽象一下,到底側滑指的是什麼呢?github
個人理解是,廣義側滑包含狹義側滑,只不過是觸發區域是否在屏幕邊緣的區別罷了。web
因而,側滑的概念就這樣被清晰地抽象出來了。編程
從這個抽象概念能夠看出:側滑手勢同一時間只處理上下左右4個方向中的一個方向
若是咱們將這個抽象概念封裝出來,將手勢事件的識別、攔截及數據加工在框架內部處理好,並向外實時地輸出側滑方向、距離及相關的回調, 理論上咱們就能夠實現全部的側滑需求了。微信
至於具體的側滑效果,學過策略模式的都知道: 每一種具體的側滑效果實現均可以看作是一種側滑策略。
胸擡,憋急!磨刀不誤砍柴工,站在巨人的肩膀上你就有可能比巨人高那麼一點點。app
Google在android support庫中爲側滑菜單的需求提供了SlidingPaneLayout和DrawerLayout兩種實現,看源碼會發現二者都是基於ViewDragHelper來實現的,那麼ViewDragHelper又是何方神聖呢?框架
ViewDragHelper是android support庫中的一個工具類。它能夠幫助咱們處理控件的拖拽,它的使用方式爲:先建立一個自定義ViewGroup,將被拖動的控件添加到這個自定義ViewGroup中,並用ViewDragHelper來處理控件的拖拽,能夠經過Callback來指定拖拽區域及捕獲子控件的邏輯。
經過閱讀ViewDragHelper的源碼發現,它對view在父容器中的拖拽行爲進行了封裝,經過攔截父容器控件的手勢事件,捕獲須要拖拽的子控件,並實時根據手指的移動改變它的座標,從而實現拖拽效果。
ViewDragHelper封裝的很優雅,也很強大,
有些開源側滑框架也是基於ViewDragHelper來實現的,例如:
ikew0ng/SwipeBackLayout / daimajia/AndroidSwipeLayout
不過,ViewDragHelper封裝的是子控件的拖拽,而不是側滑,它計算距離的基準是控件的top和left座標,雖然能夠將其中一個方向(橫向或縱向)的拖動範圍設置爲0來模擬側滑手勢,但它不符合咱們側滑手勢的抽象定義,沒法解決側滑時不是控件移動的效果。
例如:MIUI系統側滑返回效果及小米公司出品的App廣泛使用的彈性拉伸效果等
既然側滑已經被清晰地抽象出來了,一樣是對觸摸滑動事件的處理,咱們徹底能夠借鑑ViewDragHelper的思想:將它對子控件的捕獲和拖動,改爲對側滑方向的捕獲和側滑距離的計算,並將它的Callback改形成側滑距離的消費者(具體的側滑效果就看消費者用哪一種方式來消費掉這個側滑距離)。
側滑行爲的2個核心要素:側滑方向、側滑距離
根據這個思路,我封裝了一個智能的側滑框架:SmartSwipe,能夠解決你所(chui)有(niu)的(bi)側滑需求。請大聲說出它的slogan!
固然,這是吹牛逼的!
框架只是封裝了側滑行爲事件的捕獲、分發及多點交替滑動的處理,具體的側滑效果(消費側滑距離的策略)須要你本身來實現。。。哎。。。等等,胸擡,先別走啊!還沒說完呢,SmartSwipe中內置了十多種常見側滑效果,有動圖爲證:
//仿iOS的彈性留白效果: //側滑時表現爲彈性留白效果,結束後自動恢復 SmartSwipe.wrap(view) .addConsumer(new SpaceConsumer()) .enableVertical(); //工做方向:縱向
//仿MIUI的彈性拉伸效果: //側滑時表現爲彈性拉伸效果,結束後自動恢復 SmartSwipe.wrap(view) .addConsumer(new StretchConsumer()) .enableVertical(); //工做方向:縱向
抽屜顯示在主view之上,相似於DrawerLayout
SmartSwipe.wrap(view) .addConsumer(new DrawerConsumer()) //抽屜效果 //能夠設置橫向(左右兩側)的抽屜爲同一個view //也能夠爲不一樣方向分別設置不一樣的view .setHorizontalDrawerView(menuLayout) .setScrimColor(0x2F000000) //設置遮罩的顏色 .setShadowColor(0x80000000) //設置邊緣的陰影顏色 ;
抽屜顯示在主view之下
SmartSwipe.wrap(view) .addConsumer(new SlidingConsumer()) .setHorizontalDrawerView(textView) .setScrimColor(0x2F000000) //設置聯動係數 // 0:不聯動,視覺效果爲:主體移動後顯示下方的抽屜 // 0~1: 半聯動,視覺效果爲:抽屜視圖按照聯動係數與主體之間存在相對移動效果 // 1:全聯動,視覺效果爲:抽屜跟隨主體一塊兒移動(pixel by pixel) .setRelativeMoveFactor(0.5F) ;
側滑透明效果,側滑後可顯示被其遮擋的view,可用做側滑刪除,也能夠用來製做封面效果
//側滑刪除 SmartSwipe.wrap(view) .addConsumer(new TranslucentSlidingConsumer()) .enableHorizontal() //啓用左右兩側側滑 .addListener(new SimpleSwipeListener(){ @Override public void onSwipeOpened(SmartSwipeWrapper wrapper, SwipeConsumer consumer, int direction) { //側滑打開時,移除 ViewParent parent = wrapper.getParent(); if (parent instanceof ViewGroup) { ((ViewGroup) parent).removeView(wrapper); } //adapter.removeItem(getAdapterPosition());// 也可用做從recyclerView中移除該項 } }) ;
側滑時,主view保持不動,手指釋放時,識別滑動方向及速率,以肯定是否執行對應的側滑邏輯。
//demo:用StayConsumer來作activity側滑返回 SmartSwipe.wrap(this) .addConsumer(new StayConsumer()) .enableAllDirections() .addListener(new SimpleSwipeListener(){ @Override public void onSwipeOpened(SmartSwipeWrapper wrapper, SwipeConsumer consumer, int direction) { finish(); } }) ;
側滑時主view像百葉窗同樣打開,透明顯示下層的視圖。
可用來製做封面、輪播圖等
//用ShuttersConsumer實現百葉窗側滑刪除 SmartSwipe.wrap(view) .addConsumer(new ShuttersConsumer()) .enableHorizontal() //啓用左右兩側側滑 .addListener(new SimpleSwipeListener(){ @Override public void onSwipeOpened(SmartSwipeWrapper wrapper, SwipeConsumer consumer, int direction) { //側滑打開時,移除 ViewParent parent = wrapper.getParent(); if (parent instanceof ViewGroup) { ((ViewGroup) parent).removeView(wrapper); } //adapter.removeItem(getAdapterPosition());// 也可用做從recyclerView中移除該項 } }) ;
側滑時,主view像開門同樣從中間向兩邊(上下 或 左右)分開,透明顯示它下層的視圖
可用來製做封面、輪播圖等
//用DoorConsumer實現百葉窗側滑刪除 SmartSwipe.wrap(view) .addConsumer(new DoorConsumer()) .enableHorizontal() //啓用左右兩側側滑 .addListener(new SimpleSwipeListener(){ @Override public void onSwipeOpened(SmartSwipeWrapper wrapper, SwipeConsumer consumer, int direction) { //側滑打開時,移除 ViewParent parent = wrapper.getParent(); if (parent instanceof ViewGroup) { ((ViewGroup) parent).removeView(wrapper); } //adapter.removeItem(getAdapterPosition());// 也可用做從recyclerView中移除該項 } }) ;
側滑時,在控件側滑的邊緣顯示一個貝塞爾曲線的返回效果
可用於activity返回、fragment返回,也可用於webview的返回/前進
//activity側滑返回 SmartSwipe.wrap(this) .addConsumer(new BezierBackConsumer()) .enableAllDirections() .addListener(new SimpleSwipeListener() { @Override public void onSwipeOpened(SmartSwipeWrapper wrapper, SwipeConsumer consumer, int direction) { finish(); } }) ;
沒錯,專爲activity側滑返回而做的一種效果,帶聯動功能
//activity側滑返回 SmartSwipe.wrap(this) .addConsumer(new ActivitySlidingBackConsumer(this)) //設置聯動係數 .setRelativeMoveFactor(0.5F) //指定可側滑返回的方向,如:enableLeft() 僅左側可側滑返回 .enableAllDirections() ;
沒錯,也是專爲activity側滑返回而做的一種效果,透明顯示前一個activity
//activity側滑返回 SmartSwipe.wrap(this) .addConsumer(new ActivityShuttersBackConsumer(this)) .setScrimColor(0x7F000000) .enableAllDirections() ;
沒錯,這仍是一個專爲activity側滑返回而做的效果,透明顯示前一個activity
//activity側滑返回 SmartSwipe.wrap(this) .addConsumer(new ActivitySlidingBackConsumer(this)) .setRelativeMoveFactor(0.5F) .enableAllDirections() ;
SmartSwipe中絕大多數的使用均可以經過鏈式編程在一行代碼內完成,API的設計風格以下:
SmartSwipe.wrap(...) //view or Activity .addConsumer(...) //添加consumer .enableDirection(...) //指定consumer接收哪一個方向的側滑事件 .setXxx(...) //[可選]一些其它設置項 .addListener(...); //[可選]給consumer添加監聽
除了基礎的側滑效果外,爲了方便開發者使用,還封裝了工具類:SmartSwipeBack 和 SmartSwipeRefresh
//仿手機QQ的手勢滑動返回 SmartSwipeBack.activityStayBack(application, null); //仿微信帶聯動效果的透明側滑返回 SmartSwipeBack.activitySlidingBack(application, null); //側滑開門樣式關閉activity SmartSwipeBack.activityDoorBack(application, null); //側滑百葉窗樣式關閉activity SmartSwipeBack.activityShuttersBack(application, null); //仿小米MIUI系統的貝塞爾曲線返回效果 SmartSwipeBack.activityBezierBack(application, null);
可用於任意view
//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 | |
behindMode | |
scaleMode | |
translateMode |
header和footer可以使用第三方炫酷的自定義view,如:基於Ifxcyr/ArrowDrawable的ArrowHeader,效果圖以下:
這就須要自定義SwipeConsumer了,步驟以下:
tryAcceptMoving
和tryAcceptSettling
方法clampDistanceHorizontal
及clampDistanceHorizontal
方法,可在知足必定條件下才真正執行側滑以框架內置彈性拉伸效果StretchConsumer爲例
根據側滑距離,對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); } } } }
以上就是實現彈性拉伸效果的所有代碼,很簡單,不是嗎?
能實現全部側滑效果只存在於理論上,確定還須要不斷地完善,開源出來也是但願能利用開源社區的力量來完善它,讓android側滑更簡單!
最後,奉上相關連接地址:
源碼: https://github.com/luckybilly/SmartSwipe
文檔: https://luckybilly.github.io/SmartSwipe-tutorial/ (採用gitbook形式精心編寫而成)
Demo下載: https://github.com/luckybilly/SmartSwipe/raw/master/app-release.apk
本文爲雲棲社區原創內容,未經容許不得轉載。