簡體   English   中英

Android M Light and Dark status bar programmatically - 如何讓它再次變暗?

[英]Android M Light and Dark status bar programmatically - how to make it dark again?

在 Android M 中,我們可以使狀態欄圖標變暗。 為此,我們可以在主題的 xml 中指定屬性:

<item name="android:windowLightStatusBar">true</item>

或者我們在運行時用這段代碼設置它:

View someView = findViewById(R.id.some_view);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
    someView.setSystemUiVisibility(someView.getSystemUiVisibility() | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
}

它實際上工作正常。 但問題是如何在運行時正確地將狀態欄模式設置為暗?

我已經嘗試過這些變體:

// Makes status bar mode dark, but also hides it along with all navigation views. 
someView.setSystemUiVisibility(someView.getSystemUiVisibility() | ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);

// Does nothing 
someView.setSystemUiVisibility(someView.getSystemUiVisibility() & ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);

// Also does nothing 
someView.setSystemUiVisibility(someView.getSystemUiVisibility() ^ View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);

那么如何以正確的方式完成呢?

@Aracem 發布的解決方案是有效的,但是如果您嘗試更改狀態欄的背景顏色,則不起作用。 就我而言,我按以下方式進行操作。

要啟用 windowLightStatusBar(以編程方式,例如在 Utils 類中):

 public static void setLightStatusBar(View view,Activity activity){


            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {

                int flags = view.getSystemUiVisibility();
                flags |= View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
                view.setSystemUiVisibility(flags);
                activity.getWindow().setStatusBarColor(Color.WHITE); 
            }
}

要將 StatusBar 恢復到以前的狀態:

  public static void clearLightStatusBar(Activity activity) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            Window window = activity.getWindow();
            window.setStatusBarColor(ContextCompat
                 .getColor(activity,R.color.colorPrimaryDark)); 
        }
    }

恢復狀態欄的顏色就足夠了,它還恢復了圖標的顏色。 非常重要:在 setLightStatusBar(View view..) 中使用的視圖從屏幕上消失(即 view.getVisibility()==GONE|INVISIBLE)之前,不會發生恢復操作。

根據尼克布徹的項目“格子”

public static void clearLightStatusBar(@NonNull View view) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        int flags = view.getSystemUiVisibility();
        flags &= ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
        view.setSystemUiVisibility(flags);
    }
}

你可以在這里找到這個文件。

我基於@Aracem 和@Carlos Hernández Gil,但我認為如果我們使用按位異或(Java 中的^運算符)會很容易理解

private void setLightStatusBar(Activity activity) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        int flags = activity.getWindow().getDecorView().getSystemUiVisibility(); // get current flag
        flags |= View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;   // add LIGHT_STATUS_BAR to flag
        activity.getWindow().getDecorView().setSystemUiVisibility(flags); 
        activity.getWindow().setStatusBarColor(Color.GRAY); // optional
    }
}

private void clearLightStatusBar(Activity activity) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        int flags = activity.getWindow().getDecorView().getSystemUiVisibility(); // get current flag
        flags = flags ^ View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; // use XOR here for remove LIGHT_STATUS_BAR from flags
        activity.getWindow().getDecorView().setSystemUiVisibility(flags);
        activity.getWindow().setStatusBarColor(Color.GREEN); // optional
    }
}

解釋

首先看SYSTEM_UI_FLAG_LIGHT_STATUS_BARsetSystemUiVisibility

/**
 * Flag for {@link #setSystemUiVisibility(int)}: Requests the status bar to draw in a mode that
 * is compatible with light status bar backgrounds.
 */
public static final int SYSTEM_UI_FLAG_LIGHT_STATUS_BAR = 0x00002000;

public void setSystemUiVisibility(int visibility) {
    if (visibility != mSystemUiVisibility) {
        mSystemUiVisibility = visibility;
        ...
    }
}

我認為下面的兩行代碼很難理解

flags |= View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; // for set light status bar
flags = flags ^ View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; // for clear light status bar

乍一看,我只是覺得我們可以使用簡單的like

flags = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; // for set light status bar
flags = 0; // for clear light status bar (0 <=> LIGHT_STATUS_BAR <=> default systemUiVisibility)

但我們應該使用| ^因為
例如,我們想將狀態欄和導航欄都設置為亮,那么我們將使用

flags = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR | View.View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
activity.getWindow().getDecorView().setSystemUiVisibility(flags);

當我們不想讓狀態欄變亮時,我們可以使用

flags = View.View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
activity.getWindow().getDecorView().setSystemUiVisibility(flags);

或者

flags = activity.getWindow().getDecorView().getSystemUiVisibility();
flags = flags ^ View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; 
activity.getWindow().getDecorView().setSystemUiVisibility(flags);

了解更多我們為什么使用| ^ ,我認為下面的教程可能會有所幫助https://medium.com/@JakobUlbrich/flag-attributes-in-android-how-to-use-them-ac4ec8aee7d1這是我的理解。 希望這有幫助

我為 API 23-30 切換明暗的方式與這些有點不同。 這是一個科特林版本

由於我使用 Compose 和 Crossfade 動畫來更改主題,因此在某些情況下會調用此函數兩次,從而使xor自行撤消。 另一種方法是逆or運算。 我的燈光主題切換器最終看起來像這樣

@Suppress("DEPRECATION")
fun invertInsets(darkTheme: Boolean, window: Window) {
    if (Build.VERSION.SDK_INT >= 30) {
        //Correct way of doing things
        val statusBar = APPEARANCE_LIGHT_STATUS_BARS
        val navBar = APPEARANCE_LIGHT_NAVIGATION_BARS
        if (!darkTheme) {
            window.insetsController?.setSystemBarsAppearance(statusBar, statusBar)
            window.insetsController?.setSystemBarsAppearance(navBar, navBar)
        } else {
            window.insetsController?.setSystemBarsAppearance(0, statusBar)
            window.insetsController?.setSystemBarsAppearance(0, navBar)
        }
    } else {
        // Does bitwise operations (or to add, inverse or to remove)
        // This is depreciated but the new version is API 30+ so I should have this here
        val flags = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR or
            if (Build.VERSION.SDK_INT >= 26) View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR else 0

        if (!darkTheme) {
            window.decorView.systemUiVisibility = 
                window.decorView.systemUiVisibility or flags
        } else {
            window.decorView.systemUiVisibility = 
                (window.decorView.systemUiVisibility.inv() or flags).inv()
        }
    }
}

API 30+ 的位沒有貶值,但實際上 API 30 的手機並不多,因此也有較低 API 的位

它只是為了簡潔而預先計算標志(因為設置LIGHT_NAVIGATION_BARS是 API 26+),然后明確設置或重置這些確切的標志。 andxor或有趣的生意。 or將始終將標志設置為1 ,反之 or 將始終將標志設置為0 這是唯一可能的,因為SYSTEM_UI_FLAG_LIGHT_STATUS_BARSYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR都是一位。 否則它可能需要使用xor

我將這個簡單的實用程序對象放在一起,它允許您在任何片段中更改狀態欄顏色和亮狀態欄的開/關。 但是,這依賴於使用 Android Jetpack Navigation 組件進行導航 (Kotlin):

object StatusBarUtil {
    fun changeStatusBarColor(activity: Activity, @ColorInt color: Int, lightStatusBar: Boolean) {
        activity.window?.let { win ->
            val nav = Navigation.findNavController(activity, R.id.your_nav_host_fragmen /* TODO: Use the ID of your nav host fragment */)
            val currentDest = nav.currentDestination?.id
            val oldColor = win.statusBarColor
            val oldFlags = win.decorView.systemUiVisibility
            win.statusBarColor = color

            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                var flags = oldFlags
                flags = if (lightStatusBar) {
                    flags or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
                } else {
                    flags and View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.inv()
                }
                win.decorView.systemUiVisibility = flags
            }

            nav.addOnNavigatedListener { _, dest ->
                if (dest.id != currentDest) {
                    win.statusBarColor = oldColor
                    win.decorView.systemUiVisibility = oldFlags
                }
            }
        }
    }
}

要使用它,請從任何片段的onViewCreated中調用以下命令:

StatusBarUtil.changeStatusBarColor(requireActivity(), someDarkColor, false)

SDK 的 API 30 略有變化,現在燈光狀態欄的外觀由WindowInsetsController控制,可以從Window獲得。 下面是 Kotlin 中的示例方法(在 Activity 中),將新 API 與之前用於舊 Android SDK 版本的View.setSystemUiVisibility結合起來。 請記住,這只會改變狀態欄的系統圖標外觀,狀態欄的實際顏色仍然可以通過Window.setStatusBarColor設置。

@Suppress("DEPRECATION")
private fun setSystemUiLightStatusBar(isLightStatusBar: Boolean) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            val systemUiAppearance = if (isLightStatusBar) {
                WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS
            } else {
                0
            }
            window.insetsController?.setSystemBarsAppearance(systemUiAppearance,
                                                             WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS)
        } else {
            val systemUiVisibilityFlags = if (isLightStatusBar) {
                window.decorView.systemUiVisibility or SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
            } else {
                window.decorView.systemUiVisibility and SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.inv()
            }
            window.decorView.systemUiVisibility = systemUiVisibilityFlags
        }
    }
}

systemUiVisibility - 現在已棄用。 您可以改用WindowInsetsControllerCompat

private val insetsController: WindowInsetsControllerCompat? by lazy {
    activity?.window?.let { window -> WindowInsetsControllerCompat(window, window.decorView) }
}

private fun setLightStatusBar(light: Boolean) {
    insetsController?.isAppearanceLightStatusBars = light
}

UPDWindowInsetsControllerCompat的上述構造函數已棄用,因此請改用以下實例化:

private val insetsController: WindowInsetsControllerCompat? by lazy {
    activity?.window?.decorView?.let(ViewCompat::getWindowInsetsController)
}

基於@phan-van-linh 的回答,我為 Xamarin Android 編寫了這個類

public static class ActivityExtensions
{
    public static void SetLightStatusBar(this Activity activity)
    {
        int flags = (int)activity.Window.DecorView.SystemUiVisibility; // get current flag
        flags |= (int)SystemUiFlags.LightStatusBar;   // add LIGHT_STATUS_BAR to flag
        activity.Window.DecorView.SystemUiVisibility = (StatusBarVisibility)flags;
        //activity.Window.SetStatusBarColor(Color.GRAY); // optional
    }

    public static void ClearLightStatusBar(this Activity activity)
    {
        int flags = (int)activity.Window.DecorView.SystemUiVisibility; // get current flag
        flags = flags ^ (int)SystemUiFlags.LightStatusBar; // use XOR here for remove LIGHT_STATUS_BAR from flags
        activity.Window.DecorView.SystemUiVisibility = (StatusBarVisibility)flags;
        //activity.Window.setStatusBarColor(Color.GREEN); // optional
    }
}

要更改為輕狀態欄,請使用:-

 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) 
     activity?.window?.decorView?.systemUiVisibility = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR

要改回深色狀態欄:-

 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) 
     activity?.window?.decorView?.systemUiVisibility = 0

我將對上述答案進行一些更改。

上課

 public class DarkStatusBar {
    public static void setLightStatusBar(View view, Activity activity){

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {

            int flags = view.getSystemUiVisibility();
            flags |= View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
            view.setSystemUiVisibility(flags);
            activity.getWindow().setStatusBarColor(Color.WHITE);
        }
    }
}

並像這樣在任何你想要的地方調用它

        Window window = getWindow();
        View view = window.getDecorView();
        DarkStatusBar.setLightStatusBar(view,this);

設置帶有淺色文本顏色的藍色背景狀態欄kotlin版本

fun setBlueStatusBarColor(window: Window, context: Context) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            window.statusBarColor = context.getColor(R.color.colorBlue)
        }else {
            window.statusBarColor = context.resources.getColor(R.color.colorBlue)
        }

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            var flags: Int = window.decorView.systemUiVisibility
            flags = flags and View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
            window.decorView.systemUiVisibility = flags
        }
    }
}
/**
 * Changes color of the status bar icons
 * @param isLight if true - shows dark icons, light else
 */
fun setStatusBarUiTheme(activity: Activity?, isLight: Boolean) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        activity?.window?.decorView?.let {
            it.systemUiVisibility = if (isLight)
                it.systemUiVisibility or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR // dark icons
            else
                it.systemUiVisibility and View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.inv() // light icons
        }
    }
}

在 res/styles.xml

<style name="AppTheme" parent="Theme.AppCompat.NoActionBar">
    <item name="android:windowLightStatusBar">true</item>
    .......
</style>

<style name="AppTheme.DarkStatus" parent="AppTheme" tools:targetApi="23" >
    <item name="android:windowLightStatusBar">false</item>
    <item name="android:statusBarColor" >@color/status_bar_color</item>
</style>

在代碼中

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setTheme(R.style.AppTheme_DarkStatus);  //To set DarkStatusBar theme
    setContentView(R.layout.activity_drawer);
    ....
}

這個對我有用

fun Activity.clearLightStatusBar() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        val window = window
        window.statusBarColor = ContextCompat
            .getColor(this, R.color.ultramarine_blue)
    }
}

對於沒有Window實例的人。 也可以使用View實例(對於 API 30):

fun setLightStatusBar(view: View) = view.windowInsetsController?.setSystemBarsAppearance(
    WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS,
    WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS
)

fun clearLightStatusBar(view: View) = view.windowInsetsController?.setSystemBarsAppearance(
    0,
    WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS
)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM