文章目录
一、防止布局被刘海屏遮挡的处理方式
现在手机上会有很多种刘海屏、水滴屏等状态。有时候不做处理的话布局会被刘海屏遮挡。这里需要用到android:fitsSystemWindows="true"
属性。如果无效的话,尝试把布局设置为透明即可。
二、改变Android状态栏的颜色
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public static void setStatusBarColor(int statusColor, Activity activity) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Window window = activity.getWindow();
//取消设置Window半透明的Flag
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
//添加Flag把状态栏设为可绘制模式
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
//设置状态栏为透明
window.setStatusBarColor(Color.TRANSPARENT);
//如果想要修改标题栏文字可以在这里进行修改
// setAndroidNativeLightStatusBar(activity, true);//字体修改为深色
}
}
public static void setAndroidNativeLightStatusBar(@NonNull Activity activity, boolean dark) {
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
View decor = activity.getWindow().getDecorView();
if (dark) {
decor.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
} else {
decor.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
}
}
} catch (Exception ignore) {
}
}
三、fitsSystemWindows与布局的关系
这里的代码解释有问题,暂时删除,具体可以直接使用上述代码或者<style>
的方式实现沉浸式。在郭霖的帖子里面写的只需要更改状态栏颜色为透明的话会出现一些其他问题,比较好的方式是,也添加这行代码activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
。该代码加上状态栏透明的代码效果和下文的<style>
中设置沉浸式效果一样。具体差异未深究
这里有两种情况;
- 假设容器布局也只是传统的布局
LinearLayout
布局
如果什么都不做的话。只是使用fitsSystemWindows
属性并不会使布局延伸到状态栏上,这里需要配合上面的代码进行处理。但是并不是所有的组件都可以使用fitsSystemWindows
来进行修饰。例如View
,而非ViewGroup
。例如在TextView
、ImageView
上面使用就不会有效果。但是在ViewGroup
上面使用就会有效果。 - 假设容器布局是
CoordinatorLayout
、CollapsingToolbarLayout
、DrawerLayout
这些会有一些另外的情况,这时候不需要使用上面的java代码也能实现沉浸式处理,参考以下链接,
https://guolin.blog.csdn.net/article/details/123023395。
另外需要注意的是如果要开启沉浸式状态栏,还要设置style样式,代码设置则不用如此如下:<style name="Theme.DesignTest" parent="Theme.MaterialComponents.DayNight.DarkActionBar"> ... <!-- Customize your theme here. --> <item name="android:windowTranslucentStatus">true</item><!--设置沉浸式状态栏--> </style>
- 假如想给状态栏架上遮罩层的话,可以使用
<FrameLayout>
、<LinearLayout>
的不会延伸的特性,例如下文的示例代码,该代码并不需要显示声明高度
四、一个常见的例子
结合上述代码,这里实现一种常见的效果
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/root_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="#F4F5F7">
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/js_bg_result_header"
app:layout_constraintTop_toTopOf="parent"/>
<FrameLayout
android:id="@+id/fl_status"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/sq_black_alpha_30"
android:fitsSystemWindows="true"
app:layout_constraintTop_toTopOf="parent"
tools:layout_height="25dp" />
<TextView
android:id="@+id/tv_back"
style="@style/Sqmedium_18_black"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:drawablePadding="@dimen/sq_dp_8"
android:fitsSystemWindows="true"
android:gravity="center_vertical"
android:minHeight="@dimen/sq_dp_44"
android:paddingStart="@dimen/sq_dp_12"
android:text="@string/sq_feed_back"
android:textColor="@color/sq_black_alpha_80"
android:textSize="@dimen/sq_sp_16"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/fl_status"
tools:ignore="RtlSymmetry" />
</androidx.constraintlayout.widget.ConstraintLayout>
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
setupWindow(this);
super.onCreate(savedInstanceState);
}
private void setupWindow(Activity activity) {
if (activity != null && activity.getWindow() != null) {
activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
activity.getWindow().setStatusBarColor(Color.TRANSPARENT);
}
}
}
五、setDecorFitsSystemWindows
该函数用于表示内容是否超出状态栏和底部操作栏,false为超过
WindowCompat.setDecorFitsSystemWindows(window, false)
六、enableEdgeToEdge()
使用enableEdgeToEdge()
也可以做到沉浸式,源自依赖
import androidx.activity.enableEdgeToEdge
。
androidx.activity:activity:1.8.1@aar
如果不能升级的话使用google的com.google.android.material
也是可以的
EdgeToEdgeUtils.applyEdgeToEdge()
七、Fragment
正常来说Fragment
也可以使用上述方案实现沉浸式,倘若布局无法出现,可以尝试以下方案
需要注意的是该方案在Fragment
中无法使用FrameLayout
的特性,可以自己设置高度,或者使用第三方库
八、DialogFragment
如果是对话框的话,那么需要在样式里面去修改,毕竟Dialog和Activity不是一个window。
<style name="base_dialog_style" parent="android:Theme.Dialog">
....
<item name="android:windowTranslucentStatus">true</item><!--设置沉浸式状态栏-->
</style>
九、Compose
Android Jetpack Compose 沉浸式状态栏的实现
如果不使用上述链接的解决方式的话,可以使用第六条的解决方案applyEdgeToEdge()
。但是该情况下,内容会延伸到状态栏下面,如果需要做到背景延伸,但是内容在状态栏下面,可以使用Modifider..systemBarsPadding()
进行高度填充。
可使用方式具体可参考accompanist,这里面不止提供了accompanist的代码还提供了在androidX中可以使用哪些代码
十、获取状态栏和导航栏高度
以下代码可以适配各个版本
private fun initNavigationBar() {
ViewCompat.setOnApplyWindowInsetsListener(binding.root) { v, windowInsets ->
val insets = windowInsets.getInsets(WindowInsetsCompat.Type.navigationBars())//只能获取单个底部导航栏,都获取的话可以用WindowInsetsCompat.Type.systemBars()
viewModel.updateNavigationHeight(insets.bottom)
// WindowInsetsCompat.CONSUMED //使用这个后自动修改状态栏和导航栏样式
windowInsets //使用这个后不会修改状态栏和导航栏样式,有利于精细控制某一部分
}
}
十一、android:fitsSystemWindows="true"与EditText控件的关系
在 Android 中,当页面包含 EditText 控件,并且根布局设置了 android:fitsSystemWindows=“true” 属性时,如果输入框获取焦点,可能会导致整个布局下移。这是因为 fitsSystemWindows 属性与软键盘的显示行为产生了冲突。
1. 问题原因
android:fitsSystemWindows="true":
该属性用于调整布局以适应系统窗口(如状态栏、导航栏)。
当设置为 true 时,布局会为系统窗口留出空间。
软键盘弹出:
当 EditText 获取焦点时,软键盘会自动弹出。
默认情况下,Android 会调整布局以避免软键盘遮挡输入框。
冲突:
fitsSystemWindows 和软键盘的自动调整行为可能会导致布局下移。
2. 解决方案
以下是几种解决布局下移问题的方法:
方法 1:禁用软键盘的布局调整
在 AndroidManifest.xml 中,为对应的 Activity 设置 windowSoftInputMode 属性,禁用软键盘的布局调整。
<activity
android:name=".YourActivity"
android:windowSoftInputMode="adjustPan|stateHidden">
</activity>
adjustPan:当软键盘弹出时,将布局向上平移,而不是调整布局大小。
stateHidden:默认隐藏软键盘。
方法 2:动态调整布局
在代码中动态调整布局,避免软键盘弹出时布局下移。
View rootView = findViewById(android.R.id.content);
rootView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
Rect rect = new Rect();
rootView.getWindowVisibleDisplayFrame(rect);
int screenHeight = rootView.getRootView().getHeight();
int keyboardHeight = screenHeight - rect.bottom;
if (keyboardHeight > screenHeight * 0.15) {
// 软键盘弹出,调整布局
rootView.setPadding(0, 0, 0, keyboardHeight);
} else {
// 软键盘隐藏,恢复布局
rootView.setPadding(0, 0, 0, 0);
}
}
});
方法 3:使用 CoordinatorLayout 和 AppBarLayout
如果布局中使用了 CoordinatorLayout 和 AppBarLayout,可以通过设置 android:fitsSystemWindows=“true” 和 android:windowSoftInputMode=“adjustResize” 来避免布局下移。
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true">
<!-- AppBar 内容 -->
</com.google.android.material.appbar.AppBarLayout>
<!-- 页面内容 -->
</androidx.coordinatorlayout.widget.CoordinatorLayout>
在 AndroidManifest.xml 中设置:
<activity
android:name=".YourActivity"
android:windowSoftInputMode="adjustResize">
</activity>
方法 4:自定义 EditText 的焦点行为
通过监听 EditText 的焦点变化,手动调整布局。
EditText editText = findViewById(R.id.editText);
editText.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus) {
// 获取焦点时,调整布局
rootView.setPadding(0, 0, 0, keyboardHeight);
} else {
// 失去焦点时,恢复布局
rootView.setPadding(0, 0, 0, 0);
}
}
});
十二、参考链接
- 沉浸式兼容库:
更新较新:https://github.com/gyf-dev/ImmersionBar
长时间未更新:https://github.com/laobie/statusbarutil - WindowInsetsControllerCompat使用,新方式实现状态栏、导航栏、键盘控制
- Android开发 WindowInsetsController 窗口控制器
- Android 沉浸式状态栏和全面屏遇到刘海屏
- Android Detail:Window 篇——WindowInsets 与 fitsSystemBar
- Android开发 WindowInsetsController 窗口控制器
- 再学一遍android:fitsSystemWindows属性
- ANDROID沉浸式状态栏、FITSSYSTEMWINDOWS、标题栏折叠
- systemuicontroller(Compose版的状态栏控制)
- 在应用中全屏显示内容
- android 软键盘动画
- Android软键盘遮挡/动画最佳解决方案
- 在窗口边衬区内布置应用