抓住人生中的一分一秒邻吭,勝過虛度中的一月一年!
背景
用過蘋果手機(jī)的都知道,蘋果沒有物理返回鍵,原生自帶側(cè)滑回退頁面api
盛险,手勢操控起來很方便,但是Android
去實現(xiàn)較為困難勋又,現(xiàn)微信苦掘、今日頭條等app
各自都實現(xiàn)了側(cè)滑返回,于是也去研究了下如何實現(xiàn)赐写,目前GitHub
上有很多開源的框架鸟蜡,有更好的輪子那必須用輪子了,但實現(xiàn)還是需要注意一些東西事項挺邀,下面給大家講解下如何正確去實現(xiàn)側(cè)滑回退功能
有很多類似的開源框架 暫舉五個
先看一個效果圖
原理分析
側(cè)滑看似頂層Activity
整體向右移動揉忘,然而并不是這樣的,android
不支持倆個頁面聯(lián)動效果端铛,所以我們得想方設(shè)法在一個View
中看到低層布局泣矛,和頂層布局倆個畫面,才能去做這種效果禾蚕,實現(xiàn)方案有倆種
不透明方案
在頂層Activity的DecorView
中插入一個Layout您朽。監(jiān)聽側(cè)滑事件,移動頂層Activity的ContentView
同時换淆,在該Layout的onDraw
中調(diào)用View.draw(Canvas canvas)
繪制下層Activity
的ContentView
哗总。造成側(cè)滑透視到下層Activity
的假象。
存在問題:當(dāng)布局變化或數(shù)據(jù)更新倍试,如橫豎屏切換讯屈、導(dǎo)航欄隱藏、窗口模式县习、分屏模式等涮母,該假象始終如一不會有對應(yīng)改變
透明方案
設(shè)置頂層Activity
透明
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowIsTranslucent">true</item>
然后監(jiān)聽側(cè)滑事件谆趾,移動頂層Activity的ContentView
,即可真正透視到下層Activity
的界面叛本。此時無論布局變化沪蓬、數(shù)據(jù)更新,都沒問題来候。BUT跷叉!該方案問題多如牛毛。营搅。性芬。
存在問題:windowIsTranslucent
為true
會引起一系列的動畫問題,如前后臺切換動畫剧防、Activity
回退動畫等植锉。網(wǎng)上有解決方案說設(shè)置"android:windowEnterAnimation"
和"android:windowExitAnimation"
,經(jīng)測試并無卵用峭拘。同時俊庇,在SDK26(Android8.0)
及以上,會與固定屏幕方向沖突造成閃退鸡挠。同時辉饱,下層的Activity
只會進(jìn)入onPause
狀態(tài),不會onStop
拣展,等等問題
下面來說明下本人如何去實現(xiàn)了這個效果彭沼,以第二個Slidr
來演示如何實現(xiàn),有能力的朋友可以自己寫一個側(cè)滑功能备埃,用其他框架遇到下述類似情況可以借簽處理方案
1姓惑、引入第三方庫
implementation 'com.r0adkll:slidableactivity:2.0.6'
2、在BaseActivity的onCreate中初始化一下就可以了
protected void initSlidable() {
SlidrConfig config = new SlidrConfig.Builder()
.edge(false)//true 代表邊界 false全屏觸摸
.build();
slidrInterface = Slidr.attach(this, config);
}
3按脚、在AppTheme中加入支持透明屬性
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowIsTranslucent">true</item>
如此簡單就實現(xiàn)了...于毙,是不是很簡單,先別高興太早了辅搬,這只是第一步唯沮,
在SDK26(Android8.0)
及以上頁面同時設(shè)置了android:screenOrientation="portrait"
和透明屬性,運行會出現(xiàn)Only fullscreen opaque activities can request orientation
異常堪遂,大概意思為“只有不透明的全屏activity可以自主設(shè)置界面方向”
介蛉,這樣說明Android8.0
以上透明屬性和強(qiáng)制豎屏倆個只能取其一?現(xiàn)在的APP無特殊需求根本沒必要需要橫豎屏切換溶褪,只有豎屏效果币旧,這該怎么辦?后來經(jīng)過很長時間嘗試并終于解決了此問題
4竿滨、初探fullUser
來實現(xiàn)強(qiáng)制豎屏(fullUser
功能:允許使用用戶的任意方向 佳恬。自動旋轉(zhuǎn)打開:四個方向 。自動旋轉(zhuǎn)關(guān)閉:不旋轉(zhuǎn))
一般情況下強(qiáng)制豎屏我們都會這樣寫
<activity
android:name=".MainActivity"
android:screenOrientation="portrait" />
或者
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);//設(shè)置豎屏模式
其實我們設(shè)置android:screenOrientation="fullUser"
于游,具體詳細(xì)大家可以自行百度下
所以我們將portrait
替換成fullUser
解決了上邊遺留下來的問題奔潰問題毁葱,SDK26(Android8.0)
不再崩潰報異常,但是經(jīng)過機(jī)型測試贰剥,偶然間發(fā)現(xiàn)小米一款紅米手機(jī)強(qiáng)制豎屏效果沒適配倾剿,任然可以橫豎屏切換,那蚌成。前痘。。担忧,此方案就此作廢
5芹缔、最終behind
來實現(xiàn)強(qiáng)制豎屏(behind
功能:與Activity堆下的Activity方向相同 。自動旋轉(zhuǎn)打開:四個方向 瓶盛。自動旋轉(zhuǎn)關(guān)閉:不旋轉(zhuǎn))
behind
此屬性說白了講是說與上個頁面屏幕旋轉(zhuǎn)方向相同最欠,這樣我們便可以邏輯轉(zhuǎn)換去思考下,第一個Activity
設(shè)置強(qiáng)制豎屏惩猫,第二個頁面設(shè)置跟隨第一個屏幕方向?qū)傩?code>behind芝硬,首頁面MainActivity
,登陸頁LoginActivity
,閃屏頁SplashActivity
都不需要實現(xiàn)側(cè)滑轧房,我們只給它們設(shè)置強(qiáng)制豎屏"portrait"
拌阴,不設(shè)置透明屬性,因為這幾個頁面不需要側(cè)滑功能奶镶,這樣便可避免了倆者共存迟赃,經(jīng)過多方面測試,確實可以這樣厂镇,暫時沒發(fā)現(xiàn)問題
6捺氢、一些根本不需要實現(xiàn)側(cè)滑finish的頁面不設(shè)置透明屬性android:windowIsTranslucent
4.1也講了,再詳細(xì)說一下剪撬,比如LoginActivity
摄乒,MainActivity
等打開App第一個顯示的頁面其實沒必要具有側(cè)滑功能,上述我們是在全局主題AppTheme
加的支持透明屬性android:windowIsTranslucent">true
残黑,
所以應(yīng)當(dāng)修改為需要側(cè)滑的頁面增加該透明屬性馍佑,不需要側(cè)滑的頁面不設(shè)置android:windowIsTranslucent
透明屬性,于是乎需要倆個主題
//主題屬性 全局狀態(tài) Application中加入
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<!-- Customize your theme here. -->
<!--<item name="colorPrimary">@color/colorTop</item>-->
<item name="android:windowAnimationStyle">@style/activityAnimation</item>
</style>
//不需要側(cè)滑頁面增加該theme
<style name="AppTheme.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
</style>
//需要側(cè)滑頁面增加該theme
<style name="AppTheme.NoActionBar.Slidable" parent="AppTheme.NoActionBar">
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowBackground">@android:color/transparent</item>
</style>
示例如下梨水,讓大家更好理解
<application
android:name=".App"
android:allowBackup="true"
android:icon="@mipmap/logo"
android:label="@string/app_name"
android:roundIcon="@mipmap/logo"
android:supportsRtl="true"
android:theme="@style/AppTheme">
//不需要實現(xiàn)側(cè)滑的頁面
<activity
android:name=".ui.activity.MainActivity"
android:screenOrientation="portrait"
android:theme="@style/AppTheme.NoActionBar" />
//需要實現(xiàn)側(cè)滑的頁面
<activity
android:name=".ui.login.ForgetPwdActivity"
android:screenOrientation="behind"
android:theme="@style/AppTheme.NoActionBar.Slidable" />
最后一步拭荤,每個第三方庫一般都會擴(kuò)展開放是否支持側(cè)滑finish
頁面接口,我們將不需要側(cè)滑頁面設(shè)置成true
比如MainActivity
中重寫BaseActivity
中initSlidable
方法疫诽,禁止初始化側(cè)滑屬性
@Override
protected void initSlidable() {
// 禁止滑動返回
}
7舅世、側(cè)滑狀態(tài)欄跟隨側(cè)滑頁面一起移動
這回運行完美旦委,能正常使用,豎屏效果和透明效果Android
版本已兼容了雏亚,但是還會發(fā)現(xiàn)有點怪的地方是狀態(tài)欄
,側(cè)滑后頁面變了缨硝,狀態(tài)欄還有那么一橫條,太難看了罢低,想到了設(shè)置統(tǒng)一的一個透明或者灰色查辩,但是還是難看,如何能夠做到側(cè)滑狀態(tài)欄跟隨側(cè)滑頁面一起移動呢网持?
思路:狀態(tài)欄可以設(shè)置顏色宜岛,也可以設(shè)置透明隱藏
所以不就簡單了功舀?辟汰,將狀態(tài)欄隱藏掉莉擒,頁面布局整體頂?shù)綘顟B(tài)欄上涨冀,頂部給一個狀態(tài)欄高度padding
鹿鳖,為了版本兼容問題翅帜,api
小于19
不支持沉浸式涝滴,所以可以判斷版本>=19
給整體頁面一個paddingTop=
狀態(tài)欄高度
不就實現(xiàn)了側(cè)滑狀態(tài)欄跟隨側(cè)滑頁面一起移動歼疮?
/**
* 獲取狀態(tài)欄高度
*
* @param context context
* @return 狀態(tài)欄高度
*/
public static int getStatusBarHeight(Context context) {
// 獲得狀態(tài)欄高度
int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
return context.getResources().getDimensionPixelSize(resourceId);
}
/**
* 為布局文件中新增的狀態(tài)欄布局設(shè)置背景色和高度
*/
public static void setStatusViewAttr(View view, Activity activity) {
if (view == null || activity == null) {
return;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
layoutParams.height = StatusBarUtil.getStatusBarHeight(activity);
view.setLayoutParams(layoutParams);
}
}
/**
* 增加View的paddingTop,增加的值為狀態(tài)欄高度 (智能判斷韩脏,并設(shè)置高度)
*/
public static void setPaddingSmart(Context context, View view) {
if (Build.VERSION.SDK_INT >= 19) {
ViewGroup.LayoutParams lp = view.getLayoutParams();
if (lp != null && lp.height > 0) {
lp.height += getStatusBarHeight(context);//增高
}
view.setPadding(view.getPaddingLeft(), view.getPaddingTop() + getStatusBarHeight(context),
view.getPaddingRight(), view.getPaddingBottom());
}
}
運行一下便是上邊的gif圖片赡矢,想實現(xiàn)的朋友可以嘗試一下,但是還遺留下幾個問題八酒,但不影響整體效果
問題1:設(shè)置的跳轉(zhuǎn)頁面動畫效果不起作用
在整體AppTheme中設(shè)置<item name="android:windowAnimationStyle">@style/activityAnimation</item>
比如梦谜,左進(jìn)右出
<!--頁面打開關(guān)閉動畫-->
<style name="activityAnimation" parent="@android:style/Animation">
<!-- 新的Activity啟動時Enter動畫 -->
<item name="android:activityOpenEnterAnimation">@anim/right_in</item>
<!-- 新的Activity啟動時原有Activity的Exit動畫 -->
<item name="android:activityOpenExitAnimation">@anim/left_out</item>
<!-- 新的Activity退出時原有ActivityEnter動畫 -->
<item name="android:activityCloseEnterAnimation">@anim/left_in</item>
<!-- 新的Activity退出時Exit動畫 -->
<item name="android:activityCloseExitAnimation">@anim/right_out</item>
<item name="android:taskOpenEnterAnimation">@anim/right_in</item>
<item name="android:taskOpenExitAnimation">@anim/left_out</item>
<item name="android:taskCloseEnterAnimation">@anim/left_in</item>
<item name="android:taskCloseExitAnimation">@anim/right_out</item>
<item name="android:taskToFrontEnterAnimation">@anim/right_in</item>
<item name="android:taskToFrontExitAnimation">@anim/left_out</item>
<item name="android:taskToBackEnterAnimation">@anim/left_in</item>
<item name="android:taskToBackExitAnimation">@anim/right_out</item>
</style>
解決:可以更改一種實現(xiàn)動畫方式
@Override
public View onCreateView(String name, Context context, AttributeSet attrs) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
}
return super.onCreateView(name, context, attrs);
}
@Override
public void finish() {
super.finish();
overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
}
問題2:有很多言論說onStop()不執(zhí)行耸棒?
由于被設(shè)置了<item name="android:windowIsTranslucent">true</item>
的Activity
無法進(jìn)入onStop()
生命周期,所以導(dǎo)致Activity
的Window
無法回收,所以在多個Activity
疊加時會出現(xiàn)明顯的卡頓現(xiàn)象,目前并沒有特別好的解決辦法单山。
但是本人打印了下日志不管側(cè)滑返回米奸,物理鍵返回悴晰,onStop
都有日志铡溪,大家可以測試下棕硫,本文章再繼續(xù)完善
問題3:最初選用android:screenOrientation="fullUser"
來實現(xiàn)固定方向,但是暫時發(fā)現(xiàn)小米手機(jī)不適配
最終采用behind
來實現(xiàn)固定頁面方向,behind
此屬性說白了講是說與上個頁面屏幕旋轉(zhuǎn)方向相同袒啼,這樣我們便可以邏輯轉(zhuǎn)換去思考下,第一個Activity設(shè)置強(qiáng)制豎屏蚓再,第二個頁面設(shè)置跟隨第一個屏幕方向?qū)傩?code>behind,首頁面MainActivity
对途,登陸頁LoginActivity
,閃屏頁SplashActivity
都不需要實現(xiàn)側(cè)滑,我們只給它們設(shè)置強(qiáng)制豎屏"portrait"实檀,不設(shè)置透明屬性惶洲,因為這幾個頁面不需要側(cè)滑功能按声,這樣便可避免了倆者共存恬吕,經(jīng)過多方面測試铐料,確實可以這樣,暫時沒發(fā)現(xiàn)問題
問題4:后續(xù)繼續(xù)補充
最后補充
現(xiàn)側(cè)滑已應(yīng)用到我的項目中篓跛,持續(xù)踩坑中膝捞,本所有優(yōu)化是基于 Slidr庫所操作,其他開源庫有類似問題可以借簽上述處理沐寺,有能力的朋友可以自己寫個側(cè)滑功能林艘,最后的最后建議用SwipeBackLayout庫,已經(jīng)比較成熟混坞,需要處理的問題少
祝大家開發(fā)愉快!