1黔衡、 應(yīng)用如何獲取壁紙顏色届氢?
1.1缠借、調(diào)用getWallpaperColors獲取壁紙顏色
其實WallpaperManager從很早之前就提供了getWallpaperColors接口
/frameworks/base/core/java/android/app/WallpaperManager.java
...
@UnsupportedAppUsage
public @Nullable WallpaperColors getWallpaperColors(int which, int userId) {
StrictMode.assertUiContext(mContext, "getWallpaperColors");
return sGlobals.getWallpaperColors(which, userId, mContext.getDisplayId());
}
...
WallpaperColors getWallpaperColors(int which, int userId, int displayId) {
if (which != FLAG_LOCK && which != FLAG_SYSTEM) {
throw new IllegalArgumentException(
"Must request colors for exactly one kind of wallpaper");
}
try {
return mService.getWallpaperColors(which, userId, displayId);
} catch (RemoteException e) {
// Can't get colors, connection lost.
}
return null;
}
...
1.2究履、注冊O(shè)nColorsChangedListener接口監(jiān)聽壁紙顏色
1.2.1川慌、獲取整個壁紙顏色
/frameworks/base/core/java/android/app/WallpaperManager.java
...
public void addOnColorsChangedListener(@NonNull OnColorsChangedListener listener,
@NonNull Handler handler) {
addOnColorsChangedListener(listener, handler, mContext.getUserId());
}
...
使用范例:
WallpaperManager wallpaperManager=(WallpaperManager) getSystemService(Context.WALLPAPER_SERVICE);
wallpaperManager.addOnColorsChangedListener(new WallpaperManager.OnColorsChangedListener() {
@Override
public void onColorsChanged(WallpaperColors colors, int which) {
}
});
1.2.2亡笑、獲取壁紙區(qū)域顏色(需要源碼引用)
/frameworks/base/core/java/android/app/WallpaperManager.java
...
/**
* @hide
*/
public void addOnColorsChangedListener(@NonNull LocalWallpaperColorConsumer callback,
List<RectF> regions) throws IllegalArgumentException {
for (RectF region : regions) {
if (!LOCAL_COLOR_BOUNDS.contains(region)) {
throw new IllegalArgumentException("Regions must be within bounds "
+ LOCAL_COLOR_BOUNDS);
}
}
sGlobals.addOnColorsChangedListener(callback, regions, FLAG_SYSTEM,
mContext.getUserId(), mContext.getDisplayId());
}
...
==Tip: LocalWallpaperColorConsumer是hide接口侣夷,非源碼編譯的Apk無法引用==
使用范例:
addOnColorsChangedListener(new LocalWallpaperColorConsumer() {
@java.lang.Override
public void onColorsChanged(RectF area, WallpaperColors colors) {
}
},handler);
1.3、使用WallpaperColors
取出的數(shù)據(jù)都封裝在WallpaperColors中仑乌,可通過以下接口獲取到原始數(shù)據(jù)
/frameworks/base/core/java/android/app/WallpaperColors.java
//獲取墻紙最具視覺代表性的顏色百拓。“視覺上具有代表性”是指在圖像中很容易被注意到晰甚,可能發(fā)生在高頻率衙传。不為空
public @NonNull Color getPrimaryColor() {
return mMainColors.get(0);
}
//獲取壁紙的第二個最杰出的顏色〔蘧牛可以為空
public @Nullable Color getSecondaryColor() {
return mMainColors.size() < 2 ? null : mMainColors.get(1);
}
//獲得壁紙的第三個最杰出的顏色蓖捶。可以為空
public @Nullable Color getTertiaryColor() {
return mMainColors.size() < 3 ? null : mMainColors.get(2);
}
//最杰出的顏色列表,按重要性排序
public @NonNull List<Color> getMainColors() {
return Collections.unmodifiableList(mMainColors);
}
//得到所有提取出來的顏色扁远,key是顏色的rgb int值 俊鱼,value是出現(xiàn)次數(shù)
public @NonNull Map<Integer, Integer> getAllColors() {
return Collections.unmodifiableMap(mAllColors);
}
框架中只是負責(zé)提取出原始顏色刻像,但有些時候直接使用原始顏色,并不能達到最好的效果并闲。
例如:PixelLauncher的插件實現(xiàn)顏色動態(tài)變化细睡,除了從Wallpaper中拿到顏色,還進行了二次加工帝火,使其更符合Material Desgin的風(fēng)格溜徙,這也是符合“Monet”主題系統(tǒng) 中的一部分,目前是沒有開源的购公。所以一方或三方若想使用該特性萌京,可以按需進行選擇和二次加工。
2知残、根據(jù)壁紙取色,能取出多少種顏色神凑,顏色的格式和使用場景是什么?
在Android 13上坡慌,內(nèi)存低的機器 ,最多取出五種色值岖寞,內(nèi)存高的機器淑履,最多取出128種顏色勉耀。
/frameworks/base/core/java/android/app/WallpaperColors.java
...
public static WallpaperColors fromBitmap(@NonNull Bitmap bitmap,
@FloatRange (from = 0f, to = 1f) float dimAmount) {
...
if (ActivityManager.isLowRamDeviceStatic()) {
palette = Palette
.from(bitmap, new VariationalKMeansQuantizer())
.maximumColorCount(5)
.resizeBitmapArea(MAX_WALLPAPER_EXTRACTION_AREA)
.generate();
} else {
palette = Palette
.from(bitmap, new CelebiQuantizer())
.maximumColorCount(128)
.resizeBitmapArea(MAX_WALLPAPER_EXTRACTION_AREA)
.generate();
}
...
- 取出的是一個WallpaperColors對象像街,其中包含三種主顏色畴栖,以及一個保存了所有提取出顏色的Color數(shù)組(Hide)吗讶。
- 目前的應(yīng)用場景主要是PixelLauncher、SystemUI恋捆、GBoard
3关翎、能從動態(tài)壁紙中取色嗎?
很可惜鸠信,Google并沒有為動態(tài)壁紙?zhí)峁┠J的取色邏輯。
壁紙的取色论寨,最終會調(diào)用到WallpaperService的onComputeColors中星立,但該方法是空實現(xiàn),框架只是提供了最基本的獲取接口而已葬凳。
所以在Android 13之前绰垂,無論是靜態(tài)壁紙或是動態(tài) 壁紙,若想要其他模塊能獲取到壁紙顏色的話火焰,需要壁紙?zhí)峁┓骄⒆埃趯崿F(xiàn)WallpaperService的Engine時,也一并實現(xiàn)onCompleteColors方法,然后在顏色變化時調(diào)用notifyColorsChanged占业。
/frameworks/base/core/java/android/service/wallpaper/WallpaperService.java
...
public void notifyColorsChanged() {
...
try {
//關(guān)鍵調(diào)用
final WallpaperColors newColors = onComputeColors();
if (mConnection != null) {
//通知給監(jiān)聽壁紙顏色變化的模塊
mConnection.onWallpaperColorsChanged(newColors, mDisplay.getDisplayId());
} else {
Log.w(TAG, "Can't notify system because wallpaper connection "
+ "was not established.");
}
...
} catch (RemoteException e) {
Log.w(TAG, "Can't notify system because wallpaper connection was lost.", e);
}
}
//重寫該方法
public @Nullable WallpaperColors onComputeColors() {
return null;
}
...
如圖:
特性 | < Android 13 | Android 13 |
---|---|---|
靜態(tài)壁紙 | 沒有默認實現(xiàn) | 提供了默認實現(xiàn)绒怨,可以根據(jù)區(qū)域取色 |
動態(tài)壁紙 | 空實現(xiàn) | 空實現(xiàn) |
4、壁紙取色失敗會使用默認顏色嗎谦疾?
壁紙其實是通過Palette接口進行顏色提取的南蹂,基本上都能取到至少一種顏色。當(dāng)真的取出失敗或取出的顏色如果不符合期望念恍,需要采用另外一套默認顏色六剥,這是由各個各個業(yè)務(wù)去處理的,比如Launcher中的LauncherAppWidgetHostView峰伙,重寫了setColorResources方法
/packages/apps/Launcher3/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
...
@Override
public void setColorResources(@Nullable SparseIntArray colors) {
if (colors == null) {
//關(guān)鍵點1疗疟,提取不到顏色,會重置插件的顏色
resetColorResources();
} else {
//關(guān)鍵點2瞳氓,設(shè)置插件的顏色
super.setColorResources(colors);
}
}
...
5策彤、Widget 取色的整體流程是怎樣?
具體流程見下圖:
PixelLauncher是通過LocalWallpaperColorsExtractor的實現(xiàn)類進行顏色提取顿膨,該類繼承于LocalColorExtractor锅锨,該類存在于Launcher3中,是空實現(xiàn)恋沃”馗悖可以看出,Google專門將Monet算法部分折分隱藏囊咏,只公開框架部分恕洲。
而LocalWallpaperColorsExtractor是通過調(diào)用WallpaperManager.addOnColorsChangedListener接口,獲取onColorsChanged(RectF rectF, WallpaperColors wallpaperColors)中的返回信息梅割,然后再對WallpaperColors中的顏色信息進行二次加工處理霜第。
6、Widget 能使用的顏色數(shù)量和范圍
Widget能使用的色值數(shù)量沒有限制户辞,從android.R.color.system_neutrall_0(#FFFFFF) 到android.R.color.system_accent3_1000(#000000)都是合法范圍泌类。
7、為什么現(xiàn)有第三方Launcher即使在Android13上底燎,Widget的取色也會無效刃榨,而 Pixel Launcher可以。
第三方 Launcher 代碼中雖然已經(jīng)有調(diào)用AppWidgetHostView.setColorResources双仍,但是因為其提取顏色的LocalColorExtractor是空實現(xiàn)枢希,所以沒有實際效果。而Pixel Launcher 自己寫了一個LocalWallpaperColorsExtractor去繼承LocalColorExtractor朱沃。詳見以下代碼:
/packages/apps/Launcher3/src/com/android/launcher3/widget/LocalColorExtractor.java
...
public static LocalColorExtractor newInstance(Context context) {
return Overrides.getObject(WallpaperColors.class, context.getApplicationContext(),
R.string.local_colors_extraction_class);
}
...
local_colors_extraction_class 該值定義在PixelLauncher中
<string name="local_colors_extraction_class">com.google.android.apps.nexuslauncher.widget.LocalWallpaperColorsExtractor</string>
8苞轿、是否可以固定設(shè)置一套或者幾套顏色茅诱,去overlay 壁紙的取色?
可以搬卒,Android 13已經(jīng)在secure表中預(yù)定義了theme_customization_overlay_packages字段瑟俭,在其中配置了是否要根據(jù)主屏幕變色還是預(yù)置顏色,然后進行資源Overlay的秀睛。
Android 原生設(shè)置入口
theme_customization_overlay_packages字段含義
字段 | 含義 | 取值范圍 |
---|---|---|
android.theme.customization.color_index | 選擇顏色索引 | 0~n |
android.theme.customization.system_palette | 中立色 | color |
android.theme.customization.accent_color | 強調(diào)色 | color |
android.theme.customization.color_source | 采用跟隨壁紙顏色還是基本顏色 | present \ home_wallpaper \ lock_wallpaper |
android.theme.customization.color_both" | 區(qū)分當(dāng)前壁紙設(shè)置類型 | 1 鎖屏和主屏幕尔当, 0 只設(shè)置其中一項 |
我們可以直接使用或借鑒這套邏輯,預(yù)置幾套色值蹂安,并設(shè)置為使用基本顏色椭迎。