前言

本系列文章从 Architecture,UI,Performance 和 Compose 这四个方向带大家了解本次 I/O 上 Jetpack 的最新内容。

Google I/O :Android Jetpack 最新变化(三)UI_android

  • Google I/O :Android Jetpack 最新变化(一)Architecture
  • Google I/O :Android Jetpack 最新变化(二)Performance
  • Google I/O :Android Jetpack 最新变化(三)UI
  • Google I/O :Android Jetpack 最新变化(四)Compose

本文是第三篇:UI 篇。

1. WindowManager

这并非系统 WMS 获取的那个 WindowManager,它是 Jetpack 的新成员,当前刚刚迈入 1.1.0。

implementation "androidx.window:window:1.1.0-alpha02"

它可以帮助我们适配日益增多的可折叠设备,满足多窗口环境下的开发需求。 可折叠设备通常分为两类:单屏可折叠设备(一个整体的柔性屏幕)和双屏可折叠设备(两个屏幕由合页相连)。

Google I/O :Android Jetpack 最新变化(三)UI_Android Jetpack_02

目前单屏可折叠设备正逐渐成为主流,但无论哪种设备都可以通过 WindowManager 感知当前的屏幕显示特性,例如当前折叠的状态和姿势等。

获取折叠状态

多屏设备下,一个窗口可能会跨越物理屏幕显示,这样窗口中会出现铰链等不连续部分,FoldingFeature (DisplayFeature 的子类)对铰链这类的物理部件进行抽象,从中可以获取铰链在窗口中的准确位置,帮助我们避免将关键交互按钮布局在其中。另外 FoldingFeature 还提供了可以感知感知当前折叠状态的 API,我们可以根据这些状态改变应用的布局:

//铰链处于半开状态且位置水平,适合切换到平板模式
fun isTableTopMode(foldFeature: FoldingFeature) =
foldFeature.isSeparating &&
foldFeature.orientation == FoldingFeature.Orientation.HORIZONTAL
//铰链处于半开状态且位置垂直,适合切换到阅读模式
fun isBookMode(foldFeature: FoldingFeature) =
foldFeature.isSeparating &&
foldFeature.orientation == FoldingFeature.Orientation.VERTICAL

isTableTopMode

isBookMode

Google I/O :Android Jetpack 最新变化(三)UI_Google I/O_03

Google I/O :Android Jetpack 最新变化(三)UI_Android Jetpack_04

WindowManager 允许我们通过 Flow 持续观察显示特性的变化。

lifecycleScope.launch(Dispatchers.Main) {
lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
WindowInfoTracker.getOrCreate(this@SampleActivity)
.windowLayoutInfo(this@SampleActivity)
.collect { newLayoutInfo ->
// Use newLayoutInfo to update the layout.
}
}
}

如上,当显示特性变化时,我们能获取 newLayoutInfo ,它是一个 WindowLayoutInfo 类型,内部持有了 FoldingFeature 信息。

感知窗口大小变化

应用窗口可能跟随设备配置变化时(例如折叠屏的展开、旋转,或窗口在多窗口模式下调整大小)发生变化,我们可以通过 WIndowManger 的 WindowMetrics 获取窗口大小,我们有两种获取当前 WindowMetrics 的方式,同步获取和异步监听:

//异步监听
lifecycleScope.launch(Dispatchers.Main) {
windowInfoRepository().currentWindowMetrics.flowWithLifecycle(lifecycle)
.collect { windowMetrics: WindowMetrics ->
val currentBounds = windowMetrics.bounds
val width = currentBounds.width()
val height = currentBounds.height()
}
}

//同步获取
val windowMetrics = WindowMetricsCalculator.getOrCreate().computeCurrentWindowMetrics(activity)
val currentBounds = windowMetrics.bounds
val width = currentBounds.width()
val height = currentBounds.height()


更多参考:medium.com/androiddeve…


2. DragAndDrop

Google I/O :Android Jetpack 最新变化(三)UI_Google I/O_05

Jetpack DragAndDrop 是专门处理拖放手势的库,它除了服务于普通手机设备上的开发,更重要的意义是可以实现折叠设备跨屏幕的拖放

implementation 'androidx.draganddrop:draganddrop:1.0.0-alpha02'

DragStartHelper 和 DropHelper 是其最核心的 API,可以配置拖防过程中的数据传递、显示效果等,还可以监听手势回调。

拖动 DragStartHelper

DragStartHelper 负责监测拖动手势的开始时机,包括长按拖动、单击并用鼠标拖动等。我们可以将需要拖动的视图对象包装进来并开启监听,当监听到拖动手势触发时,完成一些简单配置即可。

// 使用 DragStartHelper 包装 draggableView 对象
DragStartHelper(draggableView) { view, _ ->
// 将需要传递的数据封装到 ClipData 中
val dragClipData = ClipData.newUri(contentResolver, "File", fileUri)
// 创建目标拖动时的展示图片,可自定义也可以根据 draggableView 创建默认样式
val dragShadow = View.DragShadowBuilder(view)
// 基于数据、拖动效果启动拖动
view.startDragAndDrop(
dragClipData,
dragShadow,
null, // Optional extra local state information
// 添加 flag 启动全局拖动
DRAG_FLAG_GLOBAL or DRAG_FLAG_GLOBAL_URI_READ)
)
}.attach()

如上,准备好需要拖动数据和样式等,调用 View#startDragAndDrop 启动拖动。例子中拖动的目标是 content: 这类 URI,因此我们可以通过设置 DRAG_FLAG_GLOBAL 实现跨进程的拖动。

放置 DropHelper

DropHelper 是另一个核心 API,关心拖动数据放下的时机和目标视图。

//针对可拖放视图调用 configureView
DropHelper.configureView(
this,// 当前Activity
outerDropTarget, //接收拖放的对象,会根据情况高亮显示
arrayOf(MIMETYPE_TEXT_PLAIN, "image/*"), // 支持的 MIME 类型
DropHelper.Options.Builder() //一些参数配置,例如放下时高亮的颜色,视图范围等
.addInnerEditTexts(innerEditText)
.build()
) { _, payload ->
// 监听到目标的放下,可以从 ClipData 中取得数据,
// 执行上传、显示等处理,当然还可以处理非法拖放时的警告或视图提醒等
...
}

构建 DropHelper.Options 实例的时候,需要调用 addInnerEditTexts(),这样可以确保嵌套的 EditText 控件不会抢夺视图焦点。


更多参考:medium.com/androiddeve…