[targetSDK升级为35] 恶心的EdgeToEdge适配 (v5)

8,031 阅读18分钟

开发多年, 碰到不少恶心需求与恶心甲方了. 但这次edgeToEdge真是刷新了我对google下限的想像.

事情是Google要求8月底前, 所有play store上的app升级targetSDK为35 (政策链接是这个: developer.android.com/google/play…, 目前只说2024年8月底前到34; 但按google每年的规律, 前几年也是每年都升了33, 34的, 所以今年8月底前要肯定是升到35的).

其它所有变动都可以理解, 只有一项是恶心的EdgeToEdge, 即Google强制所有app的所有页面全部变为全屏模式(full screen, edge to edge).

一. EdgeToEdge为何让人火大?

先来个图说明下这个强制要求会让我们的app如何变化:

image.png

恶心的点就在于Google是强制所有app都要这样做, 而且还有一些细节点很恶心, 后续会讲到. 这样就给我们的每一个app都造成了麻烦. 我们app设计得好好的, 为什么一定要全屏.


而且你强制让最上面的OS status bar给占了有什么意思哦, 这不是把"时间, 电量, wifi信号, .."这些都给遮挡了嘛?!

所以Google就会让你做适配, 不要占了status bar (如上图的And15+所示, 页面内部就没有占据status bar的区域)? 我就晕死了, 那适配后的效果跟不全屏不就一样了, 那你强制全屏有什么意思!!!!


同理, 你的手机要是底部是"三按钮"或"二按钮"的OS navigation区域(如"后退键 + home键 + recentTasks键"), 那全屏后你的内容也会和这个区域重合.

重合自然是不行的, 不然你按下去, 到底是响应OS的back键, 还是响应你页面中的某View的点击呢? 所以你肯定要做成不重合.

那又回到原点? 要是不强制全屏, 本来就不重叠; 你现在强制全屏, 又要给开发增加额外工作量来做到不重合. 这就像极了, 本来一条路好好的, 你非要拆了, 再重新修一遍, 重新搞得和原来的路一样, 那你干嘛重修这条路?

p.s. Google离最初的"不作恶"初心越来越远了, 现在的Android更新也不侧重于用户的体验, 反而一个劲地推销自己的AI. 很多新东西也越来越倾向于大厂的开发/经理为了自己的KPI生硬地搞新东西, 而不是想这些新东西能解决什么难受的痛点.

二. 着手做之前的分析

虽然这个需求让人很无语, 吐槽完后做还是肯定要做的. 不过我们有必要在编码之前, 先分解下这个需求, 以及分析下它到底影响了哪些部分, 这样我们才能一步一步地去解决这些问题.

2.1 影响哪些设备?

经过测试, 一个app的targetSDK为34-时, 在任何设备上都不会被强制全屏.

但要是你的app升级targetSDK到了35+了, 那么:

  • 在Android 14-的设备上, 不会全屏
  • 在Android 15+的设备上, 会强制全屏.
    • (这个其实有个后门, 在第三章 最快的适配里会讲到)

这个意味着什么?

: 意味着, 要是我做了某些操作, 来让Android 15+适配此需求; 但同时我需要要让Android 14-不要受此影响 (因为Android 14-的设备没有强制全屏嘛!)

2.2 影响所有页面, 所有组件吗?

若你升级了targetSDK到35+, 那在Android 15+的设备上就会强制全屏.

不过, 有些细节要注意:

  • 若你的页面本来就是全屏 (如很多app的splashActivity本身就是全屏, 或是video play页面本身也是全屏), 那它不受影响, 仍会是全屏的.
  • 若你的页面本身没有全屏, 那就会被强制全屏.

所以问题的规模再次缩小: 我们需要让所有本来非全屏的页面来适配这个edgeToEdge的要求.

另外强制一下, 而这里要做的改动是涉及到: Activity, Fragment, BottomSheetDialogFragment, ... 这些所有组件的哦!

2.3 代码上如何实现?

其实现在你新建一个Android工程, 它的Activity会写上:

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContentView(R.layout.activity_main)
        ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
            val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
            v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
            insets
        }
    }
}

它这里的R.id.main就是整个Activity的rootView:

<ConstarintLayout android:id="@+id/main" ...>
    <TextView ..../>
</ConstraintLayout>   

这个代码已经就已经是我们的解决方案了. 其核心思想就是:

  • 在时机合适时 (OnApplyWindowInsetsListener)时, 给Activity中的内容加一个上下左右的padding, 这样就可以让我们app的页面不和上面的status bar重叠, 也不全下面的navigation zone重叠.

2.5 思路有了

那只要让Activity的rootView做一定的padding就行了, 相当于是:

  • paddingTop = statusBar.height
  • paddingBottom = navigationZone.height

那我们完全可以在BaseActivity中用:

class BaseActivity : AppCompatActivity() {
    override fun onCreate(bundle) {
        super.onCreate(bundle)
        enableEdgeToEdge()
        setContentView(...)
        ViewCompat.setOnApplyWindowInsetsListener { 
            rootView.setPadding(...)
        }
    }
}

这个问题貌似这样简单一个设置就能成功让所有页面适配edgeToEdge了. 哎, 事情要是能这么简单, 那也算google良心了. 可偏偏google搞了好多事情, 让我们本来困难的适配工作变得更加艰难.

2.5 困难1: top方向

第一个困难就来自于Google设计人员的有限小脑以为所有页面都是非全屏的, 所以你上面的设置就没问题.

但上面第2.2小节说了, 原本已经全屏的页面是不受影响的. 你要是再加上个paddingTop, 那效果反而变成非全屏了.

image.png

所以具体到top的方向, 我们就要区分处理:

  • Android 14- (不全屏)
  • Andorid 15+的全屏页面 (不要做paddingTop)
  • Android 15+的非全屏页面(要做paddingTop)

2.6 困难2: top方向

其实从上一个图中可以看到, 我们的第二个问题, 那就是我们把Activity的rootView加了一个paddingTop后, 就把statusBar的区域给空出来了, 但空出来的好怪, 成了一个透明色, 搞得时间, 电量, 这些信息都看不见了.

image.png

所以在top方向上, 我们要做的其实是:

  • Android 14- (不全屏)
  • Andorid 15+的全屏页面 (不要做paddingTop)
  • Android 15+的非全屏页面
    • (要做paddingTop)
    • (还要给statusBar区域加上颜色)

2.7 困难3: bottom方向

联明的你肯定想到了bottom的方向上的困难也是类似的, 也要给navigation zone添加颜色 (加了paddingBottom后其背景色也成了空白了). 是的, 这样的想法是对的.

但其实更困难的点来自于Google的Material库.

implementation 'com.google.android.material:material:1.12.0'

比如说, 若你使用了Material库中的BottomNavigationView, 来看下效果:

image.png

是的, Google"贴心"地已经为Material库里做好了edgeToEdge的适配. 但这种所谓的"贴心"反而增加了我们的工作量, 因为我们本来就是想在BaseActivity中简单加上 paddingBottom = systembars.bottom, 现在发现不能这样了, 因为有了BottomNavigationView这样的, 底部paddingBottom应该为0了.

小总结下, 在bottom方向

  • Android 14- 不用变化
  • Android 15+ && 有BottomNavigationView/BottomAppBar: 这时paddingBottom应该为0
  • Android 15+ && 没有BottomNavigationView/BottomAppBar: paddingBottom应该为systemBar.bottom

2.8 困难4: BottomSheet

当我们变化targetSDK为35后, bottomSheet也变了, 来看下示例:

image.png

我们的BottomSheet布局如下:

<LinearLayout orientation = "vertical">
    <RecyclerView height="0dp" weight="1"/>
    <Buttton height="40dp" />
</LinearLayout>           

即是把Button先放到最下方, 然后RecyclerView再占据剩下的所有空间.

从上面的表格可以知道, And15+下,

  • Navigation Zone倒是和BottomSheet没有重叠
  • 但是BottomSheet的下方一大段内容 (测试约有110dp的样子)消失了.

所以我们要对Android 15+的BottomSheet也要特别处理一下.

2.9 小总结

综上所述,

  • Google的Material库会自动适配, 导致我们要特别处理一下它;
  • 同时要是已经全屏的页面, 也要处理下;
  • 同时要注意下And 14-, And 15+有所不同, 要小心点

2.10 more...

好吧, 其实还有更多细节. 不过更多的细节问题只是小问题了, 我们后续会讲到, 并不影响我们这里的分类了.

三. 最快的适配 (有后患)

上面的分析已经大致了解了要分别处理哪几点, 但其实若你的时间紧, 来不及处理这么多, Google是开了个后门的. 你可以这样:

step 1.

找到application的theme

step 2.

给这个theme添加一个属性:

<item 
  name="android:windowOptOutEdgeToEdgeEnforcement" 
  tools:targetApi="35">true
</item>

这样一来, 你的页面即使在Android 15+设备上也不会强制全屏. 不过Google也说明了, 这个flag的设置只在Android 15有用, 到了Android 16上你就是设置了它为true, 也会被Android给忽略掉.

所以设置这个flag, 只能让你今年不去适配edgeToEdge, 但明年(2026年)8月底, 你还是要去适配edgeToEdge的. 即还是要走第四章 稳当的适配的各个步骤.

四. 稳当的适配 (无患)

4.1 升级Activityx库 (可选)

现在既然升级到了targetSDK = 35, 那一些Androidx库也可以升级了, 如:

implementation "androidx.activity:activity-ktx:1.9.0"
implementation "androidx.fragment:fragment-ktx:1.7.1"

这些库一般是和targetSDK绑定的, 你不升到某个版本还用不了高版本的activity-ktx库.

升级它的原因是我们需要Activity中的enableEdgeToEdge()方法, 低版本的activity-ktx里没有.

4.1.1 为何说它是可选

因为在我的测试过程中, 我发现enableEdgeToEdge()其实并不是必需的.

是的, 现在Google官网, Google的demo, 以及很多文章都在介绍:

  • 你要要使用enableEdgeToEdge()这个方法,
  • 再配合ViewCompat.setOnApplyWindowInsetsListener(rootView) {v, insets -> ...}来调整padding 这就行了.

但我想了想, 其实它并不是必需的. 在我的多次测试下, 如何你没用调用enableEdgeToEdge()方法, 那么:

  • Android 14-上页面也不全屏.
    • ViewCompat.setOnApplyWindowInsetsListener()这个回调不会被触发
    • 注意, 在And14-的全屏页, 这个listener会触发 !!!
  • Android 15+上页面仍是全屏.
    • ViewCompat.setOnApplyWindowInsetsListener()这个回调仍会被触发

但要是你调用了enableEdgeToEdge(), 那么:

  • Android 14-上页面就是全屏
  • Android 15+上页面也是全屏.
  • 在所有机型上, ViewCompat.setOnApplyWindowInsetsListener()这个回调都被触发了

说到这里, 其实我们就明白了,

  • Android 15+自动强制全屏, 即相当于为我们调了enableEdgeToEdge()
  • Android 14-上不调用enableEdgeToEdge()就不会是全屏.

=> 所以enableEdgeToEdge()是想在全平台上实现全屏效果.

我都哭死了, 这么恶心的全屏效果我才不想在全屏上实现呢. 我只想让Android 15+有这样的效果就行了. 所以我的适配方案是没有调用的enableEdgeToEdge()方法的!!

4.2 top方向上的调整

不调用enableEdgeToEdge(), 那如何调整top上的全屏呢?

我们来看几种情况:

  • Android 14-: 因为没调用enableEdgeToEdge(), 所以完全不用理会这种情况
  • Android 15+ && 全屏页面: 就是升级到targetSDK = 35, 效果仍不变, 所以不用管
  • Android 15+ && 非全屏页面: 这些页面我们都是有ActionBar/ToolBar/TopBar在上面的, 如下图所示:

image.png

这里有一个规律: 即非全屏页基本上全是有ActionBar/TopBar/ToolBar这样的头在top上的, 所以我们只要修改TopBar的源码(自己公司写的一个类)就能完美适配了: 假设我们的topBar原本调试是50dp, statusBar调试是34dp, 所以我们原来的dimen是: topbar_height = 50dp的.

现在我们修改下, 在topBar的layout xml里面, 给原来的topbar内容再新加一个View, 宽度为全屏, 高度为top_placeholder_height:

  • res/values/dimens.xml : 设置 top_placeholder_height = 0dp
  • res/values-v35/dimens.xml: 设置toolbar_height = 34dp

当然你若是担心statusBar高度不一定是34dp, 也可以在代码中得到后更新这个topbar的高度即可:

// BaseActivity
val isAndroid15 = Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM //VANILLA_ICE_CREAM是35
val hasTopBar = contentView.findViewById(R.id.topbar) != null
if (isAndroid15 && hasTopBar)  { 
    topPlaceHolderView.updateLayoutParams<ViewGroup.LayoutParams> { 
        height = getStatusBarHeight() 
    }
}

小结:

  • Android 14-与Android 15+的全屏页面可以不用管
  • Android 15+的非全屏页面, 只要调整下actionBar的调试即可.

没有使用enableEdgeToEdge(), 也没有使用paddingTop = systembars.top, 只在有TopBar的地方加一个空view即可. 空View的高度则是根据Android版本不同而变化.

4.3 Bottom方向

4.3.1 处理paddingBottom

这个因为有些页面有BottomNavigationBar, 有些没有, 所以只好全局地设置了, 我们应该要在BaseActivity中设置:

    override fun onStart() {
        if(Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM) return  // And14-的全屏页面也会触发这个listener, 所以没有必要. 我们只要处理And15+即可. 所以加上这句

        val pageRootView = window.decorView.findViewById<ViewGroup>(android.R.id.content).getChildAt(0)        
        ViewCompat.setOnApplyWindowInsetsListener(pageRootView) { v, insets ->
            val hasBottomBar = pageRootView.findViewById<BottomNavigationView>(R.id.bottomBar) != null
            val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()) 
            val paddingBottom = if(hasBottomBar) 0 else systemBars.bottom
            v.updatePadding(systemBars.left, 0, systemBars.right, paddingBottom)
            insets
        }
    }

  • 说明1: BaseActivity还不知道每个子类页面是inflate了哪个layout XML, 所以我们要在onStart这种已经inflate完毕的时机里去调用这个ViewCompat.setOnApplyWindowInsetsListener方法

  • 说明2: 如上面所述, 有bottomNavigationView的地方, 我们就不要调整paddingBottom了, 所以有了上面的val paddingBottom = if(hasBottomBar) 0 else systemBars.bottom的设置

  • 说明3: Android 14-的设备, 因为没有调用enableEdgeToEdge()方法, 所以这个ViewCompat.setOnApplyWindowInsetsListener回调根本不会被调用, 所以无须担心Android 14-的设备了. 它们的体验和以前一样的.

  • 说明4: 要是你的Activity里还有Fragment, 那可能就要注意用下面的代码来判断

val currentFragment = activity.supportFragmentManager.findFragmentById(container) //或findFragmentByTag
hasBottomBar = fragment.view?.findViewById(R.id.bottomBar)

效果如下:

image.png

4.3.2 处理OS navigation zone的颜色

明显从上面看到这个navigation zone的颜色不太对, 太透明了, 所以我们来修改下:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    window.navigationBarColor = getColor(R.color.purple)
}

效果如下:

image.png

备注: 要是有同学看其源码, 能发现这方法说And 15+上, 这个方法将无效 image.png -> 不过, 我的实践证明这个方法在targetSDK = 35时仍可以用.

4.4 BottomSheet的处理

这里又要骂Google不当人子了, 你既然是所有页面都要app们去适配, 那为何不让开发们自己去做就好了? Google一定要去修改BottomNaivgationView, BottomSheet的代码, 导致我们app的开发工作量更大了.

这里也是, BottomSheet的处理更复杂, 原因就是BottomSheet来自于Google Material库.

4.4.1 他人的建议

文章来源于: 《Advanced Android Edge-ToEdge: BottomSheet》

这位作者的做法就是分两步

第一步, 修改theme

image.png

第二步, Google Material把BottomSheet的多个View都设置成了 fitsSystemWindows = true了, 我们要设置为false (And 14-就是false), 并做一定修改:

image.png

我试了下这些做法, 可以说, 效果有, 但并不完美, 我们app的页面看起来和And14-的效果不一样. 所以我最终没有采用方法.

(不过我仍然列出这篇文章在这里, 说不定其它同学会觉得它正好适合于你们app)

4.4.2 自己加paddingBottom

好在我们App中的BottomSheet并不多, 只有3个. 所以我的做法就是像给topBar加一个View做为paddingBottom. 这样反而效果和我们预期的一样.

不过仍是有坑, 那就是我得出来的systemBars.bottom约为46dp, 于是我给BottomSheet加了一个高度为46dp的空view

// sheet_carousel_info.xml
<LinearLayout orientation="vertical">
    <RecyclerView height="0dp" weight="1">
    <Button height="50dp"/>
    <View height="@dimen/bottom_placeholder_height"/>
</LinearLayout>

这个bottom_placeholder_heightvalues-v35里设置为46dp, 却仍是让button给显示不出来.

我最终设置成110dp, button才成功显示出来, 我也是晕了.

4.4.3 BottomSheet的另一解法

上面4.4.2是手动添加padding, 适合那些App里BottomSheet不多的情况. 对于App里很多页面都是BottomSheet, 可以用这样的方法来做到:


val fragments = this.supportFragmentManager.fragments
if(fragments.size > 0) {
    val frag = fragments[0]
    if(frag is BottomSheetDialogFragment) {
        bottomSheet.view?.updatePadding(systemBars.left, 0, systemBars.right, paddingBottom)
    }
}

当然, 这其实和你们app里具体如何显示 BottomSheet有关. 所以我的这种全局解法算是一种抛砖引玉.

4.5 ConstraintLayout的坑

上面讲了, 在处理top, 以及BottomSheet时, 我们都采用了不同OS上有不同dimen的做法, 如:

  • values/dimens.xml : 0dp
  • values-v35/dimens.xml : 34dp

但这个做法在碰到ConstraintLayout时就出问题了. 以top为例哦, 这样的设置后, 来看下效果:

image.png

即在And 15+上正常, 在And14-上却是top上的palceHolderView占据了整个屏幕.

原因就是在ConstraintLayout中, 0dp不再代表0高度, 而是代表根据约束来. 所以这就可能导致上面点满全屏的情况.

(再骂一句Google, 0dp就应该只有一种意义, 表示0高度; 而不是有2种意思, 导致了现在的问题)

解决办法其实也简单, 不过是我老婆想到的. 她说既然0dp不行, 那我设置为0.01dp总可以吧. 果然, 试了下, 真的行!

所以为了兼容ConstraintLayout, 当使用dimen方案时, 请这样设置:

  • values/dimens.xml : 0.01dp
  • values-v35/dimens.xml : 34dp

这样就基本Okay了.

4.6 总结

  • 针对top方向, 只要处理非全屏页面, 所以我们给topBar加了一个高度可变的topPlaceHolderView
    • (全局地生效)
  • 针对bottom方向, 要用Google推荐的设置paddingBottom的方法,
    • (全局地生效)
    • 只不过要注意 bottomNavigationView, 以及OS navigation zone的颜色
  • 针对BottomSheet,
    • 要么用我推荐的文章 (全局地生效)
    • 要么用我的方法(手动地一个个地加bottomPlaceHolderView)

五. 其它的可能有影响的点

5.1 Material2的theme

我没有碰到这种情况(因为我用的是material1的theme), 但老婆碰到了. 老婆说她们公司用的是Material2的theme, 这时要适配edgeToEdge, 就得升级到Material3的theme.

但熟悉的朋友一定发现了, 改整个Application的theme是个非常危险的操作, 意味着可能每个页面都有所变化, 可能你要修改每个页面的UI了.

具体方法可能只能升级到material3页面, 然后一个页面一个页面地修改material theme升级带来的影响了.

5.2 Dark theme

若你的app还支持dark theme, 那你会发现BottomSheet的方案有问题, 在Android 15设备上paddingBottom过大了:

image.png

原因也是Google Material库的锅.

  • Android认为, 又是values-v35, 又是dark theme的情况下, 那dimen的值就去values-v35中取, 自然paddingBottom这时就是100dp
  • 但是, Google Material库认为, 一个dark theme的BottomSheet, style应该走dark, 所以它的各个子view又不用得像是Android 14一样(即fitsSystemWindows = false)

这二者一结合就冲突了. 但明显是Android的处理更正常.

正确的解决办法, 新加一个res/values-night-v35目录:

image.png

总结下来就是:

  • values/dimens.xml:
    • bottomSheet_paddingBottom = 0.001dp
  • values-v35/dimens.xml:
    • bottomSheet_paddingBottom = 110dp
  • values-night-v34/dimens.xml:
    • bottomSheet_paddingBottom = 0.001dp

六. 番外: Compose体系的edgeToEdge

Compose上的处理其实取决于你的每个Composable页面是否是单独的Scaffold.

  • 要是你是一个Activity, 里面自带Scaffold, Scaffold里带路由表, 那这个看似能全局性地修改padding, 但其实灵活性差
  • 要是你是一个Activity, 里面直接带路由表. 然后每个Composable有自己的Scaffold, 这个看起来工具量大, 但其实灵活性好.
    • 比如说, 你一个页面没有TopBar, 另一个页面有BottomBar, 再一个页面是完全全屏, ... , 像这些情况, 每个Composable有自己的Scaffold的方法, 就能更好地处理一个个的情形.

我个人项目中就是第二个方案, 仍是以top与bottom方向来分解. 但其实因为Compose里Scaffold自带了padding值, 不用我们去写什么ViewCompat.setOnApplyWindowInsetsListener(), 所以其实是更容易了

6.1 ComposeHostActivity

这个Activity跟前面的第一到五章不一样, 它是调用了enableEdgeToEdge()的.

class ComposeHostActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        enableEdgeToEdge()  //<= <= 注意, 这里enable了的!!
        
        setContent {
            MyProjectTheme {
                val router = rememberNavController()
                NavHost(navController = router, startDestination = "home") {
                    appSubRoute(router)
                }
            }
        }
    }
}

6.2 top方向

6.2.1 全屏页

top方向上的全屏, 其实就是指页面会侵入到系统status bar的区域嘛, 这个其实就是不在scaffold中加入topPadding就能自动做到. 也就是这样的代码:

    Scaffold(..) {  paddings ->  // 故意不处理这个paddings
        Column() { //页面的主体内容

6.2.2 有topBar, 并要处理padding

这里的问题其实就是, 当topBar有topPadding后, 原来status bar的地方会不会变成透明的, 看起来好像空出来一在块?

: 实践发现 "不会空出来一块", 反而是这个TopBar会自动占据原来statusBar的区域, 这正是我们想要的. (这一点比View体系的edgeToEdge适配要简单)

而且请注意, And14-与And15+上, 行为是一致的(另一个好消息). 所以我们只要这样处理:

Scaffold(modifier = Modifier.fillMaxSize(),
        topBar = { TopBar("非全屏页", router)}) {  paddings ->
    Column(modifier = Modifier.fillMaxSize().padding(paddings)) { // 主体内容

备注: 我的TopBar其实就是CenterAlignedTopAppBar:


@OptIn(ExperimentalMaterial3Api::class)
@Composable fun TopBar(
    title: String, router: NavHostController
) {
    CenterAlignedTopAppBar(title = {Text(title)},
        .... 

6.2.3 最终效果

image.png

6.3 Bottom方向

6.3.1 若没有NavigationBar

Scaffold(modifier = Modifier.fillMaxSize(),
        topBar = { TopBar("非全屏页", router)}) {  paddings ->
    Column(modifier = Modifier.fillMaxSize().padding(paddings)) { // 主体内容

6.3.2 若有NavigationBar

注意, 这个NavigationBar是在Scaffold里, 不在content里, 所以我们的content得为这个Scaffold里的NavigationBar留出空间来显示 (不然content最下方的一部分内容会被NavigationBar给遮挡住)

    Scaffold(modifier = Modifier.fillMaxSize(), 
        bottomBar = { BottomBar() }) {  paddings ->
        
        Column(modifier = Modifier.fillMaxSize()
            .padding(bottom = paddings.calculateBottomPadding())) { // 主体内容
           

6.3.3 最终效果

image.png

七. 结语

其实Google早就有"开发, 我是你爹"的感觉了. 早在targetSDK升31时, 就已经有过类似事件了. 当时Google根本不管你app是否有splash, 它自己就是强行加一个splash. 结果你不适配就会变成有2个splash页. 这种一拍脑袋就要干, 完全不给开发自己选择是否跟进的选择很让人反感. 不过当时适配splash的工作量较小, 我也没有十分在意. 现在则不同了, edgeToEdge的改动太大了, 真的让人如鲠在喉了.

从上面的篇幅, 可以看出我在这上面花费了多少时间. Google这种强制app更新UI的方式很恶心, 偏偏它的Material库又给我们增加了很多障碍. 好在现在大体完工了. 多谢你的阅读~

版本说明

  • v1 : 最初版本, 外加一些语言上的小修改.
  • v2 : BottomSheet的处理, 新加一个全局适配的说明 (4.4.3小节)
  • v3 : bottom方向的处理上, 新加了 Activity中的Fragment的use case的说明. 主要是现在用Activity来findViewById去找BottomNavigationView已经不靠谱了. (4.3.1小节)
  • v4 : 新加And14-上 OnApplyWindowInsetListener的说明 (4.1.1小节)
  • v5 : 新加了第六章, 即"Compose上edgeToEdge的处理"

参考资料

1). developer.android.com/about/versi…

2). developer.android.com/develop/ui/…

3). BottomSheet与EdgeToEdge

4). 老婆的各种实践

5). 我自己的各种实践