文章插图
△ 铰链区域
当设备从折叠模式转换到非折叠模式时,有两种主要的技术方案可用于设计布局。第一种是扩大屏幕,该方案采用了一种简单的响应式布局,在该布局下应用会扩展内容并填充到屏幕上。通常情况下,我们会根据前面提到的 Material 指南来扩展栏式网格:
https://m3.material.io/foundations/adaptive-design/foldables/compositions
第二种是增加另一个页面,根据您构建的应用不同,可以采用与列表 / 详情或者以另一个面板补充主面板功能相同的方案。
文章插图
△ 情境 1: 扩大屏幕 (图左) 情境 2: 增加页面 (图右)
在这两种情况下,根据 material.io 的指南,您需要创建一个平均分布在铰链区域两侧的八栏网格,当添加 Navigation rail 等导航容器时,屏幕起始侧会被压缩以容纳导航容器。
文章插图
△ 平均分布在铰链两侧的八栏网格 (蓝背景)
适配示例现在我们来看如何在运行期间利用好折叠状态。Jetpack Window Manager 库提供了相应的 API,可以检测应用窗口是否存在折叠。任何 Activity 都可以获得一个 WindowInfoRepository 实例。然后,在 Started 和 Stopped 这两种生命周期状态之间,我们可以安全地从窗口布局信息流中收集信息。每当流发射一个值时,我们都可以检查 displayFeature,然后有针对性地寻找 FoldingFeature。
override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)val windowInfoRepo = windowInfoRepository()// 在 STARTED 和 STOPPED 这两种生命周期状态之间安全地从 windowInfoRepo 中收集数据lifecycleScope.launch(Dispatchers.Main) {lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {windowInfoRepo.windowLayoutInfo.collect { info ->for (feature in info.displayFeatures) {val fold = feature as? FoldingFeature ?: continue// 使用 FoldingFeature}}}}}△ 识别折叠姿态
掌握了折叠姿态的相关信息后,我们可以通过一些方法来查看设备是否处于前面提及的某种姿态。在书本模式下,设备的状态为 HALF_OPENED,且其方向为 VERTICAL;在桌面模式下的状态为 HALF_OPENED,且其方向为 HORIZONTAL。
// 书本模式是半打开的垂直折叠模式fun FoldingFeature.isBookMode() =state == FoldingFeature.State.HALF_OPENED &&orientation == FoldingFeature.Orientation.VERTICAL// 桌面模式是半打开的水平折叠模式fun FoldingFeature.isTableTopMode() =state == FoldingFeature.State.HALF_OPENED &&orientation == FoldingFeature.Orientation.HORIZONTAL△ 书本模式于桌面模式的判定条件
FoldingFeature 中还包含窗口中的折叠位置,当折叠导致内容视图被割裂时,我们应该及时更新布局参数。您可以做些调整,比如将支持面板置于一侧,或者在折叠的上半部分展示主页横幅。首先,我们需要知道内容视图在窗口中的位置,通过 getLocationInWindow 可以获取位置信息。我们将使用这些坐标以及宽度和高度创建一个 Rect 对象,这样我们便得到了窗口坐标空间中的视图边界。
FoldingFeature 给出了在窗口的坐标空间中的折叠边界,因此我们可以直接检查这两个区域是否相交,如果相交,我们可以将 featureRect 的边界转换为视图的坐标空间并将其返回。顺便说一下,如果您使用 SlidingPaneLayout 来实现列表 / 详情布局,您会自动获得对书本模式的支持。只要两个窗格都能容纳进去,SlidingPaneLayout 会将窗格置于折叠姿态的另一侧。
fun getFoldBoundsInView(foldingFeature: FoldingFeature,view: View): Rect? {// 获取视图在窗口坐标空间中的边界val viewLocation = IntArray(2)view.getLocationInWindow(viewLocation)val (viewX, viewY) = viewLocationval viewRect = Rect(left = viewX, top = viewYright = viewX + view.width, bottom = view + view.height)…//显示功能的边界已经在窗口的坐标空间中// 检查 view 的边界和显示功能的边界是否相交val featureRect = Rect(foldingFeature.bounds)val intersects = featureRect. intersect (viewRect)if (featureRect.isEmpty || ! intersects)return null}// 将功能的边界坐标转换为 view 的坐标空间featureRect.offset(-viewX, -viewY)return featureRect}
推荐阅读
- 海外市场|凭借近2亿的年销量,小米三年超越苹果的可能性有多大?
- iphone|苹果教你如何让 iPhone 电池保持健康
- indiRedmi Note 11 Pro+ 5G,Note 11 Pro India 可能在 3 月推出
- 满帧跑原神不发烫!首配新一代骁龙8红魔7游戏手机抢先测
- k50|红米K50电竞版不满意?标准版下月发布,K40进入清仓阶段
- 平板电脑|OPPO首款平板电脑上架京东开启预约:窄边框设计,2 月 24 日发布
- 旗舰|摩托罗拉 Edge 30 Ultra渲染图曝光,或将首发高通新一代旗舰处理器
- 指纹|比亚迪指纹识别专利获授权,实现两级唤醒避免误触
- 赵明|荣耀数字系列设计美学再升级,荣耀60星空色火了
- 富士康|厉害了!1月iPhone在中国市场出货量要创新高 国人购买力太强