你見過微信側滑返回的聯動效果,但開門效果轮傍、百葉窗效果見過嗎暂雹?

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
drawerMode
behindMode
behindMode
scaleMode
scaleMode
translateMode
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一個開源項目是對它最好的鼓勵和支持搞隐!

?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末驹愚,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子劣纲,更是在濱河造成了極大的恐慌逢捺,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件癞季,死亡現場離奇詭異劫瞳,居然都是意外死亡,警方通過查閱死者的電腦和手機绷柒,發(fā)現死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進店門志于,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人废睦,你說我怎么就攤上這事伺绽。” “怎么了?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵憔恳,是天一觀的道長瓤荔。 經常有香客問我,道長钥组,這世上最難降的妖魔是什么输硝? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任,我火速辦了婚禮程梦,結果婚禮上点把,老公的妹妹穿的比我還像新娘。我一直安慰自己屿附,他們只是感情好郎逃,可當我...
    茶點故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著挺份,像睡著了一般褒翰。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上匀泊,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天优训,我揣著相機與錄音,去河邊找鬼各聘。 笑死揣非,一個胖子當著我的面吹牛,可吹牛的內容都是我干的躲因。 我是一名探鬼主播早敬,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼大脉!你這毒婦竟也來了搞监?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤镰矿,失蹤者是張志新(化名)和其女友劉穎琐驴,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體衡怀,經...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡棍矛,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了抛杨。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片够委。...
    茶點故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖怖现,靈堂內的尸體忽然破棺而出茁帽,到底是詐尸還是另有隱情玉罐,我是刑警寧澤,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布潘拨,位于F島的核電站吊输,受9級特大地震影響,放射性物質發(fā)生泄漏铁追。R本人自食惡果不足惜季蚂,卻給世界環(huán)境...
    茶點故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望琅束。 院中可真熱鬧扭屁,春花似錦、人聲如沸涩禀。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽艾船。三九已至葵腹,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間屿岂,已是汗流浹背践宴。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留雁社,地道東北人浴井。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓晒骇,卻偏偏與公主長得像霉撵,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子洪囤,可洞房花燭夜當晚...
    茶點故事閱讀 42,722評論 2 345

推薦閱讀更多精彩內容