学更好的别人,

做更好的自己。

——《微卡智享》

本文长度为5289,预计阅读8分钟

前言

Vivo在4月11号发布的X Fold折叠屏手机,也是抢了好几周好总算拿到手了,既然已经有了折叠屏手机,做为一个开发者,当然也要研究下折叠屏的开发,本篇就先简单介绍一下折叠屏的开发及通过传感器来获取到铰链的折叠角度,针对折叠屏的适配,Android官方推出了Jetpack WindowManager,下一篇会专门介绍WindowManager的折叠屏适配开发。

实现效果

折叠屏开发

微卡智享

2018年8月Google发布Android 9.0,首次支持折叠屏功能,并推出了Android官方折叠屏适配指南。主要从以下几个方面进行适配:

  1. 应用连续性:处理配置变更

  2. 屏幕兼容性:resizeableActivity 与 maxAspectRatio

  3. 多窗口适配:多项恢复与专属资源访问

01

关于应用连续性

针对应用连续性,最核心的两点就是配置界面状态和处理配置变更

配置界面状态,现在的Android APP开发只要用到了ViewModel,其实就不是问题,Activity重建时,系统会把上次销毁的Activity的内部ViewModelStore中的所有ViewModel传给新的Activity的ViewModelStore,新的Activity中可以从ViewModelStore中获取之前的ViewModel,流程图如下:

上图中可以看出Activity最终finished的时,会通过ViewModelStore的onCleared清空当前的ViewModel,当然横竖屏切换这种情况Activity在finished志之前已经将ViewModel传递给新的Activity的ViewModelStore中了,不会有问题。

而自行配置处理变更时,如果应用在特定配置变更期间无需更新资源,并且因性能限制您需要尽量避免 Activity 重启,则可声明 Activity 自行处理配置变更,从而阻止系统重启 Activity,例如文件代码所声明的 Activity 可同时处理屏幕方向变更和键盘可用性变更:

<activity android:name=".MyActivity"android:configChanges="orientation|keyboardHidden"android:label="@string/app_name">

现在,即便其中某个配置发生变化,MyActivity 也不会重启。但 MyActivity 会接收到对 onConfigurationChanged() 的调用消息。此方法会收到传递的 Configuration 对象,从而指定新设备配置。您可以通过读取 Configuration 中的字段确定新配置,然后通过更新界面所用资源进行适当的更改。调用此方法时,Activity 的 Resources 对象会相应地进行更新,并根据新配置返回资源,以便您在系统不重启 Activity 的情况下轻松重置界面元素。

通过onConfigurationChanged() 实现用于检查当前的设备方向

override fun onConfigurationChanged(newConfig: Configuration) {super.onConfigurationChanged(newConfig)// Checks the orientation of the screenif (newConfig.orientation === Configuration.ORIENTATION_LANDSCAPE) {Toast.makeText(this, "landscape", Toast.LENGTH_SHORT).show()} else if (newConfig.orientation === Configuration.ORIENTATION_PORTRAIT) {Toast.makeText(this, "portrait", Toast.LENGTH_SHORT).show()}
}

02

resizeableActivity 与 maxAspectRatio

resizeableActivity可以让应用大小可调,设置 resizeableActivity=true,这个在Android的7.0默认为true了,这可以为应用可能遇到的任何设备类型和环境(例如可折叠设备、桌面模式或自由窗口)提供最大兼容性。请在分屏模式下,或使用可折叠模拟器测试应用行为。

如果应用设置 resizeableActivity=false,则会告知平台其不支持多窗口模式。系统可能仍会调整应用的大小或将其置于多窗口模式;但要实现兼容性,便需要对应用中的所有组件(包括应用的所有 Activity、Service 等)应用同一配置。在某些情况下,重大变更(例如,显示屏尺寸更改)可能会重启进程,而不会更改配置。

例如,以下 Activity 设置了 resizableActivity=false 以及 maxAspectRatio。设备展开时,系统会将应用置于兼容模式,以此保持 Activity 配置、大小和宽高比。

如果未设置 resizeableActivity,或将其设置为 true,系统会假定该应用完全支持多窗口并且可调整大小。

微卡智享

获取铰链角度

关于折叠屏,在Android Q 版本已经开始官方支持,本文仅针对Android 11版本上折叠屏的一些扩展支持做简单的介绍。

根据官网提供的Android 11的新特性介绍文档中,对折叠屏的支持和适配指导,有如下一段文字描述:

使用 Android 11,可以通过以下方法使运行在采用合页式屏幕配置的设备上的应用能够确定合页角度:
提供具有 TYPE_HINGE_ANGLE 的新传感器,以及新的 SensorEvent,
后者可以监控合页角度,并提供设备的两部分之间的角度测量值。
您可以使用这些原始测量值在用户操作设备时执行精细的动画显示。
尽管对于某些类型的应用(例如启动器和壁纸)而言,
知道确切的合页角度会很有用,但大多数应用都应该使用 Jetpack 窗口管理器库,
通过调用 DeviceState.getPosture() 检索设备状态。
或者,您的应用也可以调用 registerDeviceStateChangeCallback(),
以在 DeviceState 更改时收到通知,并在状态发生变化时做出响应。

当然在新的版本中DeviceState已经不再是公开的API了,下一篇会说到,今天我们主要就是看铰链的折叠角度,TYPE_HINGE_ANGLE 为 Android 11新增的传感器类型,专门用于处理折叠屏两部分屏幕之间的的角度相关。

代码展示

微卡智享

整个代码我们直接用上一篇《Android MVI架构初探》的Demo,因为直接用的ViewModel,在屏幕Destory和Create中数据是一直保持的状态。

在MainActivity中加入了SensorManager和Sensor的定义,并写了SensorEventListener的监听事件

通过onResume和onPause来注册和取消监听。

在onCreate中获取到SensorManager和Sensor,其中Sensor记得是要改为Sensor.TYPE_HINGE_ANGLE。

package pers.vaccae.mvidemo.ui.viewimport android.content.Context
import android.content.res.Configuration
import android.hardware.Sensor
import android.hardware.SensorEvent
import android.hardware.SensorEventListener
import android.hardware.SensorManager
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.widget.Button
import android.widget.Toast
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
import pers.vaccae.mvidemo.R
import pers.vaccae.mvidemo.bean.CDrugs
import pers.vaccae.mvidemo.ui.adapter.DrugsAdapter
import pers.vaccae.mvidemo.ui.intent.ActionIntent
import pers.vaccae.mvidemo.ui.intent.ActionState
import pers.vaccae.mvidemo.ui.viewmodel.MainViewModelclass MainActivity : AppCompatActivity() {private val TAG = "X Fold"private val recyclerView: RecyclerView by lazy { findViewById(R.id.recycler_view) }private val btncreate: Button by lazy { findViewById(R.id.btncreate) }private val btnadd: Button by lazy { findViewById(R.id.btnadd) }private val btndel: Button by lazy { findViewById(R.id.btndel) }private lateinit var mainViewModel: MainViewModelprivate lateinit var drugsAdapter: DrugsAdapter//adapter的位置private var adapterpos = -1private var mSensorManager: SensorManager? = nullprivate var mSensor: Sensor? = nullprivate val mSensorEventListener = object : SensorEventListener {override fun onSensorChanged(p0: SensorEvent?) {//p0.values[0]: 测量的铰链角度,其值范围在0到360度之间p0?.let {Log.i(TAG, "当前铰链角度为:${it.values[0]}")}}// 当传感器精度发生改变时回调该方法override fun onAccuracyChanged(p0: Sensor?, p1: Int) {p0?.let {Log.i(TAG, "Sensor:${it.name}, value:$p1")}}}override fun onDestroy() {super.onDestroy()Log.i(TAG, "onDestroy")}override fun onResume() {super.onResume()Log.i(TAG, "onResume")//开启监听mSensorManager?.let {it.registerListener(mSensorEventListener, mSensor!!,SensorManager.SENSOR_DELAY_NORMAL)}}override fun onPause() {super.onPause()Log.i(TAG, "onPause")// 取消监听mSensorManager?.let {it.unregisterListener(mSensorEventListener);}}override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)Log.i(TAG, "onCreate")// 获取传感器管理对象mSensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager// 获取传感器的类型(TYPE_HINGE_ANGLE:铰链角度传感器)mSensorManager?.let {mSensor = it.getDefaultSensor(Sensor.TYPE_HINGE_ANGLE);}mainViewModel = ViewModelProvider(this).get(MainViewModel::class.java)drugsAdapter = DrugsAdapter(R.layout.rcl_item, mainViewModel.listDrugs)drugsAdapter.setOnItemClickListener { baseQuickAdapter, view, i ->adapterpos = i}val gridLayoutManager = GridLayoutManager(this, 3)recyclerView.layoutManager = gridLayoutManagerrecyclerView.adapter = drugsAdapter//初始化ViewModel监听observeViewModel()btncreate.setOnClickListener {lifecycleScope.launch {mainViewModel.actionIntent.send(ActionIntent.LoadDrugs)}}btnadd.setOnClickListener {lifecycleScope.launch {mainViewModel.actionIntent.send(ActionIntent.InsDrugs)}}btndel.setOnClickListener {lifecycleScope.launch {Log.i("status", "$adapterpos")val item = try {drugsAdapter.getItem(adapterpos)} catch (e: Exception) {CDrugs()}mainViewModel.actionIntent.send(ActionIntent.DelDrugs(adapterpos, item))}}}private fun observeViewModel() {lifecycleScope.launch {repeatOnLifecycle(Lifecycle.State.STARTED) {mainViewModel.state.collect {when (it) {is ActionState.Normal -> {btncreate.isEnabled = truebtnadd.isEnabled = truebtndel.isEnabled = true}is ActionState.Loading -> {btncreate.isEnabled = falsebtncreate.isEnabled = falsebtncreate.isEnabled = false}is ActionState.Drugs -> {drugsAdapter.setList(it.drugs)
//                            drugsAdapter.setNewInstance(it.drugs)}is ActionState.Error -> {Toast.makeText(this@MainActivity, it.msg, Toast.LENGTH_SHORT).show()}}}}}}
}

上面是MainActivity的全部代码,等下一章都完成后整个Demo会更新到Github和Gitee上,这样通过传感器获取折叠屏折叠角度的代码就完成了。

参考文章

https://developer.android.google.cn/guide/topics/ui/foldables?hl=zh-cn

https://blog.csdn.net/vitaviva/article/details/105246639

https://juejin.cn/post/7049705037525680164

https://blog.csdn.net/luzhenrong45/article/details/109632140

往期精彩回顾

Android MVI架构初探

Android Kotlin协程间的通信Channel介绍

Android内存篇(三)----自动重启APP实现内存兜底策略

Android折叠屏开发学习(一)---通过传感器获取铰链角度相关推荐

  1. Android折叠屏开发学习(三)---使用MotionLayout实现折叠屏分屏效果

    学更好的别人, 做更好的自己. --<微卡智享> 本文长度为6259字,预计阅读11分钟 前言 今天是折叠屏开发的第三篇,前面已经介绍了铰链的角度监听和Jetpack Window实现监听 ...

  2. 粮草先行——Android折叠屏开发技术点番外篇之运行时变更处理原则

    上一篇文章中,我们有提到Activity在屏幕尺寸发生变更时的处理方式,总共有两种: 重启APP以适应屏幕改变: 手动处理数据,避免APP重启. 同样,这两种方式也同时适用于改变屏幕方向.更改系统语言 ...

  3. 【Android折叠屏适配】基于AutoSize框架适配折叠屏并兼容多窗口模式

    [Android折叠屏适配]基于AutoSize框架适配折叠屏并兼容多窗口模式 问题背景 当前最新的Android API 33对大屏设备的支持已较为完善,结合Jetpack Compose等响应式布 ...

  4. Android 折叠屏 适配

    最近,华为和三星都发布了各自的折叠屏 华为折叠屏 Mate X和Samsung Galaxy Fold.最近也收到了华为应用市场人员反馈的App不适配折叠屏的情况. 下面,记录一下适配折叠屏的步骤. ...

  5. Pittkai——Android折叠屏生命周期

    Android折叠屏生命周期 (第一次在CSDN上写文章,随便写写,记录一下) 如今随着手机的发展,屏幕从分屏甚至走上了折叠屏的道路,即将推出的谷歌Android Q系统更是支持了折叠屏,但苦于手头没 ...

  6. Android 折叠屏技术发展与适配

    文章目录 1 折叠屏行业概览 1.1 折叠屏诞生的背景 1.1.1 屏幕面积变化 1.1.2 屏占比变化 1.2 折叠屏手机结构 1.2.1 折叠屏手机物理结构 1.2.2 折叠屏手机分类 1.3 折 ...

  7. Android 折叠屏适配最全的攻略在这里

    随着三星 Galaxy Fold 和 华为 Mate X 的发布,折叠屏手机开始进入大家的视线.在改变手机体验的同时,也给我们开发人员在适配方面带来了更多的挑战.本文给大家介绍一下 Android 开 ...

  8. Android高级终端开发学习笔记(《疯狂Android讲义》第11章-第17章)

    Android高级终端开发笔记 2021/6/19 下午 13:34开始 多媒体应用开发 Android支持的音频格式有:MP3 WAV 3GP等.支持的视频格式有MP4 3GP等. 多媒体数据既可以 ...

  9. Android 折叠屏就要来了

    北京时间 11 月 8 日,三星在旧金山向开发者发布了一款可折叠屏幕手机三星,型号为Infinity Flex Display.视频的动态图上我们可以发现,三星的折叠屏手机是屏内折叠设计,将屏幕折叠后 ...

最新文章

  1. 华为AR28-31配置光纤接入
  2. 洛谷P2312 解方程题解
  3. boost::hana::on用法的测试程序
  4. 利用angular4和nodejs-express构建一个简单的网站(六)—用户模块和路由分析
  5. 史上最全的stm32资料库4---常见问题及编译工具篇
  6. python截取关键字后的字符串_使用正则表达式获取python中特定字符串之后的所有内容...
  7. 转:vim----复制粘贴
  8. Scrapy 爬虫教程导航
  9. Shell脚本——数字计算
  10. html 手机访问优化,移动端首屏优化
  11. 安卓应用性能调试和优化经验分享
  12. Google 开源最新 NLP 模型,能处理整本《罪与罚》
  13. 用java写蒙特卡洛模拟_java实现蒙特卡洛模拟求解渗透问题
  14. android类似iphone照片幻灯片,8个最佳照片编辑应用让你的Android或iPhone照片看起来不可思议...
  15. 黑苹果开机界面的启动项设置
  16. 微信小程序项目实例——双人五子棋
  17. HTML 边框和背景设置
  18. What Makes a Good Teacher
  19. 本软件仅供学习交流,如作他用所承受的法律责任一概与作者无关(下载使用即代表你同意上述观点)
  20. numpy与Image互转

热门文章

  1. 《全基因组测序WGS数据分析——4.构建WGS主流程》学习笔记
  2. Unity打包exe文件
  3. 最近刚完成某大学的图书馆个人阅读账单,中间遇到的难题想把总结记下来,下次再做类似的东西就有印象了,叮~
  4. 攻防世界crypto篇
  5. Nature子刊:稻田甲烷氧化的微生物机制
  6. HTML5+CSS3实现小米官网(完整版)
  7. mysql数据库linux_Linux平台安装配置MySQL数据库
  8. java发展规划与感悟
  9. 智能温室管理系统种蘑菇是怎么样的
  10. “Google 软件开发代理商计划” 中国地区正式开启报名