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

SmartSwipe是一個Android側滑處理框架,它封裝了對控件側滑事件(上/下/左/右4個方向滑動的手勢事件)的捕獲、分發及多點交替滑動的處理,基於SmartSwipe咱們能夠爲控件添加各類你想要的側滑效果。html

先來看看它能作些什麼吧!java

若是已經瞭解SmartSwipe的功能,只是想了解他的實現原理
可跳過第一節,直接看第二節的原理介紹
複製代碼

1、 用法及演示

1.1 一行代碼實現全局側滑返回

//仿手機QQ的手勢滑動返回
SmartSwipeBack.activityStayBack(application, null);		
//仿微信帶聯動效果的透明側滑返回
SmartSwipeBack.activitySlidingBack(application, null);	
//側滑開門樣式關閉activity
SmartSwipeBack.activityDoorBack(application, null);		
//側滑百葉窗樣式關閉activity
SmartSwipeBack.activityShuttersBack(application, null);	
//仿小米MIUI系統的貝塞爾曲線返回效果
SmartSwipeBack.activityBezierBack(application, null);
複製代碼

側滑返回的更多用法請戳 這裏android

效果圖:git

側滑返回效果

1.2 一行代碼讓頁面動起來

//爲控件添加仿iOS的彈性留白效果:
//當縱向不能滾動(或滾動到頂/底)時,若繼續拖動,則UI呈現彈性留白效果,釋放後平滑恢復
SmartSwipe.wrap(view)
    .addConsumer(new SpaceConsumer())
    .enableVertical();
複製代碼

效果圖:github

彈性留白效果

1.3 一行代碼讓頁面具備彈性

//爲控件添加仿MIUI的彈性拉伸效果:
//當縱向不能滾動(或滾動到頂/底)時,若繼續拖動,則UI呈現彈性拉伸效果,釋放後平滑恢復
SmartSwipe.wrap(view)
    .addConsumer(new StretchConsumer())
    .enableVertical();
複製代碼

效果圖:bash

彈性拉伸效果

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)	//設置邊緣的陰影顏色
    ;
複製代碼

效果圖:app

滑動菜單

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);
        }
    });
複製代碼

效果圖:ide

百葉窗封面

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、實現原理

2.1 先介紹一下ViewDragHelper

ViewDragHelper是Android官方支持庫中有一個工具類。它能夠幫助咱們處理控件的拖拽:先建立一個自定義ViewGroup,將被拖動的控件添加到這個自定義ViewGroup中,並用ViewDragHelper來處理控件的拖拽。

ViewDragHelper的主要做用是:攔截父容器的touch事件,捕獲一個子控件來進行拖拽,經過改變這個子控件的left和top來將其在父容器中從新定位,從而達到拖拽的效果。

在官方支持庫中,滑動抽屜相關的SlidingPaneLayout和DrawerLayout,以及CoordinatorLayout佈局相關的BottomSheetBehavior和SwipeDismissBehavior,都能看到ViewDragHelper的身影。

可是,ViewDragHelper的名稱也代表它就是用來處理拖拽的,拖拽的對象必須是一個子View,在拖拽的過程當中須要改變子控件的left和top,對於一些沒有子View被拖拽的側滑效果(例如:MIUI系統的貝塞爾曲線側滑返回效果、手機QQ的側滑返回效果及MIUI官方app中的廣泛使用了的彈性拉伸效果等等),卻有點力有不逮。

2.2 借鑑ViewDragHelper實現側滑處理

針對側滑這個手勢,咱們能不能將它的概念抽象一下,到底側滑指的是什麼呢?

  • 狹義側滑:從屏幕的某個邊緣開始向着遠離該邊緣的方向滑動
  • 廣義側滑:手指在屏幕上按下以後向着某個方向滑動

個人理解是,廣義側滑包含狹義側滑,只不過是觸發區域是否在屏幕邊緣的區別罷了。

既然側滑手勢能被明確地抽象出來,那麼咱們是否能夠借鑑ViewDragHelper的事件攔截思路將它作這樣的封裝?

對被側滑控件的touch事件進行攔截分析,確認是否將其捕獲做爲側滑手勢
而後計算好側滑的實時位移(手指滑動的位移,而不是不依賴於View的left與top)
再經過策略模式(Strategy Pattern)使用不一樣的策略不斷消費側滑的位移來進行側滑效果的UI呈現。
複製代碼

答案是確定的!

2.3 SmartSwipe的實現原理

SmartSwipe在ViewDragHelper的基礎上,將它對子View的捕獲及移動處理改形成對父View自身觸摸事件的定性(可否及是否捕獲)、定向(捕獲的事件所觸發的側滑方向)及定位(事件捕獲以後在側滑方向上移動的距離),並將側滑距離交由SwipeConsumer來消費,SwipeConsumer根據側滑距離的變化對控件佈局進行相應的改變。

SmartSwipe的封裝思路以下:

  • 用一個ViewGroup將須要處理側滑事件的控件View包裹起來(被包裹起來的控件做爲它的contentView
  • 能夠爲這個ViewGroup添加一些附屬控件(如:滑動抽屜
  • 攔截這個ViewGroup的touch事件,並將touch事件轉換爲側滑距離交給SwipeConsumer進行消費
  • SwipeConsumer根據側滑距離的變化對控件佈局進行相應的改變
  • 經過繼承SwipeConsumer,用不一樣的方式來改變控件佈局(例如:對contentView及附屬控件的位置、縮放、透明等進行改變),從而實現各類側滑的效果。

因而,側滑的手勢事件識別及滑動距離計算的工做在框架內部就統一完成了,至於根據側滑距離來實現各類不一樣的UI呈現效果,就能夠很方便地經過繼承SwipeConsumer來實現了。

2.4 如何建立自定義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;
    }
}
複製代碼

是否是也很簡單!

點擊這裏瞭解建立自定義SwipeConsumer的詳細步驟

小結

本文介紹了SmartSwipe側滑處理框架的使用方式及實現原理,並經過2個示例介紹了自定義側滑效果的方法。

只是文中的示例是較爲簡單的側滑效果,至於複雜的側滑效果實現介紹,若是讀者們須要的話,我接下來另外寫一篇文章來單獨介紹,若有須要,請給我留言!

另外,Star一個開源項目是對它最好的鼓勵和支持!

相關文章
相關標籤/搜索