Android利用Palette實現(xiàn)提取圖片顏色
前言
Palette即調(diào)色板這個功能其實很早就發(fā)布了,Jetpack同樣將這個功能也納入其中,想要使用這個功能,需要先依賴庫
implementation 'androidx.palette:palette:1.0.0'
本篇文章就來講解一下如何使用Palette在圖片中提取顏色。
創(chuàng)建Palette
創(chuàng)建Palette其實很簡單,如下
var builder = Palette.from(bitmap) var palette = builder.generate()
這樣,我們就通過一個Bitmap創(chuàng)建一個Pallete對象。
注意:直接使用Palette.generate(bitmap)也可以,但是這個方法已經(jīng)不推薦使用了,網(wǎng)上很多老文章中依然使用這種方式。建議還是使用Palette.Builder這種方式。
generate()這個函數(shù)是同步的,當(dāng)然考慮圖片處理可能比較耗時,Android同時提供了異步函數(shù)
public AsyncTask<Bitmap, Void, Palette> generate(
@NonNull final PaletteAsyncListener listener) {通過一個PaletteAsyncListener來獲取Palette實例,這個接口如下:
public interface PaletteAsyncListener {
/**
* Called when the {@link Palette} has been generated. {@code null} will be passed when an
* error occurred during generation.
*/
void onGenerated(@Nullable Palette palette);
}提取顏色
有了Palette實例,就可以通過Palette對象的相應(yīng)函數(shù)就可以獲取圖片中的顏色,而且不只一種顏色,下面一一列舉:
- getDominantColor:獲取圖片中的主色調(diào)
- getMutedColor:獲取圖片中柔和的顏色
- getDarkMutedColor:獲取圖片中柔和的暗色
- getLightMutedColor:獲取圖片中柔和的亮色
- getVibrantColor:獲取圖片中有活力的顏色
- getDarkVibrantColor:獲取圖片中有活力的暗色
- getLightVibrantColor:獲取圖片中有活力的亮色
這些函數(shù)都需要提供一個默認顏色,如果這個顏色Swatch無效則使用這個默認顏色。光這么說不直觀,我們來測試一下,代碼如下:
var bitmap = BitmapFactory.decodeResource(resources, R.mipmap.a) var builder = Palette.from(bitmap) var palette = builder.generate() color0.setBackgroundColor(palette.getDominantColor(Color.WHITE)) color1.setBackgroundColor(palette.getMutedColor(Color.WHITE)) color2.setBackgroundColor(palette.getDarkMutedColor(Color.WHITE)) color3.setBackgroundColor(palette.getLightMutedColor(Color.WHITE)) color4.setBackgroundColor(palette.getVibrantColor(Color.WHITE)) color5.setBackgroundColor(palette.getDarkVibrantColor(Color.WHITE)) color6.setBackgroundColor(palette.getLightVibrantColor(Color.WHITE))
運行后結(jié)果如下:

這樣各個顏色的差別就一目了然。除了上面的函數(shù),還可以使用getColorForTarget這個函數(shù),如下:
@ColorInt
public int getColorForTarget(@NonNull final Target target, @ColorInt final int defaultColor) {這個函數(shù)需要一個Target,提供了6個靜態(tài)字段,如下:
/** * A target which has the characteristics of a vibrant color which is light in luminance. */ public static final Target LIGHT_VIBRANT; /** * A target which has the characteristics of a vibrant color which is neither light or dark. */ public static final Target VIBRANT; /** * A target which has the characteristics of a vibrant color which is dark in luminance. */ public static final Target DARK_VIBRANT; /** * A target which has the characteristics of a muted color which is light in luminance. */ public static final Target LIGHT_MUTED; /** * A target which has the characteristics of a muted color which is neither light or dark. */ public static final Target MUTED; /** * A target which has the characteristics of a muted color which is dark in luminance. */ public static final Target DARK_MUTED;
其實就是對應(yīng)著上面除了主色調(diào)之外的六種顏色。
文字顏色自動適配
在上面的運行結(jié)果中可以看到,每個顏色上面的文字都很清楚的顯示,而且它們并不是同一種顏色。其實這也是Palette提供的功能。
通過下面的函數(shù),我們可以得到各種色調(diào)所對應(yīng)的Swatch對象:
- getDominantSwatch
- getMutedSwatch
- getDarkMutedSwatch
- getLightMutedSwatch
- getVibrantSwatch
- getDarkVibrantSwatch
- getLightVibrantSwatch
注意:同上面一樣,也可以通過getSwatchForTarget(@NonNull final Target target)來獲取
Swatch類提供了以下函數(shù):
- getPopulation(): 樣本中的像素數(shù)量
- getRgb(): 顏色的RBG值
- getHsl(): 顏色的HSL值
- getBodyTextColor(): 能都適配這個Swatch的主體文字的顏色值
- getTitleTextColor(): 能都適配這個Swatch的標(biāo)題文字的顏色值
所以我們通過getBodyTextColor()和getTitleTextColor()可以很容易得到在這個顏色上可以很好現(xiàn)實的標(biāo)題和主體文本顏色。所以上面的測試代碼完整如下:
var bitmap = BitmapFactory.decodeResource(resources, R.mipmap.a) var builder = Palette.from(bitmap) var palette = builder.generate() color0.setBackgroundColor(palette.getDominantColor(Color.WHITE)) color0.setTextColor(palette.dominantSwatch?.bodyTextColor ?: Color.WHITE) color1.setBackgroundColor(palette.getMutedColor(Color.WHITE)) color1.setTextColor(palette.mutedSwatch?.bodyTextColor ?: Color.WHITE) color2.setBackgroundColor(palette.getDarkMutedColor(Color.WHITE)) color2.setTextColor(palette.darkMutedSwatch?.bodyTextColor ?: Color.WHITE) color3.setBackgroundColor(palette.getLightMutedColor(Color.WHITE)) color3.setTextColor(palette.lightMutedSwatch?.bodyTextColor ?: Color.WHITE) color4.setBackgroundColor(palette.getVibrantColor(Color.WHITE)) color4.setTextColor(palette.vibrantSwatch?.bodyTextColor ?: Color.WHITE) color5.setBackgroundColor(palette.getDarkVibrantColor(Color.WHITE)) color5.setTextColor(palette.darkVibrantSwatch?.bodyTextColor ?: Color.WHITE) color6.setBackgroundColor(palette.getLightVibrantColor(Color.WHITE)) color6.setTextColor(palette.lightVibrantSwatch?.bodyTextColor ?: Color.WHITE)
這樣每個顏色上的文字都可以清晰的顯示。
那么這個標(biāo)題和主體文本顏色有什么差別,他們又是如何的到的?我們來看看源碼:
/**
* Returns an appropriate color to use for any 'title' text which is displayed over this
* {@link Swatch}'s color. This color is guaranteed to have sufficient contrast.
*/
@ColorInt
public int getTitleTextColor() {
ensureTextColorsGenerated();
return mTitleTextColor;
}
/**
* Returns an appropriate color to use for any 'body' text which is displayed over this
* {@link Swatch}'s color. This color is guaranteed to have sufficient contrast.
*/
@ColorInt
public int getBodyTextColor() {
ensureTextColorsGenerated();
return mBodyTextColor;
}可以看到都會先執(zhí)行ensureTextColorsGenerated(),它的源碼如下:
private void ensureTextColorsGenerated() {
if (!mGeneratedTextColors) {
// First check white, as most colors will be dark
final int lightBodyAlpha = ColorUtils.calculateMinimumAlpha(
Color.WHITE, mRgb, MIN_CONTRAST_BODY_TEXT);
final int lightTitleAlpha = ColorUtils.calculateMinimumAlpha(
Color.WHITE, mRgb, MIN_CONTRAST_TITLE_TEXT);
if (lightBodyAlpha != -1 && lightTitleAlpha != -1) {
// If we found valid light values, use them and return
mBodyTextColor = ColorUtils.setAlphaComponent(Color.WHITE, lightBodyAlpha);
mTitleTextColor = ColorUtils.setAlphaComponent(Color.WHITE, lightTitleAlpha);
mGeneratedTextColors = true;
return;
}
final int darkBodyAlpha = ColorUtils.calculateMinimumAlpha(
Color.BLACK, mRgb, MIN_CONTRAST_BODY_TEXT);
final int darkTitleAlpha = ColorUtils.calculateMinimumAlpha(
Color.BLACK, mRgb, MIN_CONTRAST_TITLE_TEXT);
if (darkBodyAlpha != -1 && darkTitleAlpha != -1) {
// If we found valid dark values, use them and return
mBodyTextColor = ColorUtils.setAlphaComponent(Color.BLACK, darkBodyAlpha);
mTitleTextColor = ColorUtils.setAlphaComponent(Color.BLACK, darkTitleAlpha);
mGeneratedTextColors = true;
return;
}
// If we reach here then we can not find title and body values which use the same
// lightness, we need to use mismatched values
mBodyTextColor = lightBodyAlpha != -1
? ColorUtils.setAlphaComponent(Color.WHITE, lightBodyAlpha)
: ColorUtils.setAlphaComponent(Color.BLACK, darkBodyAlpha);
mTitleTextColor = lightTitleAlpha != -1
? ColorUtils.setAlphaComponent(Color.WHITE, lightTitleAlpha)
: ColorUtils.setAlphaComponent(Color.BLACK, darkTitleAlpha);
mGeneratedTextColors = true;
}
}通過代碼可以看到,這兩種文本顏色實際上要么是白色要么是黑色,只是透明度Alpha不同。
這里面有一個關(guān)鍵函數(shù),即ColorUtils.calculateMinimumAlpha():
public static int calculateMinimumAlpha(@ColorInt int foreground, @ColorInt int background,
float minContrastRatio) {
if (Color.alpha(background) != 255) {
throw new IllegalArgumentException("background can not be translucent: #"
+ Integer.toHexString(background));
}
// First lets check that a fully opaque foreground has sufficient contrast
int testForeground = setAlphaComponent(foreground, 255);
double testRatio = calculateContrast(testForeground, background);
if (testRatio < minContrastRatio) {
// Fully opaque foreground does not have sufficient contrast, return error
return -1;
}
// Binary search to find a value with the minimum value which provides sufficient contrast
int numIterations = 0;
int minAlpha = 0;
int maxAlpha = 255;
while (numIterations <= MIN_ALPHA_SEARCH_MAX_ITERATIONS &&
(maxAlpha - minAlpha) > MIN_ALPHA_SEARCH_PRECISION) {
final int testAlpha = (minAlpha + maxAlpha) / 2;
testForeground = setAlphaComponent(foreground, testAlpha);
testRatio = calculateContrast(testForeground, background);
if (testRatio < minContrastRatio) {
minAlpha = testAlpha;
} else {
maxAlpha = testAlpha;
}
numIterations++;
}
// Conservatively return the max of the range of possible alphas, which is known to pass.
return maxAlpha;
}它根據(jù)背景色和前景色計算前景色最合適的Alpha。這期間如果小于minContrastRatio則返回-1,說明這個前景色不合適。而標(biāo)題和主體文本的差別就是這個minContrastRatio不同而已。
回到ensureTextColorsGenerated代碼可以看到,先根據(jù)當(dāng)前色調(diào),計算出白色前景色的Alpha,如果兩個Alpha都不是-1,就返回對應(yīng)顏色;否則計算黑色前景色的Alpha,如果都不是-1,返回對應(yīng)顏色;否則標(biāo)題和主體文本一個用白色一個用黑色,返回對應(yīng)顏色即可。
更多功能
上面我們創(chuàng)建Palette時先通過Palette.from(bitmap)的到了一個Palette.Builder對象,通過這個builder可以實現(xiàn)更多功能,比如:
- addFilter:增加一個過濾器
- setRegion:設(shè)置圖片上的提取區(qū)域
- maximumColorCount:調(diào)色板的最大顏色數(shù)
等等
總結(jié)
通過上面我們看到,Palette的功能很強大,但是它使用起來非常簡單,可以讓我們很方便的提取圖片中的顏色,并且適配合適的文字顏色。同時注意因為ColorUtils是public的,所以當(dāng)我們需要文字自動適配顏色的情況時,也可以通過ColorUtils的幾個函數(shù)自己實現(xiàn)計算動態(tài)顏色的方案。
以上就是Android利用Palette實現(xiàn)提取圖片顏色的詳細內(nèi)容,更多關(guān)于Android Palette提取顏色的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
android 跳轉(zhuǎn)到應(yīng)用通知設(shè)置界面的示例
本篇文章主要介紹了android 跳轉(zhuǎn)到應(yīng)用通知設(shè)置界面的示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-10-10
Notification消息通知 自定義消息通知內(nèi)容布局
這篇文章主要為大家詳細介紹了Notification消息通知,消息合并且顯示條數(shù),自定義消息通知內(nèi)容布局,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-09-09
Android webview轉(zhuǎn)PDF的方法示例
本篇文章主要介紹了Android webview轉(zhuǎn)PDF的方法示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-01-01
Android自定義ActionProvider ToolBar實現(xiàn)Menu小紅點
這篇文章主要介紹了Android自定義ActionProvider ToolBar實現(xiàn)Menu小紅點,具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-09-09
Android開發(fā)Jetpack組件WorkManager用例詳解
這篇文章主要為大家介紹了Android開發(fā)Jetpack組件WorkManager的使用案例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助2022-02-02
Android socket實現(xiàn)原理詳解 服務(wù)端和客戶端如何搭建
這篇文章主要為大家詳細介紹了Android socket實現(xiàn)原理詳解,以及服務(wù)端和客戶端的搭建方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-08-08

