使用Snake,安卓也能輕鬆實現類iOS滑動關閉功能
坦率地講,我並不是一個果粉。我也不覺得iOS系統比Android優秀,曾經有過一段時間將iPhone用作主流機。最後還是換成了安卓機,原因是iPhone的價效比的確不高,加上系統的一些限制,可玩性非常有限。而如果你問我,iOS系統裡面有什麼你特別喜歡的功能,滑動關閉無疑是其中一個。當人們在專注螢幕內容的時候,還要轉移注意力聚焦螢幕下方的按鈕,其實是有一些成本的,最直觀的感覺就是不太自然。用過iPhone後再用安卓,人們總是習慣性地右滑...
Demo下載體驗
為了實現類似iOS的滑動關閉效果,在大約一年多前,我開發了一個小工具Snake。使用這個小工具僅需要一行程式碼就可以輕鬆整合滑動關閉功能,當前這個工具的最新版本是0.1.0,老版本截止於0.0.6。
對於老版本的設計,有兩個明顯的缺陷:
-
必須在style檔案中新增如下樣式設定,將Window設定為透明
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowIsTranslucent">true</item>
-
在Fragment中使用必須繼承父類
me.foji.snake.app.Fragment
或me.foji.snake.v4.app.Fragment
,並且要轉移佈局程式碼到onBindView中
為了克服這些問題,我完全重寫了這個小工具,新版本將來到0.1.0。新版本不僅解決了上面兩個問題,而且對程式碼進行了簡化,去掉了冗餘設計。同時,在靈活性上面有了大幅度的提高,可以讓你完全不要改動原有程式碼架構就能使用滑動關閉功能,真正的零侵入性設計。廢話不多說,一起來看一下,怎麼使用吧!
如何使用
1)新增依賴
dependencies {
// Gradle高版本這裡可以使用implementation代替compile
// x.x.x代表最新版本號,請到文章底部點選Github連結檢視
// 截止文章截稿日期,最新版本號都是0.1.0
compile 'com.youngfeng.android:snake:x.x.x'
annotationProcessor 'com.youngfeng.android:snake-compiler:x.x.x'
}
2) 在Application的onCreate
方法中,對Snake進行初始化
public class SnakeApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
Snake.init(this);
}
}
3)在Activity中使用
-
在需要使用滑動關閉的Activity類頭新增
@EnableDragToClose
註解
@EnableDragToClose()
public class FirstActivity extends BaseActivity {
-
在
Activity.onCreate
方法中,使用host
介面對當前Activity進行託管
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Snake.host(this);
}
以上兩個步驟已經使Activity擁有了滑動關閉功能,不相信嗎?試試看!
4)在Fragment中使用
-
同樣地,在需要使用滑動關閉的Fragment類頭新增
@EnableDragToClose
註解 -
如果是android.app.Fragment的子類,使用
Snake.newProxy(FragmentA.class)
建立當前Fragment例項;如果是android.support.v4.app.Fragment的子類,使用Snake.newProxySupport(FragmentA.class)
建立當前Fragment例項。
注意:Fragment
類中不需要使用host
介面對其進行託管,Snake
將自動完成託管。
通過以上四個步驟,你已經成功完成了對滑動關閉的整合。可是,在不同的手機上測試,滑動時你依然會發現幾個問題:
-
Activity
中滑動動畫正常,使用返回鍵回退和點選進入的動畫卻表現不一致 -
Fragment
中除了存在上述一樣的問題外,在滑動關閉後,動畫還會播放一次,導致整體看起來不和諧
相信你應該知道,解決第一個問題其實很簡單,只要在啟動Activity
和結束Activity
的時候使用overridePendingTransition(R.anim.snake_slide_in_right, R.anim.snake_slide_out_left)
設定動畫即可,具體的設定可以參照 Github Demo進行處理。
而對於Fragment
的設定相對較為麻煩,在啟動的時候我們可以通過setCustomAnimation
對其啟動和回退動畫進行統一設定。可是在回退的時候,卻不能關閉某一次動畫播放,導致了動畫重複播放的問題。為了避免這個問題,Snake
增加了wrap
和SnakeAnimationController
兩個介面,通過這兩個介面聯合使用就可以避免這個問題。
具體要怎麼做呢?看這裡:
public class BaseFragment extends Fragment implements SnakeAnimationController {
private boolean mDisableAnimation;
@Override
public Animator onCreateAnimator(int transit, boolean enter, int nextAnim) {
return Snake.wrap(super.onCreateAnimator(transit, enter, nextAnim), this);
}
@Override
public void disableAnimation(boolean disable) {
mDisableAnimation = disable;
}
@Override
public boolean animationDisabled() {
return mDisableAnimation;
}
}
PS:重新onCreateAnimation
的方式完全一致,Snake
的wrap
介面也可以適用。
注意:這個操作強烈建議在Fragment
基礎父類中進行處理,通過這種方式你也可以自己呼叫disableAnimation
介面控制回退動畫的播放。
以上幾個操作已經完全可以正常使用滑動關閉了。可是,你可能還有一些特殊的需求,下面我通過QA的方式來模擬六種使用場景,如果還有沒有涵蓋的使用場景,請在評論下方告訴我!
-
場景一:如果我想對某個特殊的頁面禁用滑動關閉,需要怎麼做?
答:很簡單,註解EnableDragToClose中可以接受一個Boolean
引數,用於控制滑動關閉功能的開啟或關閉。因此,你只需要這樣做就可以了!
@EnableDragToClose(false)
public class FirstActivity extends BaseActivity {
-
場景二:如果我想對某個頁面動態開啟或關閉,換句話說,檢測到某些特殊情況開啟滑動關閉功能,某些情況下禁用滑動關閉功能,註解做不到吧?
Snake
可以做到嗎?
答:Snake已經為你考慮到了這一點,我們提供了介面enableDragToClose(Activity activity, boolean enable)
,Fragment
引數也有同名介面。因此,你只需要這樣做:
if(條件a) {
Snake.enableDragToClose(this, true);
} else if(條件b) {
Snake.enableDragToClose(this, false);
}
-
場景三:想象這樣一種場景,假設頁面中有視訊正在播放,我想在滑動開始後關閉視訊播放,結束播放繼續,可以做到嗎?
答:當然可以!為了滿足你對滑動過程的控制,Snake提供了**addDragListener(Activity activity, Snake.OnDragListener onDragListener) **介面,用於監聽整個滑動過程的。具體監聽實現,看這裡:
public static abstract class OnDragListener {
public void onDragStart(View view) {}
public void onDrag(View view, int left) {}
public void onRelease(View view, float xVelocity) {}
}
-
場景四:如果在某些特殊場景下,頁面滑動事件出現了衝突,是否有辦法補救?
答:可以!為了防止在某些特殊場景下,滑動事件出現衝突,Snake
提供了介面public static void setCustomTouchInterceptor(@NonNull Activity activity, SnakeTouchInterceptor interceptor)
實現對事件攔截的自定義處理。通常情況下,你不需要理會這個方法,如果確實發現是因為事件衝突問題導致了使用異常,才需要使用這個方法完成輔助處理。 -
場景五:關於Fragment,我看到你使用了Snake.newProxy進行了例項的建立。可是,如果我有多個構造方法呢?或者說,我沒有提供預設構造方法實現,怎麼辦?這種場景可以處理嗎?
答:當然可以!為了滿足對多構造器例項建立的處理,Snake提供了一個新的註解@PrimaryConstructor
用於指定主構造器,指定後,Snake會以這個構造器建立Fragment例項,但這裡要求你傳入構造器所需的引數,類似這樣:
Fragment fragment = Snake.newProxy(xx.class, objA, objB);
-
場景五:說了這麼多,你似乎忽略了一個主流問題!如果我想對滑動的樣式進行設定,怎麼辦?難道不允許嗎?
答:這當然也是允許的,不過不推薦你進行自定義設定,因為預設樣式已經完全可以滿足需求了。但如果你一定要修改,我也不反對。如果你要對全域性進行樣式修改,我們提供了snake.xml配置檔案對樣式進行設定。
<?xml version="1.0" encoding="utf-8"?>
<snake>
<config>
<!-- 設定為true,根Activity也能夠滑動關閉,這很奇怪!不建議修改這個變數的預設值 -->
<enable_for_root_activity>false</enable_for_root_activity>
<!-- 設定為true,將監聽當前頁面所有位置往右快速滑動手勢 -->
<only_listen_to_fast_swipe>false</only_listen_to_fast_swipe>
<!-- 快速滑動最低檢測速度,不建議修改。過高會影響靈敏度,過低會導致誤判 -->
<min_velocity>2000</min_velocity>
<!-- 設定為true,滑動時左側邊緣陰影將被隱藏, 這個變數的預設值也不建議修改 -->
<hide_shadow_of_edge>false</hide_shadow_of_edge>
<!-- 陰影邊緣漸變色起始顏色 -->
<shadow_start_color>#00000000</shadow_start_color>
<!-- 陰影邊緣漸變色結束顏色 -->
<shadow_end_color>#55000000</shadow_end_color>
</config>
</snake>
如果要對單個頁面進行滑動樣式修改,我們提供了@SetDragParameter註解:
/**
* 僅監聽快速滑動關閉
*/
boolean onlyListenToFastSwipe() default false;
/**
* 快速滑動最小檢測速度
*/
int minVelocity() default 2000;
/**
* 隱藏陰影邊緣
*/
boolean hideShadowOfEdge() default false;
/**
* 陰影起始顏色(陰影邊緣隱藏後,該設定失效)
*/
String shadowStartColor() default "#00000000";
/**
* 陰影邊緣結束顏色(陰影邊緣隱藏後,該設定失效)
*/
String shadowEndColor() default "#50000000";
注意:xml配置檔名必須是snake.xml,配置檔案應放置在assets目錄下面。
場景六:針對單個頁面逐一去開啟滑動關閉功能實在太麻煩了,是否可以針對所有頁面開啟滑動關閉功能呢?
答:當然可以!首先,針對Activity
和Fragment
你需要有一個統一的基類,使用方式與上文介紹完全一致。如果是Activity,先添加註解@EnableDragToClose
,然後在onCreate
方法中使用host
介面對其進行託管,像這樣:
@EnableDragToClose()
public class BaseActivity extends Activity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Snake.host(this);
}
}
如果是Fragment,同樣地,先添加註解@EnableDragToClose
,然後在跳轉到Fragment的時候使用Snake.newProxy
或Snake.newProxySupport
建立Fragment例項即可。
使用總結
為了讓你對整個使用過程有一個更直觀的瞭解,我用一個簡單的表格來給你展示具體使用方法:
使用步驟 | Activity | android.app.Fragment | android.support.v4.app.Fragment |
---|---|---|---|
第一步 |
添加註解@EnableDragToClose |
同Activity | 同Activity |
第二步 |
託管: Snake.host(this) |
使用Snake.newProxy 建立例項 |
使用Snake.newProxySupport 建立例項 |
第三步 |
startActivity 和finish 中使用overridePendingTransition 匯入Snake 內建動畫 |
實現介面SnakeAnimationController ,重寫onCreateAnimation 或onCreateAnimator 方法,使用Snake.wrap 包裹父類實現並返回 |
同android.app.Fragment |
第四步 | ---- |
使用setCustomAnimations 匯入Snake 內建動畫 |
同android.app.Fragment |
以上步驟省略了Snake在Application中初始化以及匯入依賴的過程,詳細步驟可以參考 Github
簡單說明
這裡容易混淆的一個概念就是:註解@EnableDragToClose
和Snake.enableDragToClose
介面。其實,這兩個部分是有一定的區別的,註解是用於標記當前頁面是否需要開啟滑動關閉功能,如果需要開啟,註解始終需要。但如果你希望當前頁面始終關閉【滑動關閉】功能,你可以在註解中傳入引數false
,而如果你希望動態控制【滑動關閉】功能的開啟或關閉,則需要使用Snake.enableDragToClose
介面來控制。如果你沒有添加註解到類頭,直接使用介面開啟滑動關閉功能,將會報錯。
Snake版本相容問題
Snake 0.1.0
不相容0.0.6
極其以前版本,如果你使用了舊版本實現,推薦暫時不要替換,在新的APP中強烈推薦使用0.1.0
極其以上版本。
Android系統版本相容問題
由於Android系統的一些限制,在低於21版本Activity滑動聯動的實現上有一些問題。為了避免這個問題,我在低版本的使用上禁用了拖拽,僅使用快速滑動關閉當前頁面。這裡我推薦使用全Fragment
或Activity
+ 多Fragment
設計。
新版本規劃
-
增加更多控制介面
-
提供WebView和X5WebView滑動關閉的支援
特別鳴謝
感謝 言小諾01 同學提供圖示設計。
PS:走過路過,不要錯過,點個贊,不要錢哦 -_-
想要學習更多開發知識,請掃描下方二維碼關注【歐陽鋒工作室】
想要獲得更好閱讀體驗,請 閱讀原文