惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

T
Tailwind CSS Blog
Hacker News - Newest:
Hacker News - Newest: "LLM"
The Cloudflare Blog
J
Java Code Geeks
大猫的无限游戏
大猫的无限游戏
雷峰网
雷峰网
WordPress大学
WordPress大学
Jina AI
Jina AI
IT之家
IT之家
Hugging Face - Blog
Hugging Face - Blog
博客园 - 聂微东
有赞技术团队
有赞技术团队
V
Vulnerabilities – Threatpost
博客园 - 司徒正美
L
Lohrmann on Cybersecurity
P
Privacy International News Feed
S
SegmentFault 最新的问题
C
Cisco Blogs
V
V2EX
博客园 - 三生石上(FineUI控件)
Apple Machine Learning Research
Apple Machine Learning Research
I
Intezer
人人都是产品经理
人人都是产品经理
博客园_首页
D
Darknet – Hacking Tools, Hacker News & Cyber Security
Cyberwarzone
Cyberwarzone
Know Your Adversary
Know Your Adversary
酷 壳 – CoolShell
酷 壳 – CoolShell
Security Latest
Security Latest
V
Visual Studio Blog
S
Schneier on Security
宝玉的分享
宝玉的分享
博客园 - 【当耐特】
腾讯CDC
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
博客园 - Franky
Last Week in AI
Last Week in AI
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
K
Kaspersky official blog
T
The Exploit Database - CXSecurity.com
G
GRAHAM CLULEY
量子位
S
Securelist
小众软件
小众软件
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
T
Tenable Blog
阮一峰的网络日志
阮一峰的网络日志
博客园 - 叶小钗
T
Troy Hunt's Blog

chenhang 的博客

Android 12 - WMS 层级结构 && DisplayAreaGroup 引入 Android 12 - 跟踪利器 WinScope Android 列表滚动优化之 OverScroller 揭秘 使用 Ninja 提升模块编译速度 Android Q 黑暗模式(Dark Mode)源码解析 如何顺滑地查看 Android Native 代码 AOSP 编译和烧写 Protocol Buffers 手册 设计模式之装饰模式 设计模式之桥接模式 设计模式之代理模式 依赖注入利器 - Dagger ‡ Robolectric使用教程 设计模式之模板方法模式和策略模式 设计模式之工厂模式(Factory) Fragment源码分析 Fragment事务管理源码分析 java动态代理 Android分包MultiDex源码分析
Android 12 - Letterbox 模式
2021-10-21 · via chenhang 的博客

1. 简介

随着越来越多大屏和折叠屏设备出现,很多应用并未对不同尺寸的设备进行 UI 适配,这时候应用选择以特定的宽高比显示(虽然 Google 不建议这这样做,官方还是希望开发者可以对不同的屏幕尺寸进行自适应布局~),当应用的宽高比和它的容器比例不兼容的时候,就会以 Letterbox 模式打开。

2021-10-21-18-17-07

Letterbox 模式下界面会以指定的比例显示,周围空白区域可以填充壁纸或者颜色。至于 Letterbox 的外观可受以下因素影响:

  • config_letterboxActivityCornersRadius: 界面圆角大小
  • config_letterboxBackgroundType: 背景填充类型,分别有:
    • LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND: 颜色受 android:colorBackground 影响
    • LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING: 颜色受 android:colorBackgroundFloating 影响
    • LETTERBOX_BACKGROUND_SOLID_COLOR: 颜色受 config_letterboxBackgroundColor 影响
    • LETTERBOX_BACKGROUND_WALLPAPER: 显示壁纸,此选项和 FLAG_SHOW_WALLPAPER 类似,会导致壁纸窗口显示
  • config_letterboxBackgroundWallpaperBlurRadius: 壁纸模糊程度
  • config_letterboxBackgroundWallaperDarkScrimAlpha: 壁纸变暗程度

2. 何时触发

Letterbox 的触发条件一般有:

  • android:resizeableActivity=false 且应用声明的宽高比与容器不兼容的时候(如屏幕宽高超出 android:maxAspectRatio)
  • setIgnoreOrientationRequest(true) 系统设置忽略屏幕方向后,以横屏模式下打开一个强制竖屏的界面

3. 实现方案

Letterbox 显示的实现并不复杂,Android 12 在 ActivityRecord 中增加了 LetterboxUiController 用以控制 Letterbox 的布局和显示,先来看看处于 Letterbox 模式时 SurfaceFlinger 状态:

2021-10-22-10-33-26

可以看到,跟正常情况相比,除了界面本身的大小和位置被缩放到指定比例外,四周还多了两个 Layer,挂在 ActiviRecord 节点下面,这两个 Layer 可根据配置进行指定的颜色填充,如果背景是壁纸的话,还可以设置壁纸的 dim 值和模糊程度,这些都可以通过 SurfaceControl 接口轻松实现。

下面简单分析一下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51

void updateLetterboxSurface(WindowState winHint) {
final WindowState w = mActivityRecord.findMainWindow();
if (w != winHint && winHint != null && w != null) {
return;
}

layoutLetterbox(winHint);
if (mLetterbox != null && mLetterbox.needsApplySurfaceChanges()) {

mLetterbox.applySurfaceChanges(mActivityRecord.getSyncTransaction());
}
}

void layoutLetterbox(WindowState winHint) {
final WindowState w = mActivityRecord.findMainWindow();
if (w == null || winHint != null && w != winHint) {
return;
}
updateRoundedCorners(w);
updateWallpaperForLetterbox(w);

if (shouldShowLetterboxUi(w)) {
if (mLetterbox == null) {

mLetterbox = new Letterbox(() -> mActivityRecord.makeChildSurface(null),
mActivityRecord.mWmService.mTransactionFactory,
mLetterboxConfiguration::isLetterboxActivityCornersRounded,
this::getLetterboxBackgroundColor,
this::hasWallpaperBackgroudForLetterbox,
this::getLetterboxWallpaperBlurRadius,
this::getLetterboxWallpaperDarkScrimAlpha);
mLetterbox.attachInput(w);
}
mActivityRecord.getPosition(mTmpPoint);




final Rect transformedBounds = mActivityRecord.getFixedRotationTransformDisplayBounds();
final Rect spaceToFill = transformedBounds != null
? transformedBounds
: mActivityRecord.inMultiWindowMode()
? mActivityRecord.getRootTask().getBounds()
: mActivityRecord.getRootTask().getParent().getBounds();

mLetterbox.layout(spaceToFill, w.getFrame(), mTmpPoint);
} else if (mLetterbox != null) {
mLetterbox.hide();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

public void applySurfaceChanges(SurfaceControl.Transaction t) {
if (!needsApplySurfaceChanges()) {

return;
}
mSurfaceFrameRelative.set(mLayoutFrameRelative);
if (!mSurfaceFrameRelative.isEmpty()) {
if (mSurface == null) {

createSurface(t);
}

mColor = mColorSupplier.get();
t.setColor(mSurface, getRgbColorArray());
t.setPosition(mSurface, mSurfaceFrameRelative.left, mSurfaceFrameRelative.top);
t.setWindowCrop(mSurface, mSurfaceFrameRelative.width(),
mSurfaceFrameRelative.height());


mHasWallpaperBackground = mHasWallpaperBackgroundSupplier.get();
updateAlphaAndBlur(t);

t.show(mSurface);
} else if (mSurface != null) {
t.hide(mSurface);
}
if (mSurface != null && mInputInterceptor != null) {
mInputInterceptor.updateTouchableRegion(mSurfaceFrameRelative);
t.setInputWindowInfo(mSurface, mInputInterceptor.mWindowHandle);
}
}

4. 小结

本文只是简单分析了下 Letterbox 模式的触发条件和显示的大概逻辑,还有很多细节没有涉及,比如详细的触发逻辑判断可以查看 LetterboxUiController#shouldShowLetterboxUi 方法