为什么无法使用 WindowInsetsControllerCompat#setAppearanceLightStatusBars() 以编程方式设置 windowLightStatusBar
Why setting windowLightStatusBar ain't working programmatically using WindowInsetsControllerCompat#setAppearanceLightStatusBars()
我正在尝试创建一个启动器应用程序,但是我在尝试以编程方式更改 windowLightStatusBar
时遇到了问题 API 30 并且相同的代码在下面工作得很好,我希望如果有人可以帮助我:)
这里是完整的 class 源代码:
public final class AppDrawer extends BottomSheetBehavior.BottomSheetCallback implements OnApplyWindowInsetsListener {
private static boolean ALREADY_CREATED = false;
private AppCompatActivity activity;
private boolean dragging = false;
private float radius;
private int left, right, height, peekOver, oldState;
private Rect displayRect = new Rect();
private DisplayMetrics displayMetrics = new DisplayMetrics();
private Insets navigationBarInsets = Insets.of(0, 0, 0, 0), cutoutInsets = navigationBarInsets;
private WindowInsetsControllerCompat windowInsetsController;
private Configuration oldConfiguration;
private CardView cardView;
private FrameLayout frameLayout;
private BottomSheetBehavior<FrameLayout> bottomSheetBehavior;
public AppDrawer(AppCompatActivity activity){
if(ALREADY_CREATED) throw new IllegalArgumentException("The appDrawer object cannot be created more than once, clear the existing one from the memory first.");
this.activity = activity;
ALREADY_CREATED = true;
}
public void destroy(){
activity = null;
radius = 0.0f;
left = right = height = peekOver = oldState = 0;
displayRect = null;
displayMetrics = null;
navigationBarInsets = cutoutInsets = navigationBarInsets = null;
windowInsetsController = null;
oldConfiguration = null;
cardView = null;
frameLayout = null;
bottomSheetBehavior = null;
ALREADY_CREATED = false;
}
// WindowInsetControllerCompat is created and set from the activity that created this class
public void setWindowInsetsController(@NonNull WindowInsetsControllerCompat windowInsetsController) { this.windowInsetsController = windowInsetsController; }
public void initViews(View contentView){
oldConfiguration = activity.getResources().getConfiguration();
cardView = activity.findViewById(R.id.app_drawer_card_view);
frameLayout = activity.findViewById(R.id.app_drawer_container);
bottomSheetBehavior = BottomSheetBehavior.from(frameLayout);
bottomSheetBehavior.addBottomSheetCallback(this);
ViewCompat.setOnApplyWindowInsetsListener(contentView, this);
updateVars();
}
@Override
public WindowInsetsCompat onApplyWindowInsets(View v, WindowInsetsCompat insets) {
if(dragging) return insets;
cutoutInsets = insets.getInsets(WindowInsetsCompat.Type.displayCutout());
navigationBarInsets = insets.getInsets(WindowInsetsCompat.Type.navigationBars());
updateVars();
if(bottomSheetBehavior.getState() == BottomSheetBehavior.STATE_COLLAPSED) frameLayout.setPadding(left + navigationBarInsets.left, 0, right + navigationBarInsets.right, 0);
return insets;
}
public void setConfiguration(Configuration configuration) { this.oldConfiguration = configuration; }
private void updateWindowInsetsBars(int state){
// here is the one i used
if (state != BottomSheetBehavior.STATE_COLLAPSED && (oldConfiguration.uiMode & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_NO) {
windowInsetsController.setAppearanceLightNavigationBars(true);
windowInsetsController.setAppearanceLightStatusBars(true);
} else {
windowInsetsController.setAppearanceLightNavigationBars(false);
windowInsetsController.setAppearanceLightStatusBars(false);
}
// here is the one of @Zain answer
if (state != BottomSheetBehavior.STATE_COLLAPSED && (oldConfiguration.uiMode & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_NO) {
new WindowInsetsControllerCompat(activity.getWindow(), activity.getWindow().getDecorView()).setAppearanceLightStatusBars(true);
new WindowInsetsControllerCompat(activity.getWindow(), activity.getWindow().getDecorView()).setAppearanceLightNavigationBars(true);
} else {
new WindowInsetsControllerCompat(activity.getWindow(), activity.getWindow().getDecorView()).setAppearanceLightStatusBars(false);
new WindowInsetsControllerCompat(activity.getWindow(), activity.getWindow().getDecorView()).setAppearanceLightNavigationBars(false);
}
}
private void updateCardViewState(float slideOffset){
float a = 1.f - slideOffset;
cardView.setRadius(radius * a);
frameLayout.setPadding((int) ((left + navigationBarInsets.left) * a) , 0, (int) ((right + navigationBarInsets.right) * a), 0);
FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) cardView.getLayoutParams();
layoutParams.height = (int) ((displayRect.height() + navigationBarInsets.bottom + cutoutInsets.top + cutoutInsets.bottom) * slideOffset + height * a);
Log.d("AppDrawer", "card height: " + layoutParams.height + ", screen height: " + displayRect.height());
//cardView.setAlpha(Math.min(slideOffset * 1.1f, 1.0f));
cardView.setLayoutParams(layoutParams);
}
private void updateDisplayMetrics(){
WindowManager windowManager = this.activity.getWindowManager();
windowManager.getDefaultDisplay().getMetrics(displayMetrics);
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.R){
displayRect.set(windowManager.getCurrentWindowMetrics().getBounds());
displayRect.bottom -= navigationBarInsets.bottom + cutoutInsets.top + cutoutInsets.bottom;
displayRect.right -= navigationBarInsets.right + cutoutInsets.left + cutoutInsets.right;
} else {
displayRect.top = 0;
displayRect.bottom = displayMetrics.heightPixels;
displayRect.left = 0;
displayRect.right = displayMetrics.widthPixels;
}
}
private void updateVars(){
updateDisplayMetrics();
peekOver = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10f, displayMetrics);
radius = radius == 0 ? cardView.getRadius() : radius;
left = left == 0 ? frameLayout.getPaddingLeft() : left;
right = right == 0 ? frameLayout.getPaddingRight() : right;
height = height == 0 ? cardView.getLayoutParams().height : height;
bottomSheetBehavior.setPeekHeight(height + navigationBarInsets.bottom + peekOver);
if(oldState != 0) updateCardViewState(oldState == BottomSheetBehavior.STATE_COLLAPSED ? 0.0f : 1.0f);
}
@Override
public void onStateChanged(@NonNull View bottomSheet, int newState) {
dragging = newState == BottomSheetBehavior.STATE_DRAGGING;
updateWindowInsetsBars(newState);
oldState = newState;
}
@Override
public void onSlide(@NonNull View bottomSheet, float slideOffset) { updateCardViewState(slideOffset); }
}
在这里你可以看到 API 30 navigationbar
是白色的,而 statusbar
仍然是黑色的,即使两者都设置为 false
但它没有'根本不会影响 statusbar
。
Screenshot-1 on API 30
Screenshot-2 on API 30
但是在 API 29 上(它在那个下方也能正常工作)它们都可以很好地改变。
Screenshot-1 on API 29
Screenshot-2 on API 29
我找到了解决方案:
经过一些不同的测试后,我发现了导致该问题的原因。
/* make system bars transparent */
private void setup(){
Window window = getWindow();
window.requestFeature(Window.FEATURE_ACTION_BAR_OVERLAY);
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
WindowCompat.setDecorFitsSystemWindows(window, false);
WindowInsetsControllerCompat windowInsetsController = new WindowInsetsControllerCompat(window, window.getDecorView());
windowInsetsController.show(WindowInsetsCompat.Type.statusBars() | WindowInsetsCompat.Type.navigationBars() | WindowInsetsCompat.Type.systemBars());
appDrawer.setWindowInsetsController(windowInsetsController);
}
当 activity 调用 onCreate()
方法时,setup()
使 status/navigation 栏透明的方法中,一切都很好,除了一件事,我没有设置 window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
之后,状态栏变化就好了。
这是新代码:
/* make system bars transparent */
private void setup(){
Window window = getWindow();
window.requestFeature(Window.FEATURE_ACTION_BAR_OVERLAY);
// Adding it fix the problem.
window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
WindowCompat.setDecorFitsSystemWindows(window, false);
WindowInsetsControllerCompat windowInsetsController = new WindowInsetsControllerCompat(window, window.getDecorView());
windowInsetsController.show(WindowInsetsCompat.Type.statusBars() | WindowInsetsCompat.Type.navigationBars() | WindowInsetsCompat.Type.systemBars());
appDrawer.setWindowInsetsController(windowInsetsController);
}
希望有人会发现这很有用,并且不会像我一样犯同样的愚蠢错误
更新:
我找到了真正导致问题的原因,是否添加:
<item name="android:windowLightStatusBar">true</item>
到 themes.xml 文件使得设置 statusbar
以编程方式点亮不起作用,我不知道是什么导致了问题,但现在我知道了,我不必使用 window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
因为它在 API 30 及更高版本上已被弃用,正如 @Zain 指出的那样。
这是一个平台错误。
https://issuetracker.google.com/issues/180881870
要解决此问题,您只需将 androidx.core:core-ktx
更新到 1.8.0-alpha03
或更高版本。
详情请参考api改动
https://developer.android.com/jetpack/androidx/releases/core#1.8.0-alpha03
我正在尝试创建一个启动器应用程序,但是我在尝试以编程方式更改 windowLightStatusBar
时遇到了问题 API 30 并且相同的代码在下面工作得很好,我希望如果有人可以帮助我:)
这里是完整的 class 源代码:
public final class AppDrawer extends BottomSheetBehavior.BottomSheetCallback implements OnApplyWindowInsetsListener {
private static boolean ALREADY_CREATED = false;
private AppCompatActivity activity;
private boolean dragging = false;
private float radius;
private int left, right, height, peekOver, oldState;
private Rect displayRect = new Rect();
private DisplayMetrics displayMetrics = new DisplayMetrics();
private Insets navigationBarInsets = Insets.of(0, 0, 0, 0), cutoutInsets = navigationBarInsets;
private WindowInsetsControllerCompat windowInsetsController;
private Configuration oldConfiguration;
private CardView cardView;
private FrameLayout frameLayout;
private BottomSheetBehavior<FrameLayout> bottomSheetBehavior;
public AppDrawer(AppCompatActivity activity){
if(ALREADY_CREATED) throw new IllegalArgumentException("The appDrawer object cannot be created more than once, clear the existing one from the memory first.");
this.activity = activity;
ALREADY_CREATED = true;
}
public void destroy(){
activity = null;
radius = 0.0f;
left = right = height = peekOver = oldState = 0;
displayRect = null;
displayMetrics = null;
navigationBarInsets = cutoutInsets = navigationBarInsets = null;
windowInsetsController = null;
oldConfiguration = null;
cardView = null;
frameLayout = null;
bottomSheetBehavior = null;
ALREADY_CREATED = false;
}
// WindowInsetControllerCompat is created and set from the activity that created this class
public void setWindowInsetsController(@NonNull WindowInsetsControllerCompat windowInsetsController) { this.windowInsetsController = windowInsetsController; }
public void initViews(View contentView){
oldConfiguration = activity.getResources().getConfiguration();
cardView = activity.findViewById(R.id.app_drawer_card_view);
frameLayout = activity.findViewById(R.id.app_drawer_container);
bottomSheetBehavior = BottomSheetBehavior.from(frameLayout);
bottomSheetBehavior.addBottomSheetCallback(this);
ViewCompat.setOnApplyWindowInsetsListener(contentView, this);
updateVars();
}
@Override
public WindowInsetsCompat onApplyWindowInsets(View v, WindowInsetsCompat insets) {
if(dragging) return insets;
cutoutInsets = insets.getInsets(WindowInsetsCompat.Type.displayCutout());
navigationBarInsets = insets.getInsets(WindowInsetsCompat.Type.navigationBars());
updateVars();
if(bottomSheetBehavior.getState() == BottomSheetBehavior.STATE_COLLAPSED) frameLayout.setPadding(left + navigationBarInsets.left, 0, right + navigationBarInsets.right, 0);
return insets;
}
public void setConfiguration(Configuration configuration) { this.oldConfiguration = configuration; }
private void updateWindowInsetsBars(int state){
// here is the one i used
if (state != BottomSheetBehavior.STATE_COLLAPSED && (oldConfiguration.uiMode & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_NO) {
windowInsetsController.setAppearanceLightNavigationBars(true);
windowInsetsController.setAppearanceLightStatusBars(true);
} else {
windowInsetsController.setAppearanceLightNavigationBars(false);
windowInsetsController.setAppearanceLightStatusBars(false);
}
// here is the one of @Zain answer
if (state != BottomSheetBehavior.STATE_COLLAPSED && (oldConfiguration.uiMode & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_NO) {
new WindowInsetsControllerCompat(activity.getWindow(), activity.getWindow().getDecorView()).setAppearanceLightStatusBars(true);
new WindowInsetsControllerCompat(activity.getWindow(), activity.getWindow().getDecorView()).setAppearanceLightNavigationBars(true);
} else {
new WindowInsetsControllerCompat(activity.getWindow(), activity.getWindow().getDecorView()).setAppearanceLightStatusBars(false);
new WindowInsetsControllerCompat(activity.getWindow(), activity.getWindow().getDecorView()).setAppearanceLightNavigationBars(false);
}
}
private void updateCardViewState(float slideOffset){
float a = 1.f - slideOffset;
cardView.setRadius(radius * a);
frameLayout.setPadding((int) ((left + navigationBarInsets.left) * a) , 0, (int) ((right + navigationBarInsets.right) * a), 0);
FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) cardView.getLayoutParams();
layoutParams.height = (int) ((displayRect.height() + navigationBarInsets.bottom + cutoutInsets.top + cutoutInsets.bottom) * slideOffset + height * a);
Log.d("AppDrawer", "card height: " + layoutParams.height + ", screen height: " + displayRect.height());
//cardView.setAlpha(Math.min(slideOffset * 1.1f, 1.0f));
cardView.setLayoutParams(layoutParams);
}
private void updateDisplayMetrics(){
WindowManager windowManager = this.activity.getWindowManager();
windowManager.getDefaultDisplay().getMetrics(displayMetrics);
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.R){
displayRect.set(windowManager.getCurrentWindowMetrics().getBounds());
displayRect.bottom -= navigationBarInsets.bottom + cutoutInsets.top + cutoutInsets.bottom;
displayRect.right -= navigationBarInsets.right + cutoutInsets.left + cutoutInsets.right;
} else {
displayRect.top = 0;
displayRect.bottom = displayMetrics.heightPixels;
displayRect.left = 0;
displayRect.right = displayMetrics.widthPixels;
}
}
private void updateVars(){
updateDisplayMetrics();
peekOver = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10f, displayMetrics);
radius = radius == 0 ? cardView.getRadius() : radius;
left = left == 0 ? frameLayout.getPaddingLeft() : left;
right = right == 0 ? frameLayout.getPaddingRight() : right;
height = height == 0 ? cardView.getLayoutParams().height : height;
bottomSheetBehavior.setPeekHeight(height + navigationBarInsets.bottom + peekOver);
if(oldState != 0) updateCardViewState(oldState == BottomSheetBehavior.STATE_COLLAPSED ? 0.0f : 1.0f);
}
@Override
public void onStateChanged(@NonNull View bottomSheet, int newState) {
dragging = newState == BottomSheetBehavior.STATE_DRAGGING;
updateWindowInsetsBars(newState);
oldState = newState;
}
@Override
public void onSlide(@NonNull View bottomSheet, float slideOffset) { updateCardViewState(slideOffset); }
}
在这里你可以看到 API 30 navigationbar
是白色的,而 statusbar
仍然是黑色的,即使两者都设置为 false
但它没有'根本不会影响 statusbar
。
Screenshot-1 on API 30
Screenshot-2 on API 30
但是在 API 29 上(它在那个下方也能正常工作)它们都可以很好地改变。
Screenshot-1 on API 29
Screenshot-2 on API 29
我找到了解决方案:
经过一些不同的测试后,我发现了导致该问题的原因。
/* make system bars transparent */
private void setup(){
Window window = getWindow();
window.requestFeature(Window.FEATURE_ACTION_BAR_OVERLAY);
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
WindowCompat.setDecorFitsSystemWindows(window, false);
WindowInsetsControllerCompat windowInsetsController = new WindowInsetsControllerCompat(window, window.getDecorView());
windowInsetsController.show(WindowInsetsCompat.Type.statusBars() | WindowInsetsCompat.Type.navigationBars() | WindowInsetsCompat.Type.systemBars());
appDrawer.setWindowInsetsController(windowInsetsController);
}
当 activity 调用 onCreate()
方法时,setup()
使 status/navigation 栏透明的方法中,一切都很好,除了一件事,我没有设置 window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
之后,状态栏变化就好了。
这是新代码:
/* make system bars transparent */
private void setup(){
Window window = getWindow();
window.requestFeature(Window.FEATURE_ACTION_BAR_OVERLAY);
// Adding it fix the problem.
window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
WindowCompat.setDecorFitsSystemWindows(window, false);
WindowInsetsControllerCompat windowInsetsController = new WindowInsetsControllerCompat(window, window.getDecorView());
windowInsetsController.show(WindowInsetsCompat.Type.statusBars() | WindowInsetsCompat.Type.navigationBars() | WindowInsetsCompat.Type.systemBars());
appDrawer.setWindowInsetsController(windowInsetsController);
}
希望有人会发现这很有用,并且不会像我一样犯同样的愚蠢错误
更新:
我找到了真正导致问题的原因,是否添加:
<item name="android:windowLightStatusBar">true</item>
到 themes.xml 文件使得设置 statusbar
以编程方式点亮不起作用,我不知道是什么导致了问题,但现在我知道了,我不必使用 window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
因为它在 API 30 及更高版本上已被弃用,正如 @Zain 指出的那样。
这是一个平台错误。 https://issuetracker.google.com/issues/180881870
要解决此问题,您只需将 androidx.core:core-ktx
更新到 1.8.0-alpha03
或更高版本。
详情请参考api改动 https://developer.android.com/jetpack/androidx/releases/core#1.8.0-alpha03