为什么无法使用 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