抓住人生中的一分一秒,胜过虚度中的一月一年!
背景
用过苹果手机的都知道,苹果没有物理返回键,原生自带侧滑回退页面api
,手势操控起来很方便,但是Android
去实现较为困难,现微信、今日头条等app
各自都实现了侧滑返回,于是也去研究了下如何实现,目前GitHub
上有很多开源的框架,有更好的轮子那必须用轮子了,但实现还是需要注意一些东西事项,下面给大家讲解下如何正确去实现侧滑回退功能
有很多类似的开源框架 暂举五个
先看一个效果图
原理分析
侧滑看似顶层Activity
整体向右移动,然而并不是这样的,android
不支持俩个页面联动效果,所以我们得想方设法在一个View
中看到低层布局,和顶层布局俩个画面,才能去做这种效果,实现方案有俩种
不透明方案
在顶层Activity的DecorView
中插入一个Layout。监听侧滑事件,移动顶层Activity的ContentView
同时,在该Layout的onDraw
中调用View.draw(Canvas canvas)
绘制下层Activity
的ContentView
。造成侧滑透视到下层Activity
的假象。
存在问题:当布局变化或数据更新,如横竖屏切换、导航栏隐藏、窗口模式、分屏模式等,该假象始终如一不会有对应改变
透明方案
设置顶层Activity
透明
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowIsTranslucent">true</item>
然后监听侧滑事件,移动顶层Activity的ContentView
,即可真正透视到下层Activity
的界面。此时无论布局变化、数据更新,都没问题。BUT!该方案问题多如牛毛。。。
存在问题:windowIsTranslucent
为true
会引起一系列的动画问题,如前后台切换动画、Activity
回退动画等。网上有解决方案说设置"android:windowEnterAnimation"
和"android:windowExitAnimation"
,经测试并无卵用。同时,在SDK26(Android8.0)
及以上,会与固定屏幕方向冲突造成闪退。同时,下层的Activity
只会进入onPause
状态,不会onStop
,等等问题
下面来说明下本人如何去实现了这个效果,以第二个Slidr
来演示如何实现,有能力的朋友可以自己写一个侧滑功能,用其他框架遇到下述类似情况可以借签处理方案
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>
如此简单就实现了...,是不是很简单,先别高兴太早了,这只是第一步,
在SDK26(Android8.0)
及以上页面同时设置了android:screenOrientation="portrait"
和透明属性,运行会出现Only fullscreen opaque activities can request orientation
异常,大概意思为“只有不透明的全屏activity可以自主设置界面方向”
,这样说明Android8.0
以上透明属性和强制竖屏俩个只能取其一?现在的APP无特殊需求根本没必要需要横竖屏切换,只有竖屏效果,这该怎么办?后来经过很长时间尝试并终于解决了此问题
4、初探fullUser
来实现强制竖屏(fullUser
功能:允许使用用户的任意方向 。自动旋转打开:四个方向 。自动旋转关闭:不旋转)
一般情况下强制竖屏我们都会这样写
<activity
android:name=".MainActivity"
android:screenOrientation="portrait" />
或者
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);//设置竖屏模式
其实我们设置android:screenOrientation="fullUser"
,具体详细大家可以自行百度下
所以我们将portrait
替换成fullUser
解决了上边遗留下来的问题奔溃问题,SDK26(Android8.0)
不再崩溃报异常,但是经过机型测试,偶然间发现小米一款红米手机强制竖屏效果没适配,任然可以横竖屏切换,那。。。,此方案就此作废
5、最终behind
来实现强制竖屏(behind
功能:与Activity堆下的Activity方向相同 。自动旋转打开:四个方向 。自动旋转关闭:不旋转)
behind
此属性说白了讲是说与上个页面屏幕旋转方向相同,这样我们便可以逻辑转换去思考下,第一个Activity
设置强制竖屏,第二个页面设置跟随第一个屏幕方向属性behind
,首页面MainActivity
,登陆页LoginActivity
,闪屏页SplashActivity
都不需要实现侧滑,我们只给它们设置强制竖屏"portrait"
,不设置透明属性,因为这几个页面不需要侧滑功能,这样便可避免了俩者共存,经过多方面测试,确实可以这样,暂时没发现问题
6、一些根本不需要实现侧滑finish的页面不设置透明属性android:windowIsTranslucent
4.1也讲了,再详细说一下,比如LoginActivity
,MainActivity
等打开App第一个显示的页面其实没必要具有侧滑功能,上述我们是在全局主题AppTheme
加的支持透明属性android:windowIsTranslucent">true
,
所以应当修改为需要侧滑的页面增加该透明属性,不需要侧滑的页面不设置android:windowIsTranslucent
透明属性,于是乎需要俩个主题
//主题属性 全局状态 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>
//不需要侧滑页面增加该theme
<style name="AppTheme.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
</style>
//需要侧滑页面增加该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">
//不需要实现侧滑的页面
<activity
android:name=".ui.activity.MainActivity"
android:screenOrientation="portrait"
android:theme="@style/AppTheme.NoActionBar" />
//需要实现侧滑的页面
<activity
android:name=".ui.login.ForgetPwdActivity"
android:screenOrientation="behind"
android:theme="@style/AppTheme.NoActionBar.Slidable" />
最后一步,每个第三方库一般都会扩展开放是否支持侧滑finish
页面接口,我们将不需要侧滑页面设置成true
比如MainActivity
中重写BaseActivity
中initSlidable
方法,禁止初始化侧滑属性
@Override
protected void initSlidable() {
// 禁止滑动返回
}
7、侧滑状态栏跟随侧滑页面一起移动
这回运行完美,能正常使用,竖屏效果和透明效果Android
版本已兼容了,但是还会发现有点怪的地方是状态栏
,侧滑后页面变了,状态栏还有那么一横条,太难看了,想到了设置统一的一个透明或者灰色,但是还是难看,如何能够做到侧滑状态栏跟随侧滑页面一起移动呢?
思路:状态栏可以设置颜色,也可以设置透明隐藏
所以不就简单了?,将状态栏隐藏掉,页面布局整体顶到状态栏上,顶部给一个状态栏高度padding
,为了版本兼容问题,api
小于19
不支持沉浸式,所以可以判断版本>=19
给整体页面一个paddingTop=
状态栏高度
不就实现了侧滑状态栏跟随侧滑页面一起移动?
/**
* 获取状态栏高度
*
* @param context context
* @return 状态栏高度
*/
public static int getStatusBarHeight(Context context) {
// 获得状态栏高度
int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
return context.getResources().getDimensionPixelSize(resourceId);
}
/**
* 为布局文件中新增的状态栏布局设置背景色和高度
*/
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,增加的值为状态栏高度 (智能判断,并设置高度)
*/
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图片,想实现的朋友可以尝试一下,但是还遗留下几个问题,但不影响整体效果
问题1:设置的跳转页面动画效果不起作用
在整体AppTheme中设置<item name="android:windowAnimationStyle">@style/activityAnimation</item>
比如,左进右出
<!--页面打开关闭动画-->
<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>
解决:可以更改一种实现动画方式
@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()不执行?
由于被设置了<item name="android:windowIsTranslucent">true</item>
的Activity
无法进入onStop()
生命周期,所以导致Activity
的Window
无法回收,所以在多个Activity
叠加时会出现明显的卡顿现象,目前并没有特别好的解决办法。
但是本人打印了下日志不管侧滑返回,物理键返回,onStop
都有日志,大家可以测试下,本文章再继续完善
问题3:最初选用android:screenOrientation="fullUser"
来实现固定方向,但是暂时发现小米手机不适配
最终采用behind
来实现固定页面方向,behind
此属性说白了讲是说与上个页面屏幕旋转方向相同,这样我们便可以逻辑转换去思考下,第一个Activity设置强制竖屏,第二个页面设置跟随第一个屏幕方向属性behind
,首页面MainActivity
,登陆页LoginActivity
,闪屏页SplashActivity
都不需要实现侧滑,我们只给它们设置强制竖屏"portrait",不设置透明属性,因为这几个页面不需要侧滑功能,这样便可避免了俩者共存,经过多方面测试,确实可以这样,暂时没发现问题
问题4:后续继续补充
最后补充
现侧滑已应用到我的项目中,持续踩坑中,本所有优化是基于 Slidr库所操作,其他开源库有类似问题可以借签上述处理,有能力的朋友可以自己写个侧滑功能,最后的最后建议用SwipeBackLayout库,已经比较成熟,需要处理的问题少
祝大家开发愉快!