一.系统主题介绍
android P都已经出来好久了,今天在使用Android P过程中发现在Dislpay下面有一个主题设置(Device theme)
如下所示
可以看出一个有3个选项,通过使用后发现,他运行用户设置手机主题,跟app主题不一样,这里的主题设置可以供系统以及所有应用查询.系统主题一共有暗和亮两张状态.上面设置中3个选项代表的意义如下:
- Automatic (base on wallpaper):系统检查当前设置的手机壁纸属于暗还是亮,然后更具判断设置主题
- Light:设置亮主题
- Dark:设置暗主题
设置亮主题:
设置暗主题:
这样看效果还是挺明显的.通过源码发现,目前系统上launche和SystemUI监听了系统主题的状态,然后显示系统的主题,其他app如果需要根据系统当前主题,也需要实时监听主题变化,
二.如何实现系统主题
个人app要监听系统主题,直接使用WallpaperManager,addOnColorsChangedListener方法,通过listener回调
实现步奏如下:
step1:获取WallpaperManager
if (wallpaperManager == null) {
wallpaperManager = WallpaperManager.getInstance(context.getApplicationContext());
}
调用WallpaperManager的单例,实际上就是getSystemService
public static WallpaperManager getInstance(Context context) {
return (WallpaperManager)context.getSystemService(
Context.WALLPAPER_SERVICE);
}
step2:监听主题变化
这个回调方法会在主题切换中触发,下面是触发后调用的方法
WallpaperManager.OnColorsChangedListener onColorsChangedListener = new WallpaperManager.OnColorsChangedListener(){
@Override
public void onColorsChanged(WallpaperColors colors, int which) {//1
if (WallpaperManager.FLAG_SYSTEM == which) {//2
updateTheme(colors);
}
}
};
wallpaperManager.addOnColorsChangedListener(onColorsChangedListene, null);//3
WallpaperColors colorsSystem = wallpaperManager.getWallpaperColors(WallpaperManager.FLAG_SYSTEM);
updateTheme(colorsSystem);//4
- 3.处是监听主题变化,第二个参数是handler,如果选择null.表示主线程的looper对应的hander
- 1处返回两个参数WallpaperColors,which;WallpaperColors可以判断属于亮还是暗主题,which表示锁屏(WallpaperManager.FLAG_LOCK)还是系统(WallpaperManager.FLAG_SYSTEM),因为主题设置有一个Automatic (base on wallpaper)选项,而锁屏壁纸和桌面壁纸可以不一样,那么换壁纸的时候,会有差异.一般app是参考系统(WallpaperManager.FLAG_SYSTEM)这个配置主题,如2处
- 4处,因为主题回调是在切换主题才会进行,app开始运行的时候,需要回去当时的主题
因为系统主题是在android O上才有的,如果要app最低版本是android O以下,请做判断
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O_MR1) {
}
step3:判断主题是亮还是暗
下面是SystemUI判断主题的代码片段
public void updateTheme(WallpaperColors colors) {
final boolean useDarkTheme = colors != null
&& (colors.getColorHints() & HINT_SUPPORTS_DARK_THEME) != 0;
}
打印log可以发现colors.getColorHints()在亮的时候是4,暗的时候是6,而HINT_SUPPORTS_DARK_THEME == 2
WallpaperColors这个类很多方法被hide修饰,包括getColorHints方法,导致非系统无法调用,如图
Android P后面又限制通过反射来调用系统API,直接调用这个方法是没戏了,但是这个类的toString方法又把这个值给打印出来了.
我们可以截取这个值,然后在判断,于是修改上面的判断
public void updateTheme(WallpaperColors colors) {
final boolean useDarkTheme = colors != null
&& (getColorHints(colors) & HINT_SUPPORTS_DARK_THEME) != 0;
}
private int getColorHints(WallpaperColors colors) {
String str = colors.toString();
int index = str.lastIndexOf("h: ");
String val = str.substring(index + 3, str.length() - 1);
if (TextUtils.isDigitsOnly(val)) {
return Integer.valueOf(val);
}
Log.e(TAG, "can not get getColorHints!!");
return 0;
}
改法不优雅,只能将就使用了
step4:退出监听
wallpaperManager.removeOnColorsChangedListener(onColorsChangedListener);
三.介绍WallpaperColors类
WallpaperColors源码解释:Provides information about the colors of a wallpaper.这个记录壁纸的color
WallpaperColors有几个有意思的方法
方法 | 返回类型 | 意义 |
---|---|---|
getPrimaryColor() | Color | 获取墙纸最具视觉代表性的颜色。 |
getSecondaryColor() | Color | 获得了壁纸的第二个最卓越的颜色。 |
getTertiaryColor() | Color | 获得了壁纸的第三个最卓越的颜色。 |
getMainColors() | List of Color | 按重要性排序的最杰出的颜色列表。 |
getColorHints() | int | 壁纸颜色提示的组合。(暗或者亮) |
calculateDarkHints(Bitmap source) | int | 根据传入的Bitmap,检查图像是否足够明亮 |
fromBitmap(@NonNull Bitmap bitmap) | WallpaperColors | 根据传入的Bitmap,检查图像返回WallpaperColors |
fromDrawable(Drawable drawable) | WallpaperColors | 根据传入的Drawable,检查图像返回WallpaperColors |
四.分享一个封装好的类
import android.app.WallpaperColors;
import android.app.WallpaperManager;
import android.content.Context;
import android.text.TextUtils;
import android.util.Log;
import java.util.ArrayList;
import java.util.List;
public final class ColorExtractor {
public interface ColorsChange {
public void themeChange(boolean useDarkTheme);
}
private static ColorExtractor colorExtractor;
private static final String TAG = "ColorExtractor";
private WallpaperManager wallpaperManager;
private static final int HINT_SUPPORTS_DARK_THEME = 1 << 1;
private WallpaperManager.OnColorsChangedListener onColorsChangedListene;
private List<ColorsChange> listens;
private ColorExtractor() {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O_MR1) {
onColorsChangedListene = new WallpaperManager.OnColorsChangedListener() {
@Override
public void onColorsChanged(WallpaperColors colors, int which) {
if (which == WallpaperManager.FLAG_SYSTEM) {
final boolean useDarkTheme = colors != null
&& (getColorHints(colors) & HINT_SUPPORTS_DARK_THEME) != 0;
if (listens != null && listens.size() > 0) {
for (ColorsChange colorsChange : listens) {
colorsChange.themeChange(useDarkTheme);
}
}
}
}
};
}
listens = new ArrayList<>();
}
public static ColorExtractor getInstance() {
if (colorExtractor == null) {
colorExtractor = new ColorExtractor();
}
return colorExtractor;
}
public void startListen(Context context) {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O_MR1) {
if (wallpaperManager == null) {
wallpaperManager = WallpaperManager.getInstance(context.getApplicationContext());
}
WallpaperColors colorsSystem = wallpaperManager.getWallpaperColors(WallpaperManager.FLAG_SYSTEM);
final boolean useDarkTheme = colorsSystem != null
&& (getColorHints(colorsSystem) & HINT_SUPPORTS_DARK_THEME) != 0;
if (listens != null && listens.size() > 0) {
for (ColorsChange colorsChange : listens) {
colorsChange.themeChange(useDarkTheme);
}
}
wallpaperManager.addOnColorsChangedListener(onColorsChangedListene, null);
}
}
public void addThemeChange(ColorsChange colorsChange) {
if (!listens.contains(colorsChange)) {
listens.add(colorsChange);
}
}
public void removeThemeChange(ColorsChange colorsChange) {
if (listens.contains(colorsChange)) {
listens.remove(colorsChange);
}
}
public void stopListen() {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O_MR1) {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O_MR1) {
wallpaperManager.removeOnColorsChangedListener(onColorsChangedListene);
}
if (listens != null) {
listens.clear();
}
}
}
private int getColorHints(WallpaperColors colors) {
String str = colors.toString();
int index = str.lastIndexOf("h: ");
String val = str.substring(index + 3, str.length() - 1);
if (TextUtils.isDigitsOnly(val)) {
return Integer.valueOf(val);
}
Log.e(TAG, "can not get getColorHints!!");
return 0;
}
}
使用方法:
//单例实例化
ColorExtractor colorExtractor = ColorExtractor.getInstance();
//注册监听事件,因为WallpaperManager注册的时候会调用初始主题,所以为了能获取初始值,所以在开始addThemeChange之前先注册监听事件.
colorExtractor.addThemeChange(new ColorExtractor.ColorsChange() {
@Override
public void themeChange(boolean useDarkTheme) {
Log.i(TAG, "useDarkTheme[" + useDarkTheme + "]");
}
});
//开始监听;一般在onStart(或onCreate)方法
colorExtractor.startListen(this);
//结束监听,一般在onStop(或onDestory)方法
colorExtractor.stopListen();