<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Android &#8211; 张三太爷</title>
	<atom:link href="https://www.somedoc.net/category/android/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.somedoc.net</link>
	<description>看前面，黑洞洞</description>
	<lastBuildDate>Thu, 01 Jan 2026 03:49:05 +0000</lastBuildDate>
	<language>zh-Hans</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9</generator>

<image>
	<url>https://www.somedoc.net/wp-content/uploads/2016/12/cropped-dandycheung-1-32x32.jpg</url>
	<title>Android &#8211; 张三太爷</title>
	<link>https://www.somedoc.net</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Android 启动器中图标管理的一些资料</title>
		<link>https://www.somedoc.net/2026/01/01/android-%e5%90%af%e5%8a%a8%e5%99%a8%e4%b8%ad%e5%9b%be%e6%a0%87%e7%ae%a1%e7%90%86%e7%9a%84%e4%b8%80%e4%ba%9b%e8%b5%84%e6%96%99/</link>
					<comments>https://www.somedoc.net/2026/01/01/android-%e5%90%af%e5%8a%a8%e5%99%a8%e4%b8%ad%e5%9b%be%e6%a0%87%e7%ae%a1%e7%90%86%e7%9a%84%e4%b8%80%e4%ba%9b%e8%b5%84%e6%96%99/#respond</comments>
		
		<dc:creator><![CDATA[张三太爷]]></dc:creator>
		<pubDate>Thu, 01 Jan 2026 03:19:09 +0000</pubDate>
				<category><![CDATA[Android]]></category>
		<category><![CDATA[技术]]></category>
		<guid isPermaLink="false">https://www.somedoc.net/?p=6595</guid>

					<description><![CDATA[文 3 的参考链接 2 也可以一看。]]></description>
										<content:encoded><![CDATA[
<ol class="wp-block-list">
<li><a href="https://zhuanlan.zhihu.com/p/708598218">Launcher3 图标布局原理解析 &#8211; 知乎</a>；</li>



<li><a href="https://juejin.cn/post/7278883071515656233">Launcher3 中 IconCache 缓存逻辑 &#8211; 掘金</a>；</li>



<li><a href="https://www.heqiangfly.com/2021/02/06/Android%20Launcher/android-launcher-icon-cache/">Android &#8212; IconCache | 孤舟蓑笠翁，独钓寒江雪</a>；</li>



<li><a href="https://blog.csdn.net/u012556114/article/details/148197324">Android 12 Launcher3 修改 App 图标白边问题 &#8211; CSDN 博客</a>；</li>



<li><a href="https://blog.csdn.net/a546036242/article/details/147518275">Android 14 系统统一修改 App 启动时 SplashScreen 中图标的大小和圆角 &#8211; CSDN 博客</a>；</li>



<li><a href="https://github.com/zhanghai/AppIconLoader/">https://github.com/zhanghai/AppIconLoader/</a>：MaterialFiles 作者张海的一个图标加载库。</li>
</ol>



<p>文 3 的参考链接 2 也可以一看。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.somedoc.net/2026/01/01/android-%e5%90%af%e5%8a%a8%e5%99%a8%e4%b8%ad%e5%9b%be%e6%a0%87%e7%ae%a1%e7%90%86%e7%9a%84%e4%b8%80%e4%ba%9b%e8%b5%84%e6%96%99/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>解决 NDK 构建时的一个警告</title>
		<link>https://www.somedoc.net/2025/12/08/%e8%a7%a3%e5%86%b3-ndk-%e6%9e%84%e5%bb%ba%e6%97%b6%e7%9a%84%e4%b8%80%e4%b8%aa%e8%ad%a6%e5%91%8a/</link>
					<comments>https://www.somedoc.net/2025/12/08/%e8%a7%a3%e5%86%b3-ndk-%e6%9e%84%e5%bb%ba%e6%97%b6%e7%9a%84%e4%b8%80%e4%b8%aa%e8%ad%a6%e5%91%8a/#respond</comments>
		
		<dc:creator><![CDATA[张三太爷]]></dc:creator>
		<pubDate>Mon, 08 Dec 2025 05:51:03 +0000</pubDate>
				<category><![CDATA[Android]]></category>
		<category><![CDATA[技术]]></category>
		<category><![CDATA[问题解决]]></category>
		<guid isPermaLink="false">https://www.somedoc.net/?p=6564</guid>

					<description><![CDATA[昨天编译时，看到两个 NDK 构建脚本发出的告警信息： 不管 <a href="https://www.somedoc.net/2025/12/08/%e8%a7%a3%e5%86%b3-ndk-%e6%9e%84%e5%bb%ba%e6%97%b6%e7%9a%84%e4%b8%80%e4%b8%aa%e8%ad%a6%e5%91%8a/" class="more-link">[&#8230;]</a>]]></description>
										<content:encoded><![CDATA[
<p>昨天编译时，看到两个 NDK 构建脚本发出的告警信息：</p>



<pre class="crayon-plain-tag">[crayon-6956a791bf902539923211 inline="true" ]C/C++: C:/Android/Sdk/ndk/27.0.12077973/build/core/build-binary.mk:418: warning: overriding recipe for target 'C:\code\doom\build\intermediates\cxx\Debug\5523b6rn/obj/local/arm64-v8a/objs-debug/lzma_1.9/C/7zDec.o'
C/C++: C:/Android/Sdk/ndk/27.0.12077973/build/core/build-binary.mk:418: warning: ignoring old recipe for target 'C:\code\doom\build\intermediates\cxx\Debug\5523b6rn/obj/local/arm64-v8a/objs-debug/lzma_1.9/C/7zDec.o'
C/C++: C:/Android/Sdk/ndk/27.0.12077973/build/core/build-binary.mk:418: warning: overriding recipe for target 'C:\code\doom\build\intermediates\cxx\Debug\5523b6rn/obj/local/arm64-v8a/objs-debug/lzma_1.9/C/7zDec.o.commands.json'
C/C++: C:/Android/Sdk/ndk/27.0.12077973/build/core/build-binary.mk:418: warning: ignoring old recipe for target 'C:\code\doom\build\intermediates\cxx\Debug\5523b6rn/obj/local/arm64-v8a/objs-debug/lzma_1.9/C/7zDec.o.commands.json'</pre>[/crayon]



<p>不管是 <code>overriding recipe for target</code> 还是 <code>ignoring old recipe for target</code>，都没看懂是怎么回事。网上搜索到 SO 上的一篇帖子，说的也是含含糊糊，但似乎说到是跟目标重复有关。打开 <code>build-binary.mk</code> 脚本的看第 418 行是没有线索的，于是又去看工程里跟指定的对象 7zDec 相关的 makefile，这下就找到了问题，<code>7zDec.c</code> 这个要编译的源码文件被写了两回，显然就是导致了“overring”以及“ignoring”的“重复”的来源。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.somedoc.net/2025/12/08/%e8%a7%a3%e5%86%b3-ndk-%e6%9e%84%e5%bb%ba%e6%97%b6%e7%9a%84%e4%b8%80%e4%b8%aa%e8%ad%a6%e5%91%8a/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Android 里的 Adaptive Icon 的规范</title>
		<link>https://www.somedoc.net/2025/12/04/android-%e9%87%8c%e7%9a%84-adaptive-icon-%e7%9a%84%e8%a7%84%e8%8c%83/</link>
					<comments>https://www.somedoc.net/2025/12/04/android-%e9%87%8c%e7%9a%84-adaptive-icon-%e7%9a%84%e8%a7%84%e8%8c%83/#respond</comments>
		
		<dc:creator><![CDATA[张三太爷]]></dc:creator>
		<pubDate>Wed, 03 Dec 2025 16:12:38 +0000</pubDate>
				<category><![CDATA[Android]]></category>
		<category><![CDATA[技术]]></category>
		<guid isPermaLink="false">https://www.somedoc.net/?p=6561</guid>

					<description><![CDATA[以前和自适应图标（adaptive icon）打过交道，不过 <a href="https://www.somedoc.net/2025/12/04/android-%e9%87%8c%e7%9a%84-adaptive-icon-%e7%9a%84%e8%a7%84%e8%8c%83/" class="more-link">[&#8230;]</a>]]></description>
										<content:encoded><![CDATA[
<p>以前和自适应图标（adaptive icon）打过交道，不过每次都是集中力气处理由它带来的问题，看过几次相关的资料，当时记住了，可时间一长，就又都漫漶在了记忆的不知何处去了。今天还是记录一下为好。</p>



<p>自适应图标是 Android O 引入的，也就是 Android 8（API level 是 26 的 Android 8.0 其 code name 是 O，而 API level 是 27 的 Android 8.1 也是）。</p>



<p><strong>资源</strong>。为自适应图标做基础的资源类型也是相应新增的，叫做 AdaptiveIconDrawable。其定义形如：</p>



<pre class="crayon-plain-tag">[crayon-6956a791bfda5346701262 inline="true" ]&lt;adaptive-icon>
  &lt;background android:drawable="@&#91;drawable|mipmap|color]/bg_foo"/>
  &lt;foreground android:drawable="@&#91;drawable|mipmap|color]/fg_bar"/>
&lt;/adaptive-icon></pre>[/crayon]



<p>两个 drawable 的大小都必须是 108dp，背景 drawable 必须不透明，而前景 drawable 可以包含透明区域。</p>



<p><strong>遮罩</strong>。需要说明的是，尽管自适应图标的大小是 108dp，但是最终显示/渲染一个自适应图标时，系统/设备/启动器所使用的施加于其上用以控制可见区域的遮罩，其大小却只能是 72dp。也就意味着，一个自适应图标的上下左右四个边，均有尺寸为 18dp 的部分在通常情况下是不可见的。</p>



<p>遮罩的形状必须是外凸（convex）的，其形状边缘至中心点的最小距离是 33dp。这也是下图中直径为 66dp 的所谓圆形“安全区”的由来。</p>



<figure class="wp-block-image size-full"><a href="https://www.somedoc.net/wp-content/uploads/2025/12/1_d3ieGM2uVdKjrlF9y-G-0w.webp"><img fetchpriority="high" decoding="async" width="324" height="324" src="https://www.somedoc.net/wp-content/uploads/2025/12/1_d3ieGM2uVdKjrlF9y-G-0w.webp" alt="" class="wp-image-6562" srcset="https://www.somedoc.net/wp-content/uploads/2025/12/1_d3ieGM2uVdKjrlF9y-G-0w.webp 324w, https://www.somedoc.net/wp-content/uploads/2025/12/1_d3ieGM2uVdKjrlF9y-G-0w-300x300.webp 300w, https://www.somedoc.net/wp-content/uploads/2025/12/1_d3ieGM2uVdKjrlF9y-G-0w-150x150.webp 150w" sizes="(max-width: 324px) 100vw, 324px" /></a></figure>



<p><strong>渲染</strong>。从 Android 8 开始，截止到 Android 12 之前，如果创建图标时使用的是 <code><strong>
			<span id="crayon-6956a791bfda7451747345" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">createWithBitmap</span></span></span></strong></code> 方法，系统（此处主要是指 Google/Android 官方的行为）会对传统图标“以示尊重”，原样渲染，如果位图中存在透明区域，要么保持透明，要么可能渲染为某种特定的背景色（如黑色）。而从 Android 12 开始，系统决定不再给予传统图标那样的优惠待遇，使其长久地破坏系统的视觉效果一致性，于是决定将其缩小，并放置在白色的背景层之上。那会缩小到什么程度呢？就是可以完全放置在安全区以内的程度（可以参见旧文：<a href="https://www.somedoc.net/2021/07/07/android-%e5%af%b9%e9%9d%9e%e8%87%aa%e9%80%82%e5%ba%94%e5%9b%be%e6%a0%87%e7%9a%84%e5%a4%84%e7%90%86/">Android 对非自适应图标的处理</a>）。</p>



<p>Medium 上有人写的两篇博客，对于理解、应用、设计自适应图标具有参考价值。而且其中还提到了上文提及的被遮罩排除的那部分可以有什么样的利用价值。</p>



<ul class="wp-block-list">
<li><a href="https://medium.com/androiddevelopers/implementing-adaptive-icons-1e4d1795470e">https://medium.com/androiddevelopers/implementing-adaptive-icons-1e4d1795470e</a></li>



<li><a href="https://medium.com/google-design/designing-adaptive-icons-515af294c783">https://medium.com/google-design/designing-adaptive-icons-515af294c783</a></li>
</ul>



<p>Android 官方对 Icon 类的文档在：<a href="https://developer.android.com/reference/android/graphics/drawable/Icon">https://developer.android.com/reference/android/graphics/drawable/Icon</a>。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.somedoc.net/2025/12/04/android-%e9%87%8c%e7%9a%84-adaptive-icon-%e7%9a%84%e8%a7%84%e8%8c%83/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>根据构建条件开关取舍 assets 中的内容</title>
		<link>https://www.somedoc.net/2025/09/23/%e6%a0%b9%e6%8d%ae%e6%9e%84%e5%bb%ba%e6%9d%a1%e4%bb%b6%e5%bc%80%e5%85%b3%e5%8f%96%e8%88%8d-assets-%e4%b8%ad%e7%9a%84%e5%86%85%e5%ae%b9/</link>
					<comments>https://www.somedoc.net/2025/09/23/%e6%a0%b9%e6%8d%ae%e6%9e%84%e5%bb%ba%e6%9d%a1%e4%bb%b6%e5%bc%80%e5%85%b3%e5%8f%96%e8%88%8d-assets-%e4%b8%ad%e7%9a%84%e5%86%85%e5%ae%b9/#comments</comments>
		
		<dc:creator><![CDATA[张三太爷]]></dc:creator>
		<pubDate>Tue, 23 Sep 2025 15:20:02 +0000</pubDate>
				<category><![CDATA[Android]]></category>
		<category><![CDATA[问题解决]]></category>
		<guid isPermaLink="false">https://www.somedoc.net/?p=6517</guid>

					<description><![CDATA[Android 项目中有一个功能，想从其它分支合并到主分支内 <a href="https://www.somedoc.net/2025/09/23/%e6%a0%b9%e6%8d%ae%e6%9e%84%e5%bb%ba%e6%9d%a1%e4%bb%b6%e5%bc%80%e5%85%b3%e5%8f%96%e8%88%8d-assets-%e4%b8%ad%e7%9a%84%e5%86%85%e5%ae%b9/" class="more-link">[&#8230;]</a>]]></description>
										<content:encoded><![CDATA[
<p>Android 项目中有一个功能，想从其它分支合并到主分支内，但是又希望把它用开关控制起来。这个需求往日里不算什么，但本次有所不同，因为这个功能还关联着有一组 assets 下的资源文件。这些文件加起来的体积不小，如果把相应的功能隐藏起来的话（例如在 
			<span id="crayon-6956a791bff63593662923" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">build</span><span class="crayon-sy">.</span><span class="crayon-v">gradle</span></span></span> 中把会生成到 
			<span id="crayon-6956a791bff68047071930" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">BuildConfig</span><span class="crayon-sy">.</span><span class="crayon-v">java</span></span></span> 的某个开关常量置为 
			<span id="crayon-6956a791bff69135065710" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-t">false</span></span></span>），希望构建时把关联的 assets 忽略掉，不打入包内以减小体积。</p>



<p>于是又向 grok 请教，而果然，还是它懂得多。给了几个方案，一个是分 
			<span id="crayon-6956a791bff6b047895565" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">sourceSet</span></span></span>，这个不考虑；一个是安装后代码下载，也不考虑。另外还有俩。其一是，在构建脚本里写一个任务，而把所有的 assets 集中放到一个 source 目录中，当构建时，自动提前执行此任务，</p>



<pre class="crayon-plain-tag">[crayon-6956a791bff6e873028428 inline="true" ]android {
    // 其他配置...

    // 动态设置 assets 目录
    sourceSets {
        main {
            assets.srcDirs = &#91;'src/main/assets_filtered']
        }
    }
}

task filterAssets(type: Copy) {
    description 'Copy assets based on includeLargeAssets flag'
    from 'src/main/assets'
    into 'src/main/assets_filtered'
    // 根据开关过滤文件
    if (!project.ext.includeLargeAssets) {
        exclude '**/*.largefile' // 假设大文件以 .largefile 结尾
    }
}

// 确保在编译前执行过滤任务
preBuild.dependsOn filterAssets</pre>[/crayon]



<p>示例代码如上，一眼可以看懂，就不再多说了，这也不是当前需求的最佳解决办法。老夫看中的是最后一种（虽简单、但够用）：使用 
			<span id="crayon-6956a791bff6f426588631" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">aaptOptions</span></span></span> 块中的 
			<span id="crayon-6956a791bff71870678167" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">ignoreAssetsPattern</span></span></span> 语句定义过滤规则。</p>



<pre class="crayon-plain-tag">[crayon-6956a791bff73965997613 inline="true" ]android {
    // 其他配置...

    // 定义一个开关（可以从命令行或默认值控制）
    ext {
        excludeLargeAssets = project.hasProperty('excludeLargeAssets') ? project.getProperty('excludeLargeAssets').toBoolean() : false
    }

    // 生成 BuildConfig 字段
    buildTypes {
        release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
            buildConfigField "boolean", "EXCLUDE_LARGE_ASSETS", "${excludeLargeAssets}"
        }
        debug {
            buildConfigField "boolean", "EXCLUDE_LARGE_ASSETS", "${excludeLargeAssets}"
        }
    }

    // 配置 aaptOptions，根据 excludeLargeAssets 动态设置 ignoreAssetsPattern
    aaptOptions {
        ignoreAssetsPattern excludeLargeAssets ? '!large/**' : ''
    }
}</pre>[/crayon]



<p>
			<span id="crayon-6956a791bff75579461763" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">ignoreAssetsPattern</span></span></span> 的参数用的是 AAPT2 的通用模式匹配语法规则：</p>



<ul class="wp-block-list">
<li><strong>模式格式</strong>：由逗号（
			<span id="crayon-6956a791bff76949892897" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-sy">,</span></span></span>）分隔的多个规则，每个规则可以是：
<ul class="wp-block-list">
<li><strong>忽略模式</strong>：以 
			<span id="crayon-6956a791bff78477062072" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-o">!</span></span></span> 开头，表示忽略匹配的文件或目录。</li>



<li><strong>包含模式</strong>：不以 
			<span id="crayon-6956a791bff79924534396" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-o">!</span></span></span> 开头，表示只包含匹配的文件（但在 
			<span id="crayon-6956a791bff7a321766914" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">ignoreAssetsPattern</span></span></span> 中很少使用，因为其主要用途是忽略）。</li>
</ul>
</li>



<li><strong>通配符</strong>：
<ul class="wp-block-list">
<li>
			<span id="crayon-6956a791bff7c375048992" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-o">*</span></span></span>：匹配文件名中的任意字符（不包括路径分隔符 
			<span id="crayon-6956a791bff7e027091633" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-o">/</span></span></span>）。</li>



<li>
			<span id="crayon-6956a791bff80007402302" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-o">*</span><span class="crayon-o">*</span></span></span>：匹配任意字符，包括路径分隔符（用于匹配子目录）。</li>



<li>
			<span id="crayon-6956a791bff81423712412" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-sy">?</span></span></span>：匹配单个字符。</li>
</ul>
</li>



<li><strong>示例</strong>：
<ul class="wp-block-list">
<li>
			<span id="crayon-6956a791bff82350411001" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-o">!</span><span class="crayon-v">large</span><span class="crayon-o">/</span><span class="crayon-o">*</span><span class="crayon-o">*</span></span></span>：忽略 
			<span id="crayon-6956a791bff84076668330" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">assets</span><span class="crayon-o">/</span><span class="crayon-v">large</span><span class="crayon-o">/</span></span></span> 目录及其子目录下的所有文件。</li>



<li>
			<span id="crayon-6956a791bff85573075570" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-o">*</span><span class="crayon-sy">.</span><span class="crayon-v">txt</span></span></span>：理论上表示只包含 
			<span id="crayon-6956a791bff86810893779" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-sy">.</span><span class="crayon-v">txt</span></span></span> 文件，但由于 
			<span id="crayon-6956a791bff88587291602" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">ignoreAssetsPattern</span></span></span> 主要用于忽略，这种用法不常见。</li>



<li>
			<span id="crayon-6956a791bff89077276485" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-o">!</span><span class="crayon-v">large</span><span class="crayon-o">/</span><span class="crayon-o">*</span><span class="crayon-sy">.</span><span class="crayon-v">png</span><span class="crayon-sy">,</span><span class="crayon-o">*</span><span class="crayon-sy">.</span><span class="crayon-v">txt</span></span></span>：忽略 
			<span id="crayon-6956a791bff8a171852667" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">assets</span><span class="crayon-o">/</span><span class="crayon-v">large</span><span class="crayon-o">/</span></span></span> 下的所有 
			<span id="crayon-6956a791bff8c178651009" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-sy">.</span><span class="crayon-v">png</span></span></span> 文件和 
			<span id="crayon-6956a791bff8d392376468" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">assets</span><span class="crayon-o">/</span></span></span> 根目录下的所有 
			<span id="crayon-6956a791bff8e515473264" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-sy">.</span><span class="crayon-v">txt</span></span></span> 文件。</li>
</ul>
</li>
</ul>



<p></p>



<p></p>



<p></p>



<p></p>



<p></p>



<p></p>



<p></p>



<p></p>



<p></p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.somedoc.net/2025/09/23/%e6%a0%b9%e6%8d%ae%e6%9e%84%e5%bb%ba%e6%9d%a1%e4%bb%b6%e5%bc%80%e5%85%b3%e5%8f%96%e8%88%8d-assets-%e4%b8%ad%e7%9a%84%e5%86%85%e5%ae%b9/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>PWA 在 Android 上的“转正”</title>
		<link>https://www.somedoc.net/2025/08/21/pwa-%e5%9c%a8-android-%e4%b8%8a%e7%9a%84%e8%bd%ac%e6%ad%a3/</link>
					<comments>https://www.somedoc.net/2025/08/21/pwa-%e5%9c%a8-android-%e4%b8%8a%e7%9a%84%e8%bd%ac%e6%ad%a3/#respond</comments>
		
		<dc:creator><![CDATA[张三太爷]]></dc:creator>
		<pubDate>Thu, 21 Aug 2025 08:28:11 +0000</pubDate>
				<category><![CDATA[Android]]></category>
		<category><![CDATA[技术]]></category>
		<guid isPermaLink="false">https://www.somedoc.net/?p=6500</guid>

					<description><![CDATA[一周前让小伙伴尝试构建一个产品的 Web 版，当中使用到了  <a href="https://www.somedoc.net/2025/08/21/pwa-%e5%9c%a8-android-%e4%b8%8a%e7%9a%84%e8%bd%ac%e6%ad%a3/" class="more-link">[&#8230;]</a>]]></description>
										<content:encoded><![CDATA[
<p>一周前让小伙伴尝试构建一个产品的 Web 版，当中使用到了 PWA 技术（Progressive Web Application）。这个产品原本计划就是主要针对 iOS 系统的，不过 PWA 本身是一个非系统特定的技术，因此并没有在一开始就主动排除在其他系统上的运行尝试。</p>



<p>当在一台 Android 设备（貌似是 OPPO A9s，Origin 3）上进行相关操作后，发现了一个有趣的现象。一个需要提前了解的信息是，从某个大版本的 Android 开始，拥有 install shortcut 权限的第三方 app 如果向桌面创建了新的快捷方式，该快捷方式的图标的右下角会自带一个创建者 app 的小图标，系统称之为 badge。似乎是为了向用户明确表明两个信息：第一，这是个快捷方式而非 app；第二，这个快捷方式的创建者是哪个 app。这种完全是工程师化的思维其实在用户那里并不受欢迎，事实上是，无数的用户都在搜索消除掉该 badge（可译为“徽章”）的方法。这是一个系统级别的限制，即便是浏览器创建 PWA 的快捷方式到桌面，也不能例外。</p>



<p>可是例外偏偏就发生了。就在上述提到的那台设备上，经由浏览器创建的一个快捷方式，一开始的时候，能够看到它的图标右下角带着 Chrome 的图标，神奇的是，过了几天后，那个“屈辱性”的 Chrome badge 竟然消失了！也就是说，它似乎被系统认可为一个 app 而不再仅仅是个快捷方式了！但它的几个兄弟姐妹快捷方式就无此殊荣。</p>



<p>三太爷已经接触 Android 系统有些年头了，一时也没想明白为何会出现此现象。只好求助 Grok，在其解释下才能够白其中缘由。AI 一般针对某个问题会给出几个可能的答案，而经过验证，可以确信准确的答案就是这个：</p>



<div class="wp-block-group"><div class="wp-block-group__inner-container is-layout-constrained wp-block-group-is-layout-constrained">
<p><strong>浏览器生成了 WebAPK</strong></p>



<p>当你通过支持 PWA 的浏览器（如 Google Chrome 或 Samsung Internet）将 PWA 添加到主屏幕时，浏览器最初可能会创建一个带有浏览器角标的快捷方式。然而，如果设备支持 WebAPK（一种更原生的 Android 应用包），浏览器可能会在后台通过云端铸造服务器（minting server）生成 WebAPK 文件并替换原有的快捷方式。WebAPK 安装后，图标会呈现为原生应用的样式，不再带有浏览器角标，提供更接近原生应用的体验。</p>
</div></div>



<p>经过了解，这种 WebAPK 必须是具有 Google 移动服务（GMS）的设备才支持，并没有办法从其它切入点进行干预。这恐怕也是 Google 的 Android 霸权的一种体现。</p>



<p>一个 PWA 快捷方式，如何才能进化成功，转变为一个 WebAPK 安装后的 app，必须满足一定的标准，例如：</p>



<ul class="wp-block-list">
<li>提供有效的 <code>manifest.json</code> 文件，包含 <code>name</code>、<code>short_name</code>、<code>start_url</code> 和至少一个符合要求的图标（通常为 512&#215;512 或更大的 PNG 图标，支持 maskable 图标以适配 Android 自适应图标）。</li>



<li>使用 HTTPS 协议。</li>



<li>注册了 Service Worker 以支持离线功能。</li>



<li>设置了 <code>display</code> 属性为 <code>standalone</code> 或 <code>fullscreen</code>。</li>
</ul>



<p>示例 <code>manifest.json</code>：</p>



<pre class="crayon-plain-tag">[crayon-6956a791c0368503998276 inline="true" ]{
  "name": "My PWA",
  "short_name": "PWA",
  "start_url": "/",
  "display": "standalone",
  "icons": &#91;
    {
      "src": "icon-512.png",
      "sizes": "512x512",
      "type": "image/png",
      "purpose": "any maskable"
    }
  ],
  "background_color": "#ffffff",
  "theme_color": "#000000"
}</pre>[/crayon]



<p>用户通过支持 WebAPK 的浏览器（如 Chrome）访问 PWA 网站，点击“添加到主屏幕”选项（通常在浏览器的“三点菜单”中），浏览器最初可能会创建一个带有浏览器角标的快捷方式。</p>



<p>重点来了：浏览器检测到 PWA 满足 WebAPK 要求后，会将 <code>manifest.json</code> 和其他元数据发送到 Google 的云端铸造服务器（WebAPK Minting Server）。服务器根据 PWA 的配置生成一个轻量级 APK 文件（WebAPK），并签名后返回给设备。设备静默安装这个 WebAPK，替换原有的快捷方式。新图标不再带有浏览器角标，看起来像原生应用。</p>



<p>你看，决定权完全在远端。事实上，变成了 WebAPK 的那个快捷方式，其目标 app 是 YouTube，正是 Google 自家的 app，而其它的几个就无此待遇了。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.somedoc.net/2025/08/21/pwa-%e5%9c%a8-android-%e4%b8%8a%e7%9a%84%e8%bd%ac%e6%ad%a3/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Android 中状态栏和导航栏等系统区域的显示控制</title>
		<link>https://www.somedoc.net/2025/07/22/android-%e4%b8%ad%e7%8a%b6%e6%80%81%e6%a0%8f%e5%92%8c%e5%af%bc%e8%88%aa%e6%a0%8f%e7%ad%89%e7%b3%bb%e7%bb%9f%e5%8c%ba%e5%9f%9f%e7%9a%84%e6%98%be%e7%a4%ba%e6%8e%a7%e5%88%b6/</link>
					<comments>https://www.somedoc.net/2025/07/22/android-%e4%b8%ad%e7%8a%b6%e6%80%81%e6%a0%8f%e5%92%8c%e5%af%bc%e8%88%aa%e6%a0%8f%e7%ad%89%e7%b3%bb%e7%bb%9f%e5%8c%ba%e5%9f%9f%e7%9a%84%e6%98%be%e7%a4%ba%e6%8e%a7%e5%88%b6/#respond</comments>
		
		<dc:creator><![CDATA[张三太爷]]></dc:creator>
		<pubDate>Tue, 22 Jul 2025 03:56:33 +0000</pubDate>
				<category><![CDATA[Android]]></category>
		<category><![CDATA[技术]]></category>
		<guid isPermaLink="false">https://www.somedoc.net/?p=6413</guid>

					<description><![CDATA[今天在这儿系统梳理一下 Android 系统中状态栏（Sta <a href="https://www.somedoc.net/2025/07/22/android-%e4%b8%ad%e7%8a%b6%e6%80%81%e6%a0%8f%e5%92%8c%e5%af%bc%e8%88%aa%e6%a0%8f%e7%ad%89%e7%b3%bb%e7%bb%9f%e5%8c%ba%e5%9f%9f%e7%9a%84%e6%98%be%e7%a4%ba%e6%8e%a7%e5%88%b6/" class="more-link">[&#8230;]</a>]]></description>
										<content:encoded><![CDATA[<p>今天在这儿系统梳理一下 Android 系统中状态栏（Status Bar）和导航栏（Navigation Bar）的显示控制，以及它们随着操作系统版本演进的变化。</p>
<h2>核心概念</h2>
<ul>
<li><strong>System Bars（系统栏）</strong>：统称状态栏和导航栏。</li>
<li><strong>Edge-to-Edge（边到边）</strong>：指应用的内容可以绘制到屏幕的边缘，延伸到系统栏的后面。这是现代 Android UI 的推荐做法。</li>
<li><strong>Insets（边衬区）</strong>：系统栏、挖孔、手势区域等占用的空间。应用需要响应 Insets 来避免内容被遮挡。</li>
<li><strong>Window Flags</strong>：用于控制窗口的各种属性，包括系统栏的可见性和行为。</li>
<li><strong>System UI Visibility Flags（View 层级）</strong>：在旧版本中用于控制系统栏的可见性和布局的标志。</li>
<li>
			<span id="crayon-6956a791c057b802195781" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">WindowInsetsController</span></span></span>：较新的 API，用于更细致地控制系统栏的外观和行为。</li>
<li>
			<span id="crayon-6956a791c057f686921106" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">WindowCompat</span><span class="crayon-sy">.</span><span class="crayon-e">setDecorFitsSystemWindows</span><span class="crayon-sy">(</span><span class="crayon-v">window</span><span class="crayon-sy">,</span><span class="crayon-h"> </span><span class="crayon-t">false</span><span class="crayon-sy">)</span></span></span>：启用 Edge-to-Edge 的关键方法。</li>
</ul>
<h2>演进历程和控制方法</h2>
<h3>Android 4.0 (API 14) 及之前 &#8211; 基本控制</h3>
<ul>
<li>状态栏：默认可见且不透明。</li>
<li>导航栏：物理按键为主，部分设备开始出现虚拟导航栏。</li>
<li>控制方式：
<ul>
<li>通过 
			<span id="crayon-6956a791c0581930608893" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">AndroidManifest</span><span class="crayon-sy">.</span><span class="crayon-v">xml</span></span></span> 中的 
			<span id="crayon-6956a791c0582399801202" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">android</span><span class="crayon-o">:</span><span class="crayon-v">theme</span></span></span> 设置主题，例如 
			<span id="crayon-6956a791c0584641632554" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-sy">@</span><span class="crayon-v">android</span><span class="crayon-o">:</span><span class="crayon-v">style</span><span class="crayon-o">/</span><span class="crayon-v">Theme</span><span class="crayon-sy">.</span><span class="crayon-v">NoTitleBar</span><span class="crayon-sy">.</span><span class="crayon-v">Fullscreen</span></span></span> 来隐藏状态栏和标题栏。</li>
<li>通过 
			<span id="crayon-6956a791c0585018825037" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">Window</span><span class="crayon-sy">.</span><span class="crayon-e">addFlags</span><span class="crayon-sy">(</span><span class="crayon-v">WindowManager</span><span class="crayon-sy">.</span><span class="crayon-v">LayoutParams</span><span class="crayon-sy">.</span><span class="crayon-v">FLAG_FULLSCREEN</span><span class="crayon-sy">)</span></span></span> 动态隐藏状态栏。</li>
<li>通过 
			<span id="crayon-6956a791c0587084918330" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">Window</span><span class="crayon-sy">.</span><span class="crayon-e">clearFlags</span><span class="crayon-sy">(</span><span class="crayon-v">WindowManager</span><span class="crayon-sy">.</span><span class="crayon-v">LayoutParams</span><span class="crayon-sy">.</span><span class="crayon-v">FLAG_FULLSCREEN</span><span class="crayon-sy">)</span></span></span> 动态显示状态栏。</li>
<li>导航栏的控制非常有限。</li>
</ul>
</li>
</ul>
<h3>Android 4.1 (API 16) &#8211; &#8220;System UI Visibility&#8221; 和精细化控制</h3>
<ul>
<li>引入了 
			<span id="crayon-6956a791c0588330757302" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">View</span><span class="crayon-sy">.</span><span class="crayon-e">setSystemUiVisibility</span><span class="crayon-sy">(</span><span class="crayon-sy">)</span></span></span> 和一系列 
			<span id="crayon-6956a791c058a648386800" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">SYSTEM_UI_FLAG_</span><span class="crayon-o">*</span></span></span> 常量，允许更灵活地控制系统栏。</li>
<li><strong>关键 Flags</strong>：
<ul>
<li>
			<span id="crayon-6956a791c058b985939603" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">SYSTEM_UI_FLAG_FULLSCREEN</span></span></span>：隐藏状态栏（类似 
			<span id="crayon-6956a791c058c427799944" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">FLAG_FULLSCREEN</span></span></span>）。</li>
<li>
			<span id="crayon-6956a791c058e740707071" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">SYSTEM_UI_FLAG_HIDE_NAVIGATION</span></span></span>：隐藏导航栏。用户与屏幕交互后，导航栏会重新出现。</li>
<li>
			<span id="crayon-6956a791c058f766669781" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN</span></span></span>：即使状态栏可见，应用内容也会布局到状态栏下方。开发者需要处理状态栏遮挡。</li>
<li>
			<span id="crayon-6956a791c0590745257918" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION</span></span></span>：即使导航栏可见，应用内容也会布局到导航栏下方。开发者需要处理导航栏遮挡。</li>
<li>
			<span id="crayon-6956a791c0592165047372" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">SYSTEM_UI_FLAG_LAYOUT_STABLE</span></span></span>：与 
			<span id="crayon-6956a791c0593525972578" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">LAYOUT_FULLSCREEN</span></span></span> 或 
			<span id="crayon-6956a791c0594301252025" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">LAYOUT_HIDE_NAVIGATION</span></span></span> 结合使用，当系统栏显示/隐藏时，保持布局稳定，避免内容跳动。</li>
<li>
			<span id="crayon-6956a791c0596769705352" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">SYSTEM_UI_FLAG_IMMERSIVE</span></span></span>：与 
			<span id="crayon-6956a791c0598164540710" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">HIDE_NAVIGATION</span></span></span> 和 
			<span id="crayon-6956a791c059a746699845" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">FULLSCREEN</span></span></span> 结合，当用户从屏幕边缘滑动时才显示系统栏，提供更沉浸的体验。</li>
<li>
			<span id="crayon-6956a791c059b169794727" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">SYSTEM_UI_FLAG_IMMERSIVE_STICKY</span></span></span>：
			<span id="crayon-6956a791c059d074756877" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">IMMERSIVE</span></span></span> 的变体，系统栏在短暂显示后会自动再次隐藏，更适合游戏或视频播放。</li>
</ul>
</li>
<li>状态栏着色：非常有限，通常是黑色或白色。</li>
</ul>
<h3>Android 4.4 (API 19) &#8211; Translucent System Bars (半透明系统栏)</h3>
<ul>
<li>允许将状态栏和导航栏设置为半透明。</li>
<li>控制方式：
<ul>
<li>在主题中设置 
			<span id="crayon-6956a791c059f811893954" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">android</span><span class="crayon-o">:</span><span class="crayon-v">windowTranslucentStatus</span></span></span> 和 
			<span id="crayon-6956a791c05a1543390433" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">android</span><span class="crayon-o">:</span><span class="crayon-v">windowTranslucentNavigation</span></span></span> 为 
			<span id="crayon-6956a791c05a2379760550" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-t">true</span></span></span>。</li>
<li>或者通过代码 
			<span id="crayon-6956a791c05a3899782556" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">Window</span><span class="crayon-sy">.</span><span class="crayon-e">addFlags</span><span class="crayon-sy">(</span><span class="crayon-v">WindowManager</span><span class="crayon-sy">.</span><span class="crayon-v">LayoutParams</span><span class="crayon-sy">.</span><span class="crayon-v">FLAG_TRANSLUCENT_STATUS</span><span class="crayon-sy">)</span></span></span> 和 
			<span id="crayon-6956a791c05a5460218544" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">Window</span><span class="crayon-sy">.</span><span class="crayon-e">addFlags</span><span class="crayon-sy">(</span><span class="crayon-v">WindowManager</span><span class="crayon-sy">.</span><span class="crayon-v">LayoutParams</span><span class="crayon-sy">.</span><span class="crayon-v">FLAG_TRANSLUCENT_NAVIGATION</span><span class="crayon-sy">)</span></span></span>。</li>
</ul>
</li>
<li>注意：应用内容会自动延伸到半透明系统栏的下方，需要配合 
			<span id="crayon-6956a791c05a6338242862" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">fitsSystemWindows</span><span class="crayon-o">=</span><span class="crayon-s">"true"</span></span></span> 或手动处理 Insets 来避免内容遮挡。</li>
<li>问题：
			<span id="crayon-6956a791c05a7995952393" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">fitsSystemWindows</span></span></span> 属性的行为有时比较复杂和难以预测，尤其是在嵌套布局中。</li>
</ul>
<h3>Android 5.0 (API 21) &#8211; Material Design 和着色控制</h3>
<ul>
<li>状态栏着色：引入了 
			<span id="crayon-6956a791c05a9629379896" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">Window</span><span class="crayon-sy">.</span><span class="crayon-e">setStatusBarColor</span><span class="crayon-sy">(</span><span class="crayon-t">int</span><span class="crayon-h"> </span><span class="crayon-v">color</span><span class="crayon-sy">)</span></span></span> 和 
			<span id="crayon-6956a791c05aa850633940" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">Window</span><span class="crayon-sy">.</span><span class="crayon-e">setNavigationBarColor</span><span class="crayon-sy">(</span><span class="crayon-t">int</span><span class="crayon-h"> </span><span class="crayon-v">color</span><span class="crayon-sy">)</span></span></span>，允许开发者设置系统栏为任意不透明颜色。
<ul>
<li>需要在主题中或代码中先添加 
			<span id="crayon-6956a791c05ab112297373" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">Window</span><span class="crayon-sy">.</span><span class="crayon-e">addFlags</span><span class="crayon-sy">(</span><span class="crayon-v">WindowManager</span><span class="crayon-sy">.</span><span class="crayon-v">LayoutParams</span><span class="crayon-sy">.</span><span class="crayon-v">FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS</span><span class="crayon-sy">)</span></span></span> 并清除 
			<span id="crayon-6956a791c05ad225737831" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">FLAG_TRANSLUCENT_STATUS</span></span></span> / 
			<span id="crayon-6956a791c05af624802679" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">FLAG_TRANSLUCENT_NAVIGATION</span></span></span>。</li>
</ul>
</li>
<li>
			<span id="crayon-6956a791c05b0074940606" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">fitsSystemWindows</span></span></span> 的改进：行为更加一致，但仍是处理 Insets 的主要（且有时棘手）方式。</li>
<li>Light Status Bar（浅色状态栏图标）：
<ul>
<li>
			<span id="crayon-6956a791c05b2757906280" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">SYSTEM_UI_FLAG_LIGHT_STATUS_BAR</span></span></span> (API 23+)：当状态栏背景为浅色时，可以将状态栏图标（时间、电量、通知图标）变为深色，以保证可见性。</li>
</ul>
</li>
</ul>
<h3>Android 6.0 (API 23) &#8211; Light Status Bar （正式引入）</h3>
<ul>
<li>如上所述，正式引入 
			<span id="crayon-6956a791c05b4175886688" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">SYSTEM_UI_FLAG_LIGHT_STATUS_BAR</span></span></span>。</li>
</ul>
<h3>Android 8.0 (API 26) &#8211; Light Navigation Bar（浅色导航栏图标）</h3>
<ul>
<li>引入 
			<span id="crayon-6956a791c05b6725778168" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR</span></span></span>：当导航栏背景为浅色时，可以将导航栏按钮（返回、主页、概览）变为深色。
<ul>
<li>需要设备制造商支持，并且导航栏是半透明或透明的。</li>
</ul>
</li>
</ul>
<h3>Android 9 (API 28) &#8211; Display Cutouts（屏幕挖孔/刘海屏）</h3>
<ul>
<li>随着刘海屏和挖孔屏的出现，需要处理这些区域。</li>
<li>
			<span id="crayon-6956a791c05b7953338171" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">windowLayoutInDisplayCutoutMode</span></span></span>：在主题或窗口属性中设置，控制内容如何与挖孔区域交互：
<ul>
<li>
			<span id="crayon-6956a791c05b9155646629" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-st">default</span></span></span>：默认行为，内容在竖屏时不会延伸到挖孔区域。</li>
<li>
			<span id="crayon-6956a791c05bb393716069" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">shortEdges</span></span></span>：允许内容在竖屏和横屏时都延伸到屏幕短边上的挖孔区域。</li>
<li>
			<span id="crayon-6956a791c05bd628677341" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">never</span></span></span>：内容永远不会延伸到挖孔区域。</li>
</ul>
</li>
<li>
			<span id="crayon-6956a791c05be983405029" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">WindowInsets</span><span class="crayon-sy">.</span><span class="crayon-e">getDisplayCutout</span><span class="crayon-sy">(</span><span class="crayon-sy">)</span></span></span>：获取挖孔区域的详细信息和安全边距。</li>
</ul>
<h3>Android 10 (API 29) &#8211; Gesture Navigation（手势导航）和 Edge-to-Edge 强制</h3>
<ul>
<li>手势导航成为主流：系统导航栏变为一个细条或者完全隐藏，通过手势操作。这对应用UI布局提出了新的要求，因为底部和侧边的手势区域可能与 UI 元素冲突。</li>
<li>Edge-to-Edge 变得更加重要：Google 开始强烈推荐应用采用 Edge-to-Edge 设计。</li>
<li>强制 Edge-to-Edge 的开端：如果 
			<span id="crayon-6956a791c05c8989163380" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">targetSdkVersion</span></span></span> &gt;= 29，系统会更倾向于让应用内容延伸到系统栏后面，即使没有显式设置 
			<span id="crayon-6956a791c05ce532210032" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">LAYOUT_FULLSCREEN</span></span></span> 或 
			<span id="crayon-6956a791c05cf757865163" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">LAYOUT_HIDE_NAVIGATION</span></span></span>。</li>
<li>Mandatory Gesture Areas：引入了系统手势区域的概念，应用应避免在这些区域放置关键交互元素。使用 
			<span id="crayon-6956a791c05d1234941946" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">WindowInsetsCompat</span><span class="crayon-sy">.</span><span class="crayon-v">Type</span><span class="crayon-sy">.</span><span class="crayon-e">systemGestures</span><span class="crayon-sy">(</span><span class="crayon-sy">)</span></span></span> 或 
			<span id="crayon-6956a791c05d2236467035" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">WindowInsetsCompat</span><span class="crayon-sy">.</span><span class="crayon-v">Type</span><span class="crayon-sy">.</span><span class="crayon-e">mandatorySystemGestures</span><span class="crayon-sy">(</span><span class="crayon-sy">)</span></span></span> 获取这些区域的 Insets。</li>
</ul>
<h3>Android 11 (API 30) &#8211; 
			<span id="crayon-6956a791c05d4605531042" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">WindowInsetsController</span></span></span> 和更一致的 API</h3>
<ul>
<li>废弃 
			<span id="crayon-6956a791c05d5355904520" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">View</span><span class="crayon-sy">.</span><span class="crayon-e">setSystemUiVisibility</span><span class="crayon-sy">(</span><span class="crayon-sy">)</span></span></span>：这个 API 因为其复杂性和难以理解的行为而被废弃。</li>
<li>引入 
			<span id="crayon-6956a791c05d6554221846" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">WindowInsetsController</span></span></span>：提供了一套更现代化、更直观的 API 来控制系统栏的可见性、行为和外观。</li>
<li>
			<span id="crayon-6956a791c05d8504932514" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">WindowCompat</span><span class="crayon-sy">.</span><span class="crayon-e">getInsetsController</span><span class="crayon-sy">(</span><span class="crayon-v">window</span><span class="crayon-sy">,</span><span class="crayon-h"> </span><span class="crayon-v">view</span><span class="crayon-sy">)</span></span></span> 获取控制器。</li>
<li>
			<span id="crayon-6956a791c05d9455640557" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-e">hide</span><span class="crayon-sy">(</span><span class="crayon-v">WindowInsets</span><span class="crayon-sy">.</span><span class="crayon-v">Type</span><span class="crayon-sy">.</span><span class="crayon-e">statusBars</span><span class="crayon-sy">(</span><span class="crayon-sy">)</span><span class="crayon-sy">)</span></span></span> / 
			<span id="crayon-6956a791c05da090457388" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-e">hide</span><span class="crayon-sy">(</span><span class="crayon-v">WindowInsets</span><span class="crayon-sy">.</span><span class="crayon-v">Type</span><span class="crayon-sy">.</span><span class="crayon-e">navigationBars</span><span class="crayon-sy">(</span><span class="crayon-sy">)</span><span class="crayon-sy">)</span></span></span></li>
<li>
			<span id="crayon-6956a791c05dc646602957" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-e">show</span><span class="crayon-sy">(</span><span class="crayon-v">WindowInsets</span><span class="crayon-sy">.</span><span class="crayon-v">Type</span><span class="crayon-sy">.</span><span class="crayon-e">statusBars</span><span class="crayon-sy">(</span><span class="crayon-sy">)</span><span class="crayon-sy">)</span></span></span> / 
			<span id="crayon-6956a791c05dd720717725" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-e">show</span><span class="crayon-sy">(</span><span class="crayon-v">WindowInsets</span><span class="crayon-sy">.</span><span class="crayon-v">Type</span><span class="crayon-sy">.</span><span class="crayon-e">navigationBars</span><span class="crayon-sy">(</span><span class="crayon-sy">)</span><span class="crayon-sy">)</span></span></span></li>
<li>
			<span id="crayon-6956a791c05de044565522" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-e">setSystemBarsBehavior</span><span class="crayon-sy">(</span><span class="crayon-v">BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE</span><span class="crayon-sy">)</span></span></span>：类似于 
			<span id="crayon-6956a791c05e0169040601" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">IMMERSIVE_STICKY</span></span></span>。</li>
<li>
			<span id="crayon-6956a791c05e1110597013" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">BEHAVIOR_SHOW_BARS_BY_TOUCH</span></span></span>：用户触摸即显示。</li>
<li>
			<span id="crayon-6956a791c05e2697609186" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">BEHAVIOR_SHOW_BARS_BY_SWIPE</span></span></span>：用户滑动边缘显示。</li>
<li>
			<span id="crayon-6956a791c05e4713893884" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-e">setSystemBarsAppearance</span><span class="crayon-sy">(</span><span class="crayon-v">APPEARANCE_LIGHT_STATUS_BARS</span><span class="crayon-sy">,</span><span class="crayon-h"> </span><span class="crayon-v">APPEARANCE_LIGHT_STATUS_BARS</span><span class="crayon-sy">)</span></span></span>：控制状态栏图标颜色。</li>
<li>
			<span id="crayon-6956a791c05e5622498949" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-e">setSystemBarsAppearance</span><span class="crayon-sy">(</span><span class="crayon-v">APPEARANCE_LIGHT_NAVIGATION_BARS</span><span class="crayon-sy">,</span><span class="crayon-h"> </span><span class="crayon-v">APPEARANCE_LIGHT_NAVIGATION_BARS</span><span class="crayon-sy">)</span></span></span>：控制导航栏图标颜色。</li>
<li>
			<span id="crayon-6956a791c05e6557104395" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">WindowCompat</span><span class="crayon-sy">.</span><span class="crayon-e">setDecorFitsSystemWindows</span><span class="crayon-sy">(</span><span class="crayon-v">window</span><span class="crayon-sy">,</span><span class="crayon-h"> </span><span class="crayon-t">false</span><span class="crayon-sy">)</span></span></span>：成为启用 Edge-to-Edge 的标准方法。当设置为 
			<span id="crayon-6956a791c05e8226955033" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-t">false</span></span></span> 时，应用内容会延伸到系统栏后面。开发者必须使用 
			<span id="crayon-6956a791c05e9334130470" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">setOnApplyWindowInsetsListener</span></span></span> 来处理 Insets。</li>
</ul>
<h3>Android 12 (API 31) 及之后 &#8211; 默认启用 Edge-to-Edge（针对特定情况）</h3>
<ul>
<li>系统进一步推动 Edge-to-Edge。</li>
<li>对于使用手势导航且 
			<span id="crayon-6956a791c05ea010610347" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">targetSdkVersion</span></span></span> &gt;= 31 的应用，系统可能会默认强制应用以 Edge-to-Edge 方式运行。</li>
</ul>
<h3>Android 15 (API 35) &#8211; 默认启用 Edge-to-Edge（全面）</h3>
<ul>
<li>
			<span id="crayon-6956a791c05ec085354388" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">targetSdk</span></span></span> 升级到 35 后，应用会默认启用 edge-to-edge 显示。这意味着 
			<span id="crayon-6956a791c05ed705824874" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">WindowCompat</span><span class="crayon-sy">.</span><span class="crayon-e">setDecorFitsSystemWindows</span><span class="crayon-sy">(</span><span class="crayon-v">window</span><span class="crayon-sy">,</span><span class="crayon-h"> </span><span class="crayon-t">false</span><span class="crayon-sy">)</span></span></span> 的行为成为默认，开发者必须处理 Insets。</li>
</ul>
<h2>现代（推荐）的控制方法 (API 30+)</h2>
<ol>
<li>启用 Edge-to-Edge：在 Activity 的 
			<span id="crayon-6956a791c05ee255468967" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">onCreate</span></span></span> 方法中，
			<span id="crayon-6956a791c05f0215692335" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-r">super</span><span class="crayon-sy">.</span><span class="crayon-e">onCreate</span><span class="crayon-sy">(</span><span class="crayon-sy">)</span></span></span> 之后，
			<span id="crayon-6956a791c05f1416553206" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-e">setContentView</span><span class="crayon-sy">(</span><span class="crayon-sy">)</span></span></span> 之前调用：
			<span id="crayon-6956a791c05f2833024393" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">WindowCompat</span><span class="crayon-sy">.</span><span class="crayon-e">setDecorFitsSystemWindows</span><span class="crayon-sy">(</span><span class="crayon-v">window</span><span class="crayon-sy">,</span><span class="crayon-h"> </span><span class="crayon-t">false</span><span class="crayon-sy">)</span></span></span></li>
</ol>
<ol start="2">
<li>处理 Insets：使用 
			<span id="crayon-6956a791c05f4251273749" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">ViewCompat</span><span class="crayon-sy">.</span><span class="crayon-v">setOnApplyWindowInsetsListener</span></span></span> 来获取系统栏、挖孔、手势区域等的大小，并相应地调整你布局中视图的 
			<span id="crayon-6956a791c05f5368264644" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">padding</span></span></span> 或 
			<span id="crayon-6956a791c05f6771420951" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">margin</span></span></span>。</li>
</ol>
<p></p><pre class="crayon-plain-tag">[crayon-6956a791c05f9385922341 inline="true"  class="language-kotlin" lang="kotlin"]ViewCompat.setOnApplyWindowInsetsListener(yourRootViewOrSpecificView) { view, windowInsets -&gt;
    val systemBarsInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
    val displayCutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
    val imeInsets = windowInsets.getInsets(WindowInsetsCompat.Type.ime()) // 软键盘

    view.updatePadding(
        left = systemBarsInsets.left + displayCutoutInsets.left,
        top = systemBarsInsets.top + displayCutoutInsets.top,
        right = systemBarsInsets.right + displayCutoutInsets.right,
        bottom = systemBarsInsets.bottom + displayCutoutInsets.bottom // 或 imeInsets.bottom 当键盘弹出时
    )

    // 或者只处理特定方向，例如只为顶部 AppBar 添加状态栏高度的 padding
    // appBar.updatePadding(top = systemBarsInsets.top)
    // bottomNav.updatePadding(bottom = systemBarsInsets.bottom)

    WindowInsetsCompat.CONSUMED // 或者返回修改后的 insets
}</pre><p>[/crayon]</p>
<ol start="3">
<li>控制系统栏可见性和外观（
			<span id="crayon-6956a791c05fb192824666" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">WindowInsetsController</span></span></span>）：</li>
</ol>
<p></p><pre class="crayon-plain-tag">[crayon-6956a791c05fd995772102 inline="true"  class="language-kotlin" lang="kotlin"]val insetsController = WindowCompat.getInsetsController(window, window.decorView)

// 隐藏状态栏
insetsController?.hide(WindowInsetsCompat.Type.statusBars())
// 显示状态栏
insetsController?.show(WindowInsetsCompat.Type.statusBars())

// 设置状态栏图标为深色 (当状态栏背景为浅色时)
insetsController?.isAppearanceLightStatusBars = true
// 设置导航栏按钮为深色 (当导航栏背景为浅色时)
insetsController?.isAppearanceLightNavigationBars = true

// 控制系统栏在隐藏后的行为 (例如滑动边缘显示)
insetsController?.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE</pre><p>[/crayon]</p>
<ol start="4">
<li>设置系统栏颜色：即使内容延伸到系统栏后面，你仍然可以为它们设置半透明或透明的背景色，以获得更好的视觉效果。 在主题 
			<span id="crayon-6956a791c05ff777500731" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">themes</span><span class="crayon-sy">.</span><span class="crayon-v">xml</span></span></span>（推荐 values-v21 及以上）：</li>
</ol>
<p></p><pre class="crayon-plain-tag">[crayon-6956a791c0601134651217 inline="true"  class="language-xml" lang="xml"]&lt;item name="android:statusBarColor"&gt;@android:color/transparent&lt;/item&gt;
&lt;item name="android:navigationBarColor"&gt;@android:color/transparent&lt;/item&gt;</pre><p>[/crayon]<br />
或者代码中（需要在 
			<span id="crayon-6956a791c0603177134376" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS</span></span></span> 被设置后）：</p><pre class="crayon-plain-tag">[crayon-6956a791c0605242939255 inline="true"  class="language-kotlin" lang="kotlin"]window.statusBarColor = Color.TRANSPARENT
window.navigationBarColor = Color.TRANSPARENT</pre><p>[/crayon]<br />
如果你希望系统栏有一个半透明的遮罩效果（例如在深色模式下），可以使用类似 
			<span id="crayon-6956a791c0607462384851" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-p">#80000000</span></span></span>（50% 透明度的黑色）的颜色。</p>
<h2>总结和最佳实践</h2>
<ul>
<li>拥抱 Edge-to-Edge：这是现代 Android 应用的趋势。</li>
<li>使用 
			<span id="crayon-6956a791c0608259133245" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">WindowCompat</span><span class="crayon-sy">.</span><span class="crayon-e">setDecorFitsSystemWindows</span><span class="crayon-sy">(</span><span class="crayon-v">window</span><span class="crayon-sy">,</span><span class="crayon-h"> </span><span class="crayon-t">false</span><span class="crayon-sy">)</span></span></span> 启用它。</li>
<li>核心是处理 Insets：使用 
			<span id="crayon-6956a791c0609759065264" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">ViewCompat</span><span class="crayon-sy">.</span><span class="crayon-v">setOnApplyWindowInsetsListener</span></span></span> 和 
			<span id="crayon-6956a791c060b412091405" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">WindowInsetsCompat</span><span class="crayon-sy">.</span><span class="crayon-v">Type</span><span class="crayon-sy">.</span><span class="crayon-o">*</span></span></span>。</li>
<li>使用 
			<span id="crayon-6956a791c060c439325570" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">WindowInsetsController</span></span></span> (API 30+) 控制系统栏的可见性、行为和图标颜色。</li>
<li>避免使用废弃的 
			<span id="crayon-6956a791c060d854648434" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">View</span><span class="crayon-sy">.</span><span class="crayon-e">setSystemUiVisibility</span><span class="crayon-sy">(</span><span class="crayon-sy">)</span></span></span>。</li>
<li>为系统栏设置透明或半透明背景色，以配合 Edge-to-Edge 设计。</li>
<li>在不同设备和 Android 版本上充分测试，特别注意有挖孔和使用手势导航的设备。</li>
<li>利用 Material Components：许多 Material Design 组件（如 
			<span id="crayon-6956a791c060f437510276" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">AppBarLayout</span></span></span>、
			<span id="crayon-6956a791c0610668386126" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">BottomAppBar</span></span></span>、
			<span id="crayon-6956a791c0611597890794" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">CoordinatorLayout</span></span></span>）已经内置了对 Insets 的良好处理。</li>
</ul>
<p>这个演进过程确实比较复杂，但理解了这些核心概念和 API 的变化，就能更好地掌控应用的视觉表现，并提供更沉浸的用户体验。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.somedoc.net/2025/07/22/android-%e4%b8%ad%e7%8a%b6%e6%80%81%e6%a0%8f%e5%92%8c%e5%af%bc%e8%88%aa%e6%a0%8f%e7%ad%89%e7%b3%bb%e7%bb%9f%e5%8c%ba%e5%9f%9f%e7%9a%84%e6%98%be%e7%a4%ba%e6%8e%a7%e5%88%b6/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Android 应用中主题的几种变换方式</title>
		<link>https://www.somedoc.net/2025/04/29/android-%e5%ba%94%e7%94%a8%e4%b8%ad%e4%b8%bb%e9%a2%98%e7%9a%84%e5%87%a0%e7%a7%8d%e5%8f%98%e6%8d%a2%e6%96%b9%e5%bc%8f/</link>
					<comments>https://www.somedoc.net/2025/04/29/android-%e5%ba%94%e7%94%a8%e4%b8%ad%e4%b8%bb%e9%a2%98%e7%9a%84%e5%87%a0%e7%a7%8d%e5%8f%98%e6%8d%a2%e6%96%b9%e5%bc%8f/#respond</comments>
		
		<dc:creator><![CDATA[张三太爷]]></dc:creator>
		<pubDate>Tue, 29 Apr 2025 07:20:24 +0000</pubDate>
				<category><![CDATA[Android]]></category>
		<category><![CDATA[问题解决]]></category>
		<guid isPermaLink="false">https://www.somedoc.net/?p=6380</guid>

					<description><![CDATA[本篇文章是由一个小疑点慢慢扩展出来的需要在探寻的过程中形成的 <a href="https://www.somedoc.net/2025/04/29/android-%e5%ba%94%e7%94%a8%e4%b8%ad%e4%b8%bb%e9%a2%98%e7%9a%84%e5%87%a0%e7%a7%8d%e5%8f%98%e6%8d%a2%e6%96%b9%e5%bc%8f/" class="more-link">[&#8230;]</a>]]></description>
										<content:encoded><![CDATA[<p>本篇文章是由一个小疑点慢慢扩展出来的需要在探寻的过程中形成的。</p>
<p>在之前的产品中，实现了动态切换 app 主题（主要是整体色调）的功能，在 app 中实际应用多年，此后一直没有再变过需求，因此代码也一直维持没动。它工作得很好。不过，这份代码不止存在于实战 app 中，它还存在于一个测试工程里，问题，就是从测试工程里开始暴露的。</p>
<p>通常来说，对于任何一个工程师，测试工程都不会只有一个，而是会有一堆。“俺也一样”。这些代码短期无用，抛弃可惜。于是老夫就计划把它们拢到一个工程里，上一段提到的那个测试工程，被我选定为了基础工程，至于其它测试工程，只把其中的技术点并入到基础工程中后就要废弃了。一次合并某个 Activity 后，要查看一下相同的代码在原先的工程中和迁移后的工程中表现是否保持一致，发现略有不同：原先的测试工程中的 Activity，显示了 ActionBar，而合并到基础工程中以后，ActionBar 不显示了。由于 ActionBar 是集成环境的向导生成框架代码时的自动产物，也没有实际用到，所以当时只是感觉奇怪了一下，也就没有深究。</p>
<p>可问题前两天又一次出现了，这次是迁移了另一个 Activity。跟前次不同的在于，这个 Activity 里有的操作入口在菜单里，而菜单的入口在 ActionBar 上。可这 ActionBar 不明不白地玩儿失踪，我还怎么测功能？到 manifest 里面修改 theme 属性，指定为明确的带有 ActionBar 的主题，一点作用都没有。在 onCreate 里使用 getSupportActionBar 方法检查，把把都是返回 null。这时，我突然想到了那个一直被忽略的 ThemeManager。到 Application 类里一看，果然有它的初始化，注释掉之后，ActionBar 回来了。再一查，它掌控的数十个不同色调的主题，都派生于一个以系统的 NoActionBar 系的主题为父主题的基础主题。</p>
<p>真相终于大白。ThemeManager 监控了 app 内所有 Activity 的生命周期，在创建之初就会一视同仁地将主题设置为它自己的，而没有顾及要兼容 Activity 原先主题内 ActionBar 的有无。当我询问 AI 可以怎么解决时，AI 的第一反馈是：你可以创建一批带有 ActionBar 的主题供 ThemeManager 使用。一下把我惊呆了，几十个呢，全都复制一遍？就是改个根主题？幸亏老夫还没傻糊涂，又变着法儿问了一次，于是有了以下的几个选择。</p>
<p>Q：之前为了能够切换主题色，做了数十个主题，都是以 NoActionBar 为基础的，现在要兼容带有 ActionBar 的，难道只能把这几十个主题都复制一份，而仅仅是把 parent 改成另一个？</p>
<p>A：你的顾虑非常合理！不需要为每个 NoActionBar 主题手动复制一份带 ActionBar 的变体。以下是高效且可维护的解决方案：</p>
<h3>方案一：使用主题继承 + 覆盖属性（推荐）</h3>
<h4>1. <strong>定义基础主题层级</strong></h4>
<p></p><pre class="crayon-plain-tag">&lt;!-- 基础主题（无 ActionBar） --&gt;
&lt;style name="Theme.App.Base" parent="Theme.Material3.Light.NoActionBar"&gt;
    &lt;item name="colorPrimary"&gt;@color/primary&lt;/item&gt;
    &lt;!-- 其他通用属性 --&gt;
&lt;/style&gt;

&lt;!-- 带 ActionBar 的基础变体 --&gt;
&lt;style name="Theme.App.Base.WithActionBar" parent="Theme.Material3.Light"&gt;
    &lt;!-- 继承所有基础属性 --&gt;
    &lt;item name="colorPrimary"&gt;@color/primary&lt;/item&gt;
&lt;/style&gt;</pre><p></p>
<h4>2. <strong>动态生成主题</strong></h4>
<p class="ds-markdown-paragraph">在代码中通过 <strong>主题叠加</strong> 动态生成最终主题：</p>
<p></p><pre class="crayon-plain-tag">public static void applyTheme(Activity activity) {
    boolean needActionBar = hasOriginalActionBar(activity);
    int baseTheme = needActionBar ? R.style.Theme_App_Base_WithActionBar 
                                : R.style.Theme_App_Base;

    // 动态创建叠加主题
    ContextThemeWrapper wrapper = new ContextThemeWrapper(
        activity, 
        baseTheme
    );
    
    // 应用当前颜色配置
    TypedArray ta = wrapper.obtainStyledAttributes(new int[]{
        R.attr.colorPrimary,
        R.attr.colorSecondary
    });
    int colorPrimary = ta.getColor(0, Color.BLACK);
    ta.recycle();

    // 设置到 Activity
    activity.setTheme(baseTheme);
    if (needActionBar) {
        activity.getWindow().setStatusBarColor(colorPrimary);
    }
}</pre><p></p>
<h3>方案二：使用 
			<span id="crayon-6956a791c0c0c783319052" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-v">ThemeOverlay</span></span></span> 动态覆盖</h3>
<h4>1. <strong>定义 ActionBar 覆盖层</strong></h4>
<p></p><pre class="crayon-plain-tag">&lt;!-- 覆盖层：添加 ActionBar 支持 --&gt;
&lt;style name="ThemeOverlay.AddActionBar" parent=""&gt;
    &lt;item name="windowActionBar"&gt;true&lt;/item&gt;
    &lt;item name="windowNoTitle"&gt;false&lt;/item&gt;
&lt;/style&gt;</pre><p></p>
<h4>2. <strong>动态组合主题</strong></h4>
<p></p><pre class="crayon-plain-tag">// 获取当前主题资源 ID（假设是某个 NoActionBar 主题）
int currentTheme = R.style.Theme_App_Red_NoActionBar; 

// 创建动态主题配置
ContextThemeWrapper wrapper = new ContextThemeWrapper(
    activity,
    currentTheme
);

// 如果需要 ActionBar，叠加覆盖层
if (needActionBar) {
    wrapper.applyStyle(R.style.ThemeOverlay_AddActionBar, true);
}

// 应用到 Activity
activity.setTheme(wrapper.getTheme());</pre><p></p>
<h3>方案三：使用 XML 的 
			<span id="crayon-6956a791c0c10281040596" class="crayon-syntax crayon-syntax-inline  crayon-theme-visual-assist crayon-theme-visual-assist-inline crayon-font-droid-sans-mono" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important;"><span class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 16px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"><span class="crayon-r">parent</span></span></span> 链式继承</h3>
<h4>1. <strong>利用抽象中间层</strong></h4>
<p></p><pre class="crayon-plain-tag">&lt;!-- 所有主题的抽象父级 --&gt;
&lt;style name="Theme.App.Abstract" parent=""&gt;
    &lt;item name="colorPrimary"&gt;@color/primary&lt;/item&gt;
    &lt;!-- 其他共享属性 --&gt;
&lt;/style&gt;

&lt;!-- 具体实现层 --&gt;
&lt;style name="Theme.App.Red" parent="Theme.App.Abstract"&gt;
    &lt;item name="colorPrimary"&gt;@color/red&lt;/item&gt;
&lt;/style&gt;

&lt;!-- 运行时动态决定最终 parent --&gt;
&lt;style name="Theme.App.Red.Final" parent="Theme.App.Red"&gt;
    &lt;!-- 空实现，由代码决定 parent --&gt;
&lt;/style&gt;</pre><p></p>
<h4>2. <strong>代码中动态指定parent</strong></h4>
<p></p><pre class="crayon-plain-tag">// 在 ThemeManager 中
int finalParent = needActionBar 
    ? android.R.style.Theme_Material 
    : android.R.style.Theme_Material_NoActionBar;

// 通过反射动态修改 parent
Resources.Theme theme = activity.getTheme();
try {
    Field mThemeResId = theme.getClass().getDeclaredField("mThemeResId");
    mThemeResId.setAccessible(true);
    mThemeResId.set(theme, R.style.Theme_App_Red_Final);
    
    Field mTheme = theme.getClass().getDeclaredField("mTheme");
    mTheme.setAccessible(true);
    ((ResourcesImpl.ThemeImpl)mTheme.get(theme)).setBase(finalParent);
} catch (Exception e) {
    // 降级处理
}</pre><p></p>
<h3>对比方案优劣</h3>
<div class="markdown-table-wrapper">
<table>
<thead>
<tr>
<th>方案</th>
<th>优点</th>
<th>缺点</th>
<th>适用场景</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>继承+覆盖</strong></td>
<td>无反射，兼容性好</td>
<td>需维护基础主题</td>
<td>大多数项目</td>
</tr>
<tr>
<td><strong>ThemeOverlay</strong></td>
<td>灵活度高</td>
<td>需处理属性冲突</td>
<td>需要精细控制时</td>
</tr>
<tr>
<td><strong>动态parent</strong></td>
<td>最少的重复代码</td>
<td>反射有风险</td>
<td>激进的技术栈</td>
</tr>
</tbody>
</table>
<h3>终极建议：<strong>方案一 + 代码生成</strong></h3>
<ol start="1">
<li>
<p class="ds-markdown-paragraph"><strong>使用 Python/Gradle 脚本</strong> 自动生成带 ActionBar 的变体：</p>
<p></p><pre class="crayon-plain-tag"># build-themes.py
for color in ["red", "blue", "green"]:
    with open(f"res/values/themes_{color}.xml", "w") as f:
        f.write(f"""
        &lt;style name="Theme.App.{color}" parent="Theme.App.Base"/&gt;
        &lt;style name="Theme.App.{color}.WithActionBar" 
               parent="Theme.App.Base.WithActionBar"/&gt;
        """)</pre><p>
</li>
<li>
<p class="ds-markdown-paragraph"><strong>在 build.gradle 中配置</strong>：</p>
<p></p><pre class="crayon-plain-tag">task generateThemes(type: Exec) {
    commandLine 'python', 'build-themes.py'
}
preBuild.dependsOn generateThemes</pre><p>这样既能保持代码整洁，又避免了手动维护重复主题。</li>
</ol>
<hr />
<p>其实吧，出于老夫的审美，显然更倾向于方案二或者三。</p>
</div>
]]></content:encoded>
					
					<wfw:commentRss>https://www.somedoc.net/2025/04/29/android-%e5%ba%94%e7%94%a8%e4%b8%ad%e4%b8%bb%e9%a2%98%e7%9a%84%e5%87%a0%e7%a7%8d%e5%8f%98%e6%8d%a2%e6%96%b9%e5%bc%8f/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>DeepSeek 答疑第二弹</title>
		<link>https://www.somedoc.net/2025/03/15/deepseek-%e7%ad%94%e7%96%91%e7%ac%ac%e4%ba%8c%e5%bc%b9/</link>
					<comments>https://www.somedoc.net/2025/03/15/deepseek-%e7%ad%94%e7%96%91%e7%ac%ac%e4%ba%8c%e5%bc%b9/#respond</comments>
		
		<dc:creator><![CDATA[张三太爷]]></dc:creator>
		<pubDate>Fri, 14 Mar 2025 16:32:06 +0000</pubDate>
				<category><![CDATA[Android]]></category>
		<category><![CDATA[技术]]></category>
		<category><![CDATA[问题解决]]></category>
		<guid isPermaLink="false">https://www.somedoc.net/?p=6342</guid>

					<description><![CDATA[Q：LayoutInflater 的 inflate 方法可 <a href="https://www.somedoc.net/2025/03/15/deepseek-%e7%ad%94%e7%96%91%e7%ac%ac%e4%ba%8c%e5%bc%b9/" class="more-link">[&#8230;]</a>]]></description>
										<content:encoded><![CDATA[<p>Q：LayoutInflater 的 inflate 方法可以接受一个 layout 的 id，在此加载过程中，有没有什么办法对加载进行拦截，对布局实现某种方式的动态修改？</p>
<p>A：在 Android 中，如果你想在 <code>LayoutInflater</code> 加载布局时拦截并动态修改布局，可以通过自定义 <code>LayoutInflater.Factory2</code> 来实现。<code>Factory2</code> 允许你在布局加载过程中拦截视图的创建，并对其进行修改或替换。</p>
<h3>实现步骤</h3>
<ol start="1">
<li><strong>自定义 <code>LayoutInflater.Factory2</code></strong>:
<ul>
<li>通过实现 <code>LayoutInflater.Factory2</code> 接口，你可以在视图创建时拦截并修改布局。</li>
</ul>
</li>
<li><strong>设置自定义 <code>Factory2</code></strong>:
<ul>
<li>将自定义的 <code>Factory2</code> 设置到 <code>LayoutInflater</code> 中，使其生效。</li>
</ul>
</li>
<li><strong>在 <code>onCreateView</code> 中拦截视图创建</strong>:
<ul>
<li>在 <code>onCreateView</code> 方法中，你可以根据需要修改或替换视图。</li>
</ul>
</li>
</ol>
<h3>示例代码</h3>
<p>在 <code>LayoutInflater.Factory2</code> 的 <code>onCreateView</code> 方法中将 <code>TextView</code> 替换为 <code>ImageView</code>。以下是实现这一功能的完整示例。</p>
<h4>1. 自定义 <code>LayoutInflater.Factory2</code></h4>
<p></p><pre class="crayon-plain-tag">import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;

public class CustomLayoutInflaterFactory implements LayoutInflater.Factory2 {

    private final LayoutInflater.Factory2 originalFactory;

    public CustomLayoutInflaterFactory(LayoutInflater.Factory2 originalFactory) {
        this.originalFactory = originalFactory;
    }

    @Override
    public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
        // 拦截视图创建
        View view = null;

        // 如果视图是 TextView，替换为 ImageView
        if ("TextView".equals(name)) {
            // 创建 ImageView
            view = new ImageView(context, attrs);
            // 设置 ImageView 的属性（例如设置图片资源）
            ((ImageView) view).setImageResource(R.drawable.ic_launcher_foreground); // 替换为你的图片资源
        }

        // 如果不需要拦截，调用原始的 Factory2
        if (view == null &amp;&amp; originalFactory != null) {
            view = originalFactory.onCreateView(parent, name, context, attrs);
        }

        return view;
    }

    @Override
    public View onCreateView(String name, Context context, AttributeSet attrs) {
        return onCreateView(null, name, context, attrs);
    }
}</pre><p></p>
<h4>2. 设置自定义 <code>Factory2</code></h4>
<p>在你的 <code>Activity</code> 中设置自定义的 <code>Factory2</code>：</p><pre class="crayon-plain-tag">import android.app.Activity;
import android.os.Bundle;
import android.view.LayoutInflater;

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // 设置自定义的 LayoutInflater.Factory2
        LayoutInflater inflater = LayoutInflater.from(this);
        if (inflater.getFactory2() == null) {
            inflater.setFactory2(new CustomLayoutInflaterFactory(inflater.getFactory2()));
        } else {
            // 如果已经设置了 Factory2，可以替换或包装它
            inflater.setFactory2(new CustomLayoutInflaterFactory(inflater.getFactory2()));
        }

        super.onCreate(savedInstanceState);

        // 加载布局
        setContentView(R.layout.activity_main);
    }
}</pre><p></p>
<h4>3. 测试布局 (<code>activity_main.xml</code>)</h4>
<p></p><pre class="crayon-plain-tag">&lt;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"&gt;

    &lt;TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Original TextView" /&gt;

    &lt;Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Click Me" /&gt;
&lt;/LinearLayout&gt;</pre><p></p>
<h4>4. 添加图片资源</h4>
<p>确保在 <code>res/drawable</code> 目录下有一个图片资源（例如 <code>ic_launcher_foreground.png</code>），用于替换 <code>TextView</code>。</p>
<h3>运行结果</h3>
<ul>
<li>原本的 <code>TextView</code> 会被替换为 <code>ImageView</code>，并显示指定的图片资源。</li>
<li>其他视图（如 <code>Button</code>）不受影响。</li>
</ul>
<h3>关键点</h3>
<ol start="1">
<li><strong>替换逻辑</strong>：
<ul>
<li>在 <code>onCreateView</code> 方法中，通过判断 <code>name</code> 是否为 <code>"TextView"</code>，决定是否替换为 <code>ImageView</code>。</li>
<li>使用 <code>new ImageView(context, attrs)</code> 创建 <code>ImageView</code>，并保留原始的属性（如 <code>layout_width</code> 和 <code>layout_height</code>）。</li>
</ul>
</li>
<li><strong>属性传递</strong>：
<ul>
<li><code>AttributeSet attrs</code> 包含了 XML 中定义的属性（如 <code>android:layout_width</code> 和 <code>android:layout_height</code>），这些属性会自动应用到新创建的 <code>ImageView</code> 上。</li>
</ul>
</li>
<li><strong>图片资源</strong>：
<ul>
<li>通过 <code>setImageResource</code> 方法为 <code>ImageView</code> 设置图片资源。</li>
</ul>
</li>
</ol>
<h3>注意事项</h3>
<ul>
<li><strong>属性兼容性</strong>：
<ul>
<li>如果 <code>TextView</code> 有一些 <code>ImageView</code> 不支持的属性（如 <code>android:text</code>），这些属性会被忽略。</li>
<li>如果需要保留某些属性，可以手动解析 <code>AttributeSet</code> 并应用到 <code>ImageView</code>。</li>
</ul>
</li>
<li><strong>性能</strong>：
<ul>
<li>拦截布局加载会增加一定的性能开销，尤其是在布局复杂的情况下。</li>
</ul>
</li>
<li><strong>适用范围</strong>：
<ul>
<li>这种方式适用于全局替换特定视图的场景，例如主题切换、动态换肤等。</li>
</ul>
</li>
</ul>
<p>通过这种方式，你可以灵活地将 <code>TextView</code> 替换为 <code>ImageView</code>，并实现更复杂的布局动态修改。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.somedoc.net/2025/03/15/deepseek-%e7%ad%94%e7%96%91%e7%ac%ac%e4%ba%8c%e5%bc%b9/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>DeepSeek 答疑第一弹</title>
		<link>https://www.somedoc.net/2025/03/14/deepseek-%e7%ad%94%e7%96%91%e7%ac%ac%e4%b8%80%e5%bc%b9/</link>
					<comments>https://www.somedoc.net/2025/03/14/deepseek-%e7%ad%94%e7%96%91%e7%ac%ac%e4%b8%80%e5%bc%b9/#respond</comments>
		
		<dc:creator><![CDATA[张三太爷]]></dc:creator>
		<pubDate>Fri, 14 Mar 2025 15:53:52 +0000</pubDate>
				<category><![CDATA[Android]]></category>
		<category><![CDATA[技术]]></category>
		<category><![CDATA[问题解决]]></category>
		<guid isPermaLink="false">https://www.somedoc.net/?p=6340</guid>

					<description><![CDATA[之前有过的一个想法，不过一直没有着手去查过资料，今天突然又想 <a href="https://www.somedoc.net/2025/03/14/deepseek-%e7%ad%94%e7%96%91%e7%ac%ac%e4%b8%80%e5%bc%b9/" class="more-link">[&#8230;]</a>]]></description>
										<content:encoded><![CDATA[<p>之前有过的一个想法，不过一直没有着手去查过资料，今天突然又想到了，就问了一声 DeepSeek。问答暂存于此。</p>
<p>Q：在 Android 下想使用代码动态生成一个布局 XML，然后再用 layout inflator 把它加载成视图树，有可行的办法吗？</p>
<p>A：在 Android 中，你可以通过代码动态生成布局 XML，然后使用 <code>LayoutInflater</code> 将其加载为视图树。以下是具体步骤：</p>
<h3>1. 动态生成布局 XML</h3>
<p>你可以使用 <code>XmlPullParser</code> 或直接构建 XML 字符串来生成布局。</p>
<h3>2. 使用 <code>LayoutInflater</code> 加载布局</h3>
<p>将生成的 XML 字符串解析为 <code>XmlResourceParser</code>，然后通过 <code>LayoutInflater</code> 加载。</p>
<h3>示例代码</h3>
<p></p><pre class="crayon-plain-tag">import android.content.Context;
import android.util.Xml;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
import org.xmlpull.v1.XmlPullParser;
import java.io.StringReader;

public class DynamicLayout {

    public static View createDynamicLayout(Context context) {
        // 动态生成 XML 布局字符串
        String xmlLayout = "&lt;LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\" "
                + "android:layout_width=\"match_parent\" "
                + "android:layout_height=\"match_parent\" "
                + "android:orientation=\"vertical\"&gt;"
                + "&lt;TextView "
                + "android:id=\"@+id/textView\" "
                + "android:layout_width=\"wrap_content\" "
                + "android:layout_height=\"wrap_content\" "
                + "android:text=\"Hello, Dynamic Layout!\" /&gt;"
                + "&lt;/LinearLayout&gt;";

        try {
            // 将 XML 字符串解析为 XmlPullParser
            XmlPullParser parser = Xml.newPullParser();
            parser.setInput(new StringReader(xmlLayout));

            // 使用 LayoutInflater 加载布局
            LayoutInflater inflater = LayoutInflater.from(context);
            View view = inflater.inflate(parser, null);

            // 获取并操作视图
            TextView textView = view.findViewById(R.id.textView);
            textView.setText("Dynamic Layout Loaded!");

            return view;
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }
}</pre><p></p>
<h3>使用示例</h3>
<p></p><pre class="crayon-plain-tag">@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // 创建动态布局
    View dynamicLayout = DynamicLayout.createDynamicLayout(this);

    // 将动态布局添加到 Activity 的根视图
    if (dynamicLayout != null) {
        setContentView(dynamicLayout);
    }
}</pre><p></p>
<h3>关键点</h3>
<ul>
<li><strong>XmlPullParser</strong>: 用于解析 XML 字符串。</li>
<li><strong>LayoutInflater</strong>: 用于将 XML 布局转换为视图树。</li>
<li><strong>动态生成 XML</strong>: 你可以根据需要生成不同的 XML 布局字符串。</li>
</ul>
<h3>注意事项</h3>
<ul>
<li>动态生成的 XML 必须符合 Android 的布局规范。</li>
<li>这种方式适合简单布局，复杂布局建议使用静态 XML 文件。</li>
</ul>
<p>通过这种方法，你可以在运行时动态生成并加载布局。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.somedoc.net/2025/03/14/deepseek-%e7%ad%94%e7%96%91%e7%ac%ac%e4%b8%80%e5%bc%b9/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>VosDolly 修改版差不多了</title>
		<link>https://www.somedoc.net/2025/02/06/vosdolly-%e4%bf%ae%e6%94%b9%e7%89%88%e5%b7%ae%e4%b8%8d%e5%a4%9a%e4%ba%86/</link>
					<comments>https://www.somedoc.net/2025/02/06/vosdolly-%e4%bf%ae%e6%94%b9%e7%89%88%e5%b7%ae%e4%b8%8d%e5%a4%9a%e4%ba%86/#respond</comments>
		
		<dc:creator><![CDATA[张三太爷]]></dc:creator>
		<pubDate>Thu, 06 Feb 2025 15:52:45 +0000</pubDate>
				<category><![CDATA[Android]]></category>
		<category><![CDATA[技术]]></category>
		<category><![CDATA[问题解决]]></category>
		<guid isPermaLink="false">https://www.somedoc.net/?p=6311</guid>

					<description><![CDATA[年前后，有一搭没一搭地拾掇腾讯家的开源插件 VasDolly <a href="https://www.somedoc.net/2025/02/06/vosdolly-%e4%bf%ae%e6%94%b9%e7%89%88%e5%b7%ae%e4%b8%8d%e5%a4%9a%e4%ba%86/" class="more-link">[&#8230;]</a>]]></description>
										<content:encoded><![CDATA[<p>年前后，有一搭没一搭地拾掇腾讯家的开源插件 VasDolly。当然了，这些年移动开发已经式微，这是口冷灶。不过老夫的习惯是上手了就得有个交代。</p>
<p>出于洁癖使然，兵分两路，互有联系却又互不干涉，所谓低耦合。一路是消费侧，也就是说集成使用了 VasDolly 插件的项目。这一侧三太爷也花了点心思，因为出发点是在 VasDolly 本身原地踏步的情况下，也要把它的潜力挖掘出来，敲骨吸髓式利用。另一侧则是供给侧，如果 VasDolly 内部作了改良，那消费侧的花活就可以消停一二。这一侧改动的难点不是实现层面的，而是设计层面的，如何优雅地兼容之前的接口/约定/协议，同时扩展出新的能力。</p>
<p>今天抽了一小会儿工夫，看了一下 VasDolly 在 GitHub 上的 PR，共有三个：一是增加了渠道别名的支持，二是增加了产品变体的支持，三是一个 bug fix（作者自称）。</p>
<p>渠道别名的支持不算强需求。嵌入到安装包体内的渠道号，直接使用 huawei 这样的字串并非不可，只不过实践中，大家都不愿这么直接暴露出去，所以通常会写成 90001 这种代码。但是这样的代码放到文件名里，供人肉眼检索辩识的话，就会差那么一点，所以就有了文件名里使用渠道别名的需求。在三太爷的衡量之下，当然不愿把 VasDolly 改的面目模糊，而是宁可额外多一个重命名各个渠道包的善后步骤。事实上也确实已经这么做了。</p>
<p>看到支持产品变体（product flavor）的这个 PR 的时候还是吃了一惊的，颇以为忙忙叨叨实现了个别人已经实现的功能。再一看代码，差别就出来了。这位仁兄只是在输出结果文件时发现存在产品变体名称的话，多产生一级子目录，免得不同产品变体的输出文件名相同的话互相覆盖。当然了，三太爷的方案想得比这个完善，由此不再担心白忙活。</p>
<p>最后的那个 bug fix，一开始只是肉眼 review 了一下代码。对一个函数的返回结果进行检查，原来的代码是使用 == null 判空， PR 里改作了 .isEmpty 判空。假如说，在充分相信双方都曾运行代码覆盖到所处条件的话，三太爷其实更倾向于两者相与（&#038;&#038;），而不是后者直接替换掉前者。后来又到 IDE 里看了一眼，发现原版的判空总是 false，所以人家的修改完全正确且合适，老夫甚至把他的修改同步到了自己的修改版里了，毕竟还多发现了一处也应该同样修改的地方。</p>
<p>这么看下来，真正在老夫之前为该项目提交的有实质提升的改进并不多。老怀颇慰。</p>
<p>把它构建并发布到了本地，又在实际项目中引用进来初步测了一下，还没有出错。过几天实际跑一下发版看看结果。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.somedoc.net/2025/02/06/vosdolly-%e4%bf%ae%e6%94%b9%e7%89%88%e5%b7%ae%e4%b8%8d%e5%a4%9a%e4%ba%86/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
