SmartSwipe是一個Android側滑處理框架,它封裝了對控件側滑事件(上/下/左/右4個方向滑動的手勢事件)的捕獲创夜、分發(fā)及多點交替滑動的處理杭跪,基于SmartSwipe我們可以為控件添加各種你想要的側滑效果。
先來看看它能做些什么吧驰吓!
如果已經了解SmartSwipe的功能涧尿,只是想了解他的實現原理
可跳過第一節(jié),直接看第二節(jié)的原理介紹
一檬贰、 用法及演示
1.1 一行代碼實現全局側滑返回
//仿手機QQ的手勢滑動返回
SmartSwipeBack.activityStayBack(application, null);
//仿微信帶聯動效果的透明側滑返回
SmartSwipeBack.activitySlidingBack(application, null);
//側滑開門樣式關閉activity
SmartSwipeBack.activityDoorBack(application, null);
//側滑百葉窗樣式關閉activity
SmartSwipeBack.activityShuttersBack(application, null);
//仿小米MIUI系統(tǒng)的貝塞爾曲線返回效果
SmartSwipeBack.activityBezierBack(application, null);
側滑返回的更多用法請戳 這里
效果圖:
1.2 一行代碼讓頁面動起來
//為控件添加仿iOS的彈性留白效果:
//當縱向不能滾動(或滾動到頂/底)時姑廉,若繼續(xù)拖動,則UI呈現彈性留白效果翁涤,釋放后平滑恢復
SmartSwipe.wrap(view)
.addConsumer(new SpaceConsumer())
.enableVertical();
效果圖:
1.3 一行代碼讓頁面具有彈性
//為控件添加仿MIUI的彈性拉伸效果:
//當縱向不能滾動(或滾動到頂/底)時桥言,若繼續(xù)拖動,則UI呈現彈性拉伸效果葵礼,釋放后平滑恢復
SmartSwipe.wrap(view)
.addConsumer(new StretchConsumer())
.enableVertical();
效果圖:
1.4 一行代添加下拉刷新
//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 |
1.5 一行代碼添加滑動菜單
SmartSwipe.wrap(view)
//添加抽屜效果章咧,其效果與DrawerLayout相似
// DrawerLayout只支持左右2個方向倦西,而DrawerConsumer支持上下左右4個方向
.addConsumer(new DrawerConsumer())
//設置橫向(左右兩側)的抽屜為同一個view(常見的側滑顯示刪除按鈕的功能)
.setHorizontalDrawerView(buttonsViewGroup)
.setScrimColor(0x2F000000) //設置遮罩的顏色
.setShadowColor(0x80000000) //設置邊緣的陰影顏色
;
效果圖:
1.6 一行代碼添加具有聯動效果的滑動菜單
SmartSwipe.wrap(view)
.addConsumer(new SlidingConsumer())
.setRelativeMoveFactor(0.3F) //聯動系數
.setHorizontalDrawerView(buttonsView)
.setScrimColor(0x2F000000)
;
效果圖:
1.7 炫酷的封面
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);
}
});
效果圖:
關于封面的更多設置請參考: Demo
二、實現原理
2.1 先介紹一下ViewDragHelper
ViewDragHelper是Android官方支持庫中有一個工具類赁严。它可以幫助我們處理控件的拖拽:先創(chuàng)建一個自定義ViewGroup扰柠,將被拖動的控件添加到這個自定義ViewGroup中粉铐,并用ViewDragHelper來處理控件的拖拽。
ViewDragHelper的主要作用是:攔截父容器的touch事件卤档,捕獲一個子控件來進行拖拽蝙泼,通過改變這個子控件的left和top來將其在父容器中重新定位,從而達到拖拽的效果劝枣。
在官方支持庫中汤踏,滑動抽屜相關的SlidingPaneLayout和DrawerLayout,以及CoordinatorLayout布局相關的BottomSheetBehavior和SwipeDismissBehavior舔腾,都能看到ViewDragHelper的身影溪胶。
但是,ViewDragHelper的名稱也表明它就是用來處理拖拽的稳诚,拖拽的對象必須是一個子View哗脖,在拖拽的過程中需要改變子控件的left和top,對于一些沒有子View被拖拽的側滑效果(例如:MIUI系統(tǒng)的貝塞爾曲線側滑返回效果扳还、手機QQ的側滑返回效果及MIUI官方app中的普遍使用了的彈性拉伸效果等等)才避,卻有點力有不逮。
2.2 借鑒ViewDragHelper實現側滑處理
針對側滑這個手勢氨距,我們能不能將它的概念抽象一下桑逝,到底側滑指的是什么呢?
- 狹義側滑:從屏幕的某個邊緣開始向著遠離該邊緣的方向滑動
- 廣義側滑:手指在屏幕上按下之后向著某個方向滑動
我的理解是俏让,廣義側滑包含狹義側滑肢娘,只不過是觸發(fā)區(qū)域是否在屏幕邊緣的區(qū)別罷了。
既然側滑手勢能被明確地抽象出來舆驶,那么我們是否可以借鑒ViewDragHelper的事件攔截思路將它做這樣的封裝橱健?
對被側滑控件的touch事件進行攔截分析,確認是否將其捕獲作為側滑手勢
然后計算好側滑的實時位移(手指滑動的位移沙廉,而不是不依賴于View的left與top)
再通過策略模式(Strategy Pattern)使用不同的策略不斷消費側滑的位移來進行側滑效果的UI呈現拘荡。
答案是肯定的!
2.3 SmartSwipe的實現原理
SmartSwipe在ViewDragHelper的基礎上撬陵,將它對子View的捕獲及移動處理改造成對父View自身觸摸事件的定性(能否及是否捕獲)珊皿、定向(捕獲的事件所觸發(fā)的側滑方向)及定位(事件捕獲之后在側滑方向上移動的距離),并將側滑距離交由SwipeConsumer來消費巨税,SwipeConsumer根據側滑距離的變化對控件布局進行相應的改變蟋定。
SmartSwipe的封裝思路如下:
- 用一個ViewGroup將需要處理側滑事件的控件View包裹起來(被包裹起來的控件作為它的
contentView
) - 可以為這個ViewGroup添加一些附屬控件(如:滑動抽屜)
- 攔截這個ViewGroup的touch事件,并將touch事件轉換為側滑距離交給SwipeConsumer進行消費
- SwipeConsumer根據側滑距離的變化對控件布局進行相應的改變
- 通過繼承SwipeConsumer草添,用不同的方式來改變控件布局(例如:對
contentView
及附屬控件的位置驶兜、縮放、透明等進行改變),從而實現各種側滑的效果抄淑。
于是屠凶,側滑的手勢事件識別及滑動距離計算的工作在框架內部就統(tǒng)一完成了,至于根據側滑距離來實現各種不同的UI呈現效果肆资,就可以很方便地通過繼承SwipeConsumer來實現了矗愧。
2.4 如何創(chuàng)建自定義SwipeConsumer?
以框架內置的仿MIUI系統(tǒng)應用中彈性拉伸效果的實現為例
根據側滑距離,對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)) {
//置為打開狀態(tài)
mCurSwipeDistanceX = getSwipeOpenDistance();
mProgress = 1;
}
} else {
if (mDirection == DIRECTION_TOP && yVelocity >= mMinVelocity || (mDirection == DIRECTION_BOTTOM && yVelocity <= -mMinVelocity)) {
//置為打開狀態(tài)
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;
}
}
是不是也很簡單!
點擊這里了解創(chuàng)建自定義SwipeConsumer的詳細步驟
小結
本文介紹了SmartSwipe側滑處理框架的使用方式及實現原理晓避,并通過2個示例介紹了自定義側滑效果的方法簇捍。
只是文中的示例是較為簡單的側滑效果,至于復雜的側滑效果實現介紹俏拱,如果讀者們需要的話暑塑,我接下來另外寫一篇文章來單獨介紹,如有需要锅必,請給我留言事格!
另外,Star一個開源項目是對它最好的鼓勵和支持搞隐!