Android折叠屏适配基于AutoSize框架适配折叠屏并兼容多窗口模式
Posted 一碗碗碗
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android折叠屏适配基于AutoSize框架适配折叠屏并兼容多窗口模式相关的知识,希望对你有一定的参考价值。
【android折叠屏适配】基于AutoSize框架适配折叠屏并兼容多窗口模式
问题背景
当前最新的Android API 33对大屏设备的支持已较为完善,结合Jetpack Compose等响应式布局可实现一次适配所有屏幕。但考虑到代码往往是不能立即重构的,想要利用存量代码适配层出不穷的大屏及可折叠设备,并兼容多窗口场景,可采用Android界大名鼎鼎的AutoSize框架配合2套设计图尺寸来完成。
AutoSize框架基本原理
AutoSize框架支持5种单位 ,包括dp、sp、pt、in、mm,其中dp及sp为AutoSize框架的主单位。开发过程中可开启或关闭对某一单位的支持,使用主单位+副单位或只使用副单位进行适配都是可以的。选择哪一个副单位,撰写layout时就使用该单位即可。
AutoSize适配屏幕的核心原理为通过WindowManager拿到DisplayMetrics中的屏幕宽高(单位为px),再结合设置的设计图宽高(单位为px)计算并全局设置density(细节可查阅AutoSize框架源码)。
在Application及BaseActivity中进行设置
考虑到折叠屏开合时具有2组不同的屏幕宽高,我们针对其设置2套设计图宽高用以正确计算density。由于具有2套设计图尺寸,在AndroidManifest中设置设计图宽高不可行,需在Application onCreate()中计算当前屏幕宽高比及window宽高比手动设置设计图宽高,代码如下。
此外,在屏幕折叠及旋转场景下,从registerComponentCallbacks的onConfigurationChanged()回调中拿到的屏幕宽高有时不准确,尚未定位到原因(增加时延也并不稳定),故需在封装的BaseActivity的onConfigurationChanged()生命周期中也加入如下处理。
/*注册屏幕适配监听器,适配前后均会回调此方法*/
AutoSizeConfig.getInstance().setOnAdaptListener(new onAdaptListener()
@Override
public void onAdaptBefore(Object target, Activity activity)
if (activity == null)
return;
int[] windowSize = new int[2];
windowSize[0] = activity.getResources().getDisplayMetrics().widthPixels;
windowSize[1] = activity.getResources().getDisplayMetrics().heightPixels;
AutoSizeConfig.getInstance()
.setCustomFragment(true)
.getUnitsManager()
.setSupportSP(false)
.setSupportDP(false)
.setSupportSubunits(Subunits.MM);
/*判断屏幕宽高比是否为大屏*/
if (FoldScreenUtil.isLargeScreen(activity))
/*判断window宽高比是否为大窗口*/
if (FoldScreenUtil.isLargeWindow(activity))
Log.i("isLargeScreen", "isLargeWindow");
/*判断横竖屏*/
if (activity.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE)
/*大屏-大窗口-横屏设计尺寸*/
AutoSizeConfig.getInstance()
.getUnitsManager()
.setDesignWidth(windowSize[0])
.setDesignHeight(windowSize[1])
.setDesignSize(1556, 1395);
else
/*大屏-大窗口-竖屏设计尺寸*/
AutoSizeConfig.getInstance()
.getUnitsManager()
.setDesignWidth(windowSize[0])
.setDesignHeight(windowSize[1])
.setDesignSize(1395, 1556);
else
if (activity.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE)
/*大屏-小窗口-横屏设计尺寸*/
AutoSizeConfig.getInstance()
.getUnitsManager()
.setDesignWidth(windowSize[0])
.setDesignHeight(windowSize[1])
.setDesignSize(1556, 750);
else
/*大屏-小窗口-竖屏设计尺寸*/
AutoSizeConfig.getInstance()
.getUnitsManager()
.setDesignWidth(windowSize[0])
.setDesignHeight(windowSize[1])
.setDesignSize(750, 1556);
else
/*小屏通常禁止旋转,仅设置竖屏设计尺寸即可*/
/*特殊情况:平行视窗下只能拿到当前activity的显示区域宽高,会被判定为小屏*/
AutoSizeConfig.getInstance()
.getUnitsManager()
.setDesignWidth(windowSize[0])
.setDesignHeight(windowSize[1])
.setDesignSize(750, 1556);
@Override
public void onAdaptAfter(Object target, Activity activity)
);
注意:多窗口场景下Activity window与屏幕不等价
多窗口场景下(如平行视窗、分屏及悬浮窗等),window大小和屏幕大小并不等价,若在多窗⼝模式下仍是以屏幕的宽⾼进⾏适配,则density一定是不正确的值。而AutoSize框架中获取的宽高为屏幕宽高,因此,需利用Activity上下文拿到窗口大小并手动设置。
经测试,平行视窗较为特殊,拿到的屏幕宽高为当前activity显示的区域宽高。
可参考如下方法判断当前window是否为大窗口状态。
public static boolean isLargeWindow(Activity activity)
if (activity == null)
return false;
float longSide = 0;
float shortSide = 0;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)
WindowMetrics windowMetrics = activity.getWindowManager().getCurrentWindowMetrics();
Rect windowRect = windowMetrics.getBounds();
longSide = Math.max(windowRect.width(), windowRect.height());
shortSide = Math.min(windowRect.width(), windowRect.height());
else
int widthPixels = activity.getResources().getDisplayMetrics().widthPixels;
int heightPixels = activity.getResources().getDisplayMetrics().heightPixels;
longSide = Math.max(widthPixels, heightPixels);
shortSide = Math.min(widthPixels, heightPixels);
boolean isLargeWindow = longSide / shortSide < MIN_ASPECT;
Log.i("isLargeWindow", "longSide" + longSide + "shortSide" + shortSide);
return isLargeWindow;
在item的getView()或onBindViewHolder中进行设置
由于GridView、RecyclerView等组件存在item复用机制,屏幕开合或旋转时,item宽高可能不正确。在getView()中加入AutoSize.autoConvertDensityOfGlobal(activity)手动设置density可解决此问题。
FlutterAndroidFlutter 折叠屏适配 ( 展开大屏 | 折叠主屏 | 折叠副屏 | 静态展示 | 动态热切换适配 | 拉伸布局 | X 轴自适应适配 | 布局重构 )
文章目录
一、Android、Flutter 折叠屏适配
华为的 Mate X 折叠屏有 3 3 3 种形态 :
- 展开形态 ( 大屏 ) : 屏幕宽高比 8 : 7.1 8:7.1 8:7.1 , 近似于正方形 ; 分辨率为 2480 × 2200 2480 \\times 2200 2480×2200 ;
- 折叠形态 ( 主屏 ) : 屏幕宽高比 19.5 : 9 19.5:9 19.5:9 , 这是个全面屏 , 就是手机正面 ; 分辨率为 2480 × 1148 2480 \\times 1148 2480×1148 ;
- 折叠形态 ( 副屏 ) : 屏幕宽高比 25 : 9 25:9 25:9 , 这是屏幕背面 , 这一面比较窄 ; 分辨率为 2480 × 892 2480 \\times 892 2480×892 ;
二、展开大屏适配
屏幕展开后 , 处于屏幕宽高比 8 : 7.1 8:7.1 8:7.1 状态下 , 屏幕要完全填充整个屏幕 , 如下图的 A 的样式 ;
B 中左右出现的黑边 , C 中上下出现黑边 , D 中四周出现黑边 , 都不能出现 ;
三、折叠主屏适配
折叠主屏 就按照 全面屏的样式进行适配 ;
折叠状态下 , 主屏要填充满整个屏幕 , 如 A 样式 ;
B 中左右两遍留出空白 , C 中下面留出空白 , 不可取 ;
四、折叠副屏适配
折叠副屏 的 屏幕分辨率是 25 : 9 25:9 25:9 , 是长条形的 ;
华为官方建议 , 该屏幕不以全屏显示 , 在副屏模式下 , 仍然保持 19.5 : 9 19.5 : 9 19.5:9 的屏幕适配即可 , 如下图的 A ;
折叠状态下的副屏 , 不能完全填充 , 以及以其它宽高比填充 , 只能以 19.5 : 9 19.5 : 9 19.5:9 的比例填充 ;
下图中 , 只有 A 是合格的 , 其它都不符合规范 ;
五、折叠屏动态热切换适配
上述折叠屏的三种形态 , 在任何一种形态 , 打开应用 , 都按照对应的适配要求显示 ;
假如再打开后 , 屏幕形态切换 , 就需要自动切换屏幕样式 ;
如 : 当前是 折叠状态主屏 打开应用 ( 此时宽高比
19.5
:
9
19.5:9
19.5:9 ) , 突然将手机打开开 , 变为 展开状态大屏 ( 此时宽高比
8
:
7.1
8:7.1
8:7.1 ) , 官方要求应用的布局要实时切换成
8
:
7.1
8:7.1
8:7.1 的布局样式 ;
大厂的大应用 , 可以考虑适配一下 ;
个人感觉一般的应用 , 只要符合静态打开的要求就可以 , 切换状态后 , 直接关掉应用重启就可以 ;
五、拉伸布局
主屏 , 副屏 , 大屏 三种状态 , 只显示一种布局 , 直接将布局填充满整个界面 , 大屏状态下直接拍扁了 ;
这种布局比较难看 , 适合初期发布应用时进行这种适配 ;
六、X 轴自适应适配
主屏 , 副屏 , 大屏 三种状态 , Y 轴实际上是没有变化的 , 高度基本不变 , 大屏 相对于 主屏 和 副屏 , 只是 X 轴 变宽了 , 这里组件的 Y 轴元素可以不变 , 将 X 轴的元素进行横向自适应改变 ;
如下图的两个界面 , 左侧是 主屏 , 副屏 , 右侧是 大屏 , 右侧的 UI 布局与左侧进行比较 , Y 坐标不变 , X 坐标根据屏幕宽度自适应变化 ;
七、布局重构
屏幕变宽之后 , 设置不同的布局 ;
主屏 , 副屏 , 使用一套布局 ;
大屏状态下 , 使用另外一套布局 ;
这种开发代价较大 , 一般 Web 开发可以使用这种布局样式 ;
八、Android、Flutter 中的程序配置
1、屏幕自适应配置
在 AndroidManifest.xml 的清单文件中 设置 activity 或 application 的 android:resizeableActivity 属性为 true ;
配置示例 :
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.flutter_screen_adaption">
<application
android:label="flutter_screen_adaption"
android:resizeableActivity="true"
android:icon="@mipmap/ic_launcher">
</application>
</manifest>
2、设置切换屏蔽宽高比不重启适配
在 AndroidManifest.xml 的清单文件中 的 activity 节点配置 android:configChanges="screenSize|orientation|smallestScreenSize"
属性 ;
Flutter 给默认配置好了 :
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.flutter_screen_adaption">
<application
android:label="flutter_screen_adaption"
android:resizeableActivity="true"
android:icon="@mipmap/ic_launcher">
<activity
android:name=".MainActivity"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
</activity>
</application>
</manifest>
3、设置最大最小屏幕比例
设置最大宽高比 : 在 AndroidManifest.xml 的清单文件中 的 application 节点下配置
<meta-data
android:name="android.max_aspect"
android:value="2.1"/>
最值最小宽高比 : 在 AndroidManifest.xml 的清单文件中 的 application 节点下配置
<meta-data android:name="android.min_aspect"
android:value="1.0" />
完整配置如下 :
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.flutter_screen_adaption">
<application
android:label="flutter_screen_adaption"
android:resizeableActivity="true"
android:icon="@mipmap/ic_launcher">
<activity
android:name=".MainActivity"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
</activity>
<!-- 添加 Android 可以适配的最大宽高比为 2.1 : 1 , 适配全面屏添加 -->
<meta-data
android:name="android.max_aspect"
android:value="2.1"/>
<meta-data android:name="android.min_aspect"
android:value="1.0" />
</application>
</manifest>
以上是关于Android折叠屏适配基于AutoSize框架适配折叠屏并兼容多窗口模式的主要内容,如果未能解决你的问题,请参考以下文章