最近のAndroidバージョン、Android 8.1以降のバージョン)では、OSはより多くのテーマをサポートしています。より具体的には、暗いテーマです。
ユーザーの視点でダークモードについて多くの話があるにもかかわらず、開発者向けに書かれたものはほとんどありません。
Android 8.1以降、Googleはある種の暗いテーマを提供しました。ユーザーが暗い壁紙を選択すると、OSの一部のUIコンポーネントが黒くなります(記事 ここ )。
さらに、ライブ壁紙アプリを開発した場合、OSにどの色(3種類の色)があるかを通知でき、OSの色にも影響を与えました(少なくともバニラベースのROMとGoogleデバイスでは)。そのため、色を選択しながら壁紙を作成できるアプリを作成しました( こちら )。これは、 notifyColorsChanged を呼び出し、次に onComputeColorsを使用して提供します
Android 9.0以降、テーマを選択できるようになりました:明るい、暗い、または自動(壁紙に基づく):
そして今Android Q、それはさらに進んだようですが、それでもまだどの程度かは不明です。どういうわけか「スマートランチャー」と呼ばれるランチャーがそれに乗っていて、テーマを正しく使用することを申し出ていますそれ自体(記事 here )なので、ダークモードを有効にすると(手動で、書いたように here )、アプリの設定画面が表示されます:
これまでに見つけた唯一のことは、上記の記事であり、私はこの種のトピックに従っています。
ライブ壁紙を使用して色を変更するようにOSに要求する方法も知っていますが、これはAndroid Q、少なくとも私が試したときに見たものに従って)変化しているようです(時刻に基づいていますが、確実ではありません)。
OSが使用するように設定されている色を取得するAPIはありますか?
OSのテーマを取得するためのAPIはありますか?どのバージョンから?
新しいAPIはなんとなくナイトモードに関連していますか?それらはどのように連携しますか?
アプリが選択したテーマを処理するための素晴らしいAPIはありますか?つまり、OSが特定のテーマの場合、現在のアプリはそうでしょうか?
わかりましたので、Android(Q)以前のバージョンの両方で、これが通常どのように機能するかを知るようになりました。
OSがWallpaperColorsを作成すると、色のヒントも生成されるようです。関数WallpaperColors.fromBitmap
にはint hints = calculateDarkHints(bitmap);
への呼び出しがあり、これはcalculateDarkHints
のコードです。
/**
* Checks if image is bright and clean enough to support light text.
*
* @param source What to read.
* @return Whether image supports dark text or not.
*/
private static int calculateDarkHints(Bitmap source) {
if (source == null) {
return 0;
}
int[] pixels = new int[source.getWidth() * source.getHeight()];
double totalLuminance = 0;
final int maxDarkPixels = (int) (pixels.length * MAX_DARK_AREA);
int darkPixels = 0;
source.getPixels(pixels, 0 /* offset */, source.getWidth(), 0 /* x */, 0 /* y */,
source.getWidth(), source.getHeight());
// This bitmap was already resized to fit the maximum allowed area.
// Let's just loop through the pixels, no sweat!
float[] tmpHsl = new float[3];
for (int i = 0; i < pixels.length; i++) {
ColorUtils.colorToHSL(pixels[i], tmpHsl);
final float luminance = tmpHsl[2];
final int alpha = Color.alpha(pixels[i]);
// Make sure we don't have a dark pixel mass that will
// make text illegible.
if (luminance < DARK_PIXEL_LUMINANCE && alpha != 0) {
darkPixels++;
}
totalLuminance += luminance;
}
int hints = 0;
double meanLuminance = totalLuminance / pixels.length;
if (meanLuminance > BRIGHT_IMAGE_MEAN_LUMINANCE && darkPixels < maxDarkPixels) {
hints |= HINT_SUPPORTS_DARK_TEXT;
}
if (meanLuminance < DARK_THEME_MEAN_LUMINANCE) {
hints |= HINT_SUPPORTS_DARK_THEME;
}
return hints;
}
次に、WallpaperColors.Java
にあるgetColorHints
を検索すると、StatusBar.Java
にupdateTheme
関数が見つかりました。
WallpaperColors systemColors = mColorExtractor
.getWallpaperColors(WallpaperManager.FLAG_SYSTEM);
final boolean useDarkTheme = systemColors != null
&& (systemColors.getColorHints() & WallpaperColors.HINT_SUPPORTS_DARK_THEME) != 0;
これはAndroid 8.1でのみ機能します。テーマは壁紙の色のみに基づいているためです。Android 9.0では、ユーザーは壁紙への接続。
Androidで見たところによると、これが私が作ったものです:
enum class DarkThemeCheckResult {
DEFAULT_BEFORE_THEMES, LIGHT, DARK, PROBABLY_DARK, PROBABLY_LIGHT, USER_CHOSEN
}
@JvmStatic
fun getIsOsDarkTheme(context: Context): DarkThemeCheckResult {
when {
Build.VERSION.SDK_INT <= Build.VERSION_CODES.O -> return DarkThemeCheckResult.DEFAULT_BEFORE_THEMES
Build.VERSION.SDK_INT <= Build.VERSION_CODES.P -> {
val wallpaperManager = WallpaperManager.getInstance(context)
val wallpaperColors = wallpaperManager.getWallpaperColors(WallpaperManager.FLAG_SYSTEM)
?: return DarkThemeCheckResult.UNKNOWN
val primaryColor = wallpaperColors.primaryColor.toArgb()
val secondaryColor = wallpaperColors.secondaryColor?.toArgb() ?: primaryColor
val tertiaryColor = wallpaperColors.tertiaryColor?.toArgb() ?: secondaryColor
val bitmap = generateBitmapFromColors(primaryColor, secondaryColor, tertiaryColor)
val darkHints = calculateDarkHints(bitmap)
//taken from StatusBar.Java , in updateTheme :
val HINT_SUPPORTS_DARK_THEME = 1 shl 1
val useDarkTheme = darkHints and HINT_SUPPORTS_DARK_THEME != 0
if (Build.VERSION.SDK_INT == VERSION_CODES.O_MR1)
return if (useDarkTheme)
DarkThemeCheckResult.UNKNOWN_MAYBE_DARK
else DarkThemeCheckResult.UNKNOWN_MAYBE_LIGHT
return if (useDarkTheme)
DarkThemeCheckResult.MOST_PROBABLY_DARK
else DarkThemeCheckResult.MOST_PROBABLY_LIGHT
}
else -> {
return when (context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) {
Configuration.UI_MODE_NIGHT_YES -> DarkThemeCheckResult.DARK
Configuration.UI_MODE_NIGHT_NO -> DarkThemeCheckResult.LIGHT
else -> DarkThemeCheckResult.MOST_PROBABLY_LIGHT
}
}
}
}
fun generateBitmapFromColors(@ColorInt primaryColor: Int, @ColorInt secondaryColor: Int, @ColorInt tertiaryColor: Int): Bitmap {
val colors = intArrayOf(primaryColor, secondaryColor, tertiaryColor)
val imageSize = 6
val bitmap = Bitmap.createBitmap(imageSize, 1, Bitmap.Config.ARGB_8888)
for (i in 0 until imageSize / 2)
bitmap.setPixel(i, 0, colors[0])
for (i in imageSize / 2 until imageSize / 2 + imageSize / 3)
bitmap.setPixel(i, 0, colors[1])
for (i in imageSize / 2 + imageSize / 3 until imageSize)
bitmap.setPixel(i, 0, colors[2])
return bitmap
}
ほとんどの場合、何も保証されないため、さまざまな値を設定しました。
GoogleはI/O 2019の終わりにダークテーマに関するドキュメントを公開しました here 。
ダークテーマを管理するには、最初に最新バージョンのMaterial Componentsライブラリを使用する必要があります:"com.google.Android.material:material:1.1.0-alpha06"
。
システムのテーマに応じてアプリケーションのテーマを変更します
システムに応じてアプリケーションがダークテーマに切り替わる場合、必要なテーマは1つだけです。これを行うには、テーマに親としてTheme.MaterialComponents.DayNightが必要です。
<style name="AppTheme" parent="Theme.MaterialComponents.DayNight">
...
</style>
現在のシステムテーマを決定します
システムが現在ダークテーマになっているかどうかを確認するには、次のコードを実装できます。
switch (getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK) {
case Configuration.UI_MODE_NIGHT_YES:
…
break;
case Configuration.UI_MODE_NIGHT_NO:
…
break;
}
テーマの変更が通知されます
テーマが変更されるたびに通知されるコールバックを実装することは可能ではないと思いますが、それは問題ではありません。実際、システムがテーマを変更すると、アクティビティは自動的に再作成されます。その場合、アクティビティの最初に前のコードを配置するだけで十分です。
Android SDKのどのバージョンから機能しますか?
Android Pie =Android SDKのバージョン28で動作します。これは、次のバージョンのSDKでのみ機能すると想定しています。 SDK、Q、バージョン29でリリースされます。
結果
Charles Annicの答えに対するより単純なKotlinアプローチ:
fun Context.isDarkThemeOn(): Boolean{
return resources.configuration.uiMode and
Configuration.UI_MODE_NIGHT_MASK == UI_MODE_NIGHT_YES
}
Googleでは、Android Q.
たぶん DayNightテーマ ?
次に、アプリで機能を有効にする必要があります。そのためには、次のいずれかの値を取るAppCompatDelegate.setDefaultNightMode()を呼び出します。
- MODE_NIGHT_NO。常に日(光)テーマを使用します。
- MODE_NIGHT_YES。常に夜(暗い)テーマを使用します。
- MODE_NIGHT_FOLLOW_SYSTEM(デフォルト)。この設定はシステムの設定に従います。Android Pie以上はシステム設定です(これについては以下で詳しく説明します)。
- MODE_NIGHT_AUTO_BATTERY。デバイスの「バッテリーセーバー」機能が有効になっている場合は暗くなり、それ以外の場合は明るくなります。 vv1.1.0-alpha03の新機能。
- MODE_NIGHT_AUTO_TIMEおよびMODE_NIGHT_AUTO。時刻に基づいて、昼と夜の間に変化します。