1. 程式人生 > >使用Snake,安卓也能輕鬆實現類iOS滑動關閉功能

使用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.Fragmentme.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增加了wrapSnakeAnimationController兩個介面,通過這兩個介面聯合使用就可以避免這個問題。

具體要怎麼做呢?看這裡:

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的方式完全一致,Snakewrap介面也可以適用。

注意:這個操作強烈建議在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目錄下面。

場景六:針對單個頁面逐一去開啟滑動關閉功能實在太麻煩了,是否可以針對所有頁面開啟滑動關閉功能呢?
答:當然可以!首先,針對ActivityFragment你需要有一個統一的基類,使用方式與上文介紹完全一致。如果是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.newProxySnake.newProxySupport建立Fragment例項即可。

使用總結

為了讓你對整個使用過程有一個更直觀的瞭解,我用一個簡單的表格來給你展示具體使用方法:

使用步驟 Activity android.app.Fragment android.support.v4.app.Fragment
第一步 添加註解@EnableDragToClose 同Activity 同Activity
第二步 託管: Snake.host(this) 使用Snake.newProxy建立例項 使用Snake.newProxySupport建立例項
第三步 startActivityfinish中使用overridePendingTransition匯入Snake內建動畫 實現介面SnakeAnimationController,重寫onCreateAnimationonCreateAnimator方法,使用Snake.wrap包裹父類實現並返回 android.app.Fragment
第四步 ---- 使用setCustomAnimations匯入Snake內建動畫 android.app.Fragment

以上步驟省略了SnakeApplication中初始化以及匯入依賴的過程,詳細步驟可以參考 Github

簡單說明

這裡容易混淆的一個概念就是:註解@EnableDragToCloseSnake.enableDragToClose介面。其實,這兩個部分是有一定的區別的,註解是用於標記當前頁面是否需要開啟滑動關閉功能,如果需要開啟,註解始終需要。但如果你希望當前頁面始終關閉【滑動關閉】功能,你可以在註解中傳入引數false,而如果你希望動態控制【滑動關閉】功能的開啟或關閉,則需要使用Snake.enableDragToClose介面來控制。如果你沒有添加註解到類頭,直接使用介面開啟滑動關閉功能,將會報錯。

Snake版本相容問題

Snake 0.1.0不相容0.0.6極其以前版本,如果你使用了舊版本實現,推薦暫時不要替換,在新的APP中強烈推薦使用0.1.0極其以上版本。

Android系統版本相容問題

由於Android系統的一些限制,在低於21版本Activity滑動聯動的實現上有一些問題。為了避免這個問題,我在低版本的使用上禁用了拖拽,僅使用快速滑動關閉當前頁面。這裡我推薦使用全FragmentActivity + 多Fragment設計。

新版本規劃

  • 增加更多控制介面

  • 提供WebView和X5WebView滑動關閉的支援

特別鳴謝

感謝 言小諾01 同學提供圖示設計。

PS:走過路過,不要錯過,點個贊,不要錢哦 -_-

想要學習更多開發知識,請掃描下方二維碼關注【歐陽鋒工作室】


想要獲得更好閱讀體驗,請 閱讀原文