Palette 是 Android L SDK 中的新特性,可以使用 Palette 从图像中提取出突出的颜色(主色调),获取到颜色之后我们再将这个颜色值赋给 ActionBar、状态栏等。从而达到界面色调的统一,使界面美观协调。
Palette 原理:通过得到一个 bitmap,通过方法进行分析,取出
LightVibrantSwatch,DarkVibrantSwatch,LightMutedSwatch,DarkMutedSwatch
这些样本,然后得到 rgb 值。
方法 | 介绍 |
---|---|
Palette.Builder | 生成器类,生成 Palette 实例 |
Palette.Filter | 过滤器接口,使 Palette 有更加细腻的颜色过滤 |
Palette.PaletteAsyncListener | 异步加载监听 |
pattle.Swatch | 提供获取结果的色彩样本 |
from(List |
通过预设的 Palette.Swatch 颜色样本列表 来生成 Palette |
from(Bitmap bitmap) | 通过返回 Palette.Builder 实例来构建 Palette |
palette.getDarkMutedColor(Color.BLUE) | 获取到柔和的深色的颜色(可传默认值) |
palette.getDarkVibrantColor(Color.BLUE) | 获取到活跃的深色的颜色(可传默认值) |
palette.getLightMutedColor(Color.BLUE) | 获取到柔和的明亮的颜色(可传默认值) |
palette.getLightVibrantColor(Color.BLUE) | 获取到活跃的明亮的颜色(可传默认值) |
palette.getVibrantColor(Color.BLUE) | 获取图片中最活跃的颜色(也可以说整个图片出现最多的颜色)(可传默认值) |
palette.getMutedColor(Color.BLUE) | 获取图片中一个最柔和的颜色(可传默认值) |
Palette.Builder
生成器类,生成 Palette 实例
Palette.Filter
过滤器接口,使 Palette 有更加细腻的颜色过滤
Palette.PaletteAsyncListener
异步加载监听
pattle.Swatch
提供获取结果的色彩样本
from(List
通过预设的 Palette.Swatch 颜色样本列表 来生成 Palette
from(Bitmap bitmap)
通过返回 Palette.Builder 实例来构建 Palette
palette.getDarkMutedColor(Color.BLUE)
获取到柔和的深色的颜色(可传默认值)
palette.getDarkVibrantColor(Color.BLUE)
获取到活跃的深色的颜色(可传默认值)
palette.getLightMutedColor(Color.BLUE)
获取到柔和的明亮的颜色(可传默认值)
palette.getLightVibrantColor(Color.BLUE)
获取到活跃的明亮的颜色(可传默认值)
palette.getVibrantColor(Color.BLUE)
获取图片中最活跃的颜色(也可以说整个图片出现最多的颜色)(可传默认值)
palette.getMutedColor(Color.BLUE)
获取图片中一个最柔和的颜色(可传默认值)
implementation 'androidx.palette:palette:1.0.0'
Palette 创建有同步和异步两种方式,开发中我们为了提高应用性能,比较耗时的操作都会采用异步方式。
Bitmap bm =BitmapFactory.decodeResource(getResources(),R.drawable.kale);
// 同步
Palette.Builder builder = Palette.from(bm);
Palette palette=builder.generate();
// 异步
builder.generate(bitmap, new Palette.PaletteAsyncListener() {
@Override
public void onGenerated(Palette palette) {
}
}
Palette 可以分析提取出以下突出的颜色,在应用适用的环境下灵活使用,如下图所示,获取到屏幕中图片 BitMap 对象,然后通过 Palette 提取到相关属性,在 TextView 设置背景颜色。
Bitmap bitmap = ((BitmapDrawable) imageView.getDrawable()).getBitmap();
Palette.from(bitmap).generate(new Palette.PaletteAsyncListener() {
@Override
public void onGenerated(@Nullable Palette palette) {
//获取到柔和的深色的颜色(可传默认值)
int darkMutedColor = palette.getDarkMutedColor(Color.BLUE);//如果分析不出来,则返回默认颜色
//获取到柔和的明亮的颜色(可传默认值)
int lightMutedColor = palette.getLightMutedColor(Color.BLUE);
//获取到活跃的深色的颜色(可传默认值)
int darkVibrantColor = palette.getDarkVibrantColor(Color.BLUE);
//获取到活跃的明亮的颜色(可传默认值)
int lightVibrantColor = palette.getLightVibrantColor(Color.BLUE);
//获取图片中一个最柔和的颜色(可传默认值)
int mutedColor = palette.getMutedColor(Color.BLUE);
//获取图片中最活跃的颜色(也可以说整个图片出现最多的颜色)(可传默认值)
int vibrantColor = palette.getVibrantColor(Color.BLUE);
//获取某种特性颜色的样品
Palette.Swatch lightVibrantSwatch = palette.getVibrantSwatch();
//谷歌推荐的:图片的整体的颜色rgb的混合值---主色调
int rgb = lightVibrantSwatch.getRgb();
//谷歌推荐:图片中间的文字颜色
int bodyTextColor = lightVibrantSwatch.getBodyTextColor();
//谷歌推荐:作为标题的颜色(有一定的和图片的对比度的颜色值)
int titleTextColor = lightVibrantSwatch.getTitleTextColor();
//颜色向量
float[] hsl = lightVibrantSwatch.getHsl();
//分析该颜色在图片中所占的像素多少值
int population = lightVibrantSwatch.getPopulation();
tv1.setText("darkMutedColor");
tv1.setBackgroundColor(darkMutedColor);
tv2.setText("lightMutedColor");
tv2.setBackgroundColor(lightMutedColor);
tv3.setText("darkVibrantColor");
tv3.setBackgroundColor(darkVibrantColor);
tv4.setText("lightVibrantColor");
tv4.setBackgroundColor(lightVibrantColor);
tv5.setText("mutedColor");
tv5.setBackgroundColor(mutedColor);
tv6.setText("vibrantColor");
tv6.setBackgroundColor(vibrantColor);
}
});
通过上述简单介绍,我们已经大致清除 Palette 是干什么的,接下来就根据开发中常见需求实现效果。
在列表页加载卡片的情况下,经常会在图片底部或者顶部添加一片半透明区域,用来显示文本信息,以前常规做法是设置一个半透明颜色,但是这种做法在暗灰色的图片上,展示效果极差。Google 提供 Palette 之后,就可以根据图片分析出最适合的底色作为背景色,让图片和整列表更加优雅的展示。
本文主要内容是 Palette,如果对 CardView 不熟悉的朋友可以查看前文:
创建完一个 Palette 实例之后,我们还需要得到一种采集的样本(swatch),有 6 中样本(swatch)
Palette.getVibrantSwatch()
返回一个鲜明(有活力)的样本类Palette.getDarkVibrantSwatch()
返回一个鲜明(有活力)的暗色调样本类Palette.getLightVibrantSwatch()
返回一个鲜明(有活力)的亮色调样本类Palette.getMutedSwatch()
返回一个柔和的样本类Palette.getDarkMutedSwatch()
返回一个柔和的暗色调样本类Palette.getLightMutedSwatch()
返回一个柔和的亮色调样本类以上 6 种样本色调使用方式一模一样,这里用getVibrantSwatch()
举例说明,其他用法自行上手练习。
//获取某种特性颜色的样品
Palette.Swatch lightVibrantSwatch = palette.getVibrantSwatch();
if (lightVibrantSwatch == null) {
for (Palette.Swatch swatch : palette.getSwatches()) {
lightVibrantSwatch = swatch;
break;
}
}
//谷歌推荐的:图片的整体的颜色rgb的混合值---主色调
int rgb = lightVibrantSwatch.getRgb();
//谷歌推荐:图片中间的文字颜色
int bodyTextColor = lightVibrantSwatch.getBodyTextColor();
//谷歌推荐:作为标题的颜色(有一定的和图片的对比度的颜色值)
int titleTextColor = lightVibrantSwatch.getTitleTextColor();
注意:getVibrantSwatch()
方法返回的 Palette.Swatch 对象有可能为 null,所以一定要判断是否为 null,否则可能会抛出NullPointerException
异常。
当我们获取到图片的整体的颜色 rgb 的混合值,这个值就是跟图片最接近的颜色,也是 Google 官方推荐使用的主色调,要想达到前文中降的效果,应用时还需对 rgb 值进行透明后再使用。
/**
* @param percent 透明度
* @param rgb RGB值
* @return 最终设置过透明度的颜色值
*/
protected int getTranslucentColor(float percent, int rgb) {
int blue = Color.blue(rgb);
int green = Color.green(rgb);
int red = Color.red(rgb);
int alpha = Color.alpha(rgb);
alpha = Math.round(alpha * percent);
return Color.argb(alpha, red, green, blue);
}
因为发现很多文章都是采用本地资源库图片,获取 BitMap 方式比较简单,在实际项目中,如果加载网络图片的话,可以使用 Glide.asBitMap()
方法来实现,这里使用了一个 Adapter 的三方库,防止朋友们看懵逼,贴上 Adapter 完整代码,如果想学习的小伙伴,建议在文末下载源码学习。
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/cardView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:clickable="true"
android:focusable="true"
android:foreground="@drawable/item_touch_bg"
app:cardCornerRadius="1dp"
app:cardElevation="1dp"
app:cardPreventCornerOverlap="false"
app:cardUseCompatPadding="true">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/articleListImg"
android:layout_width="match_parent"
android:layout_height="200dp"
android:contentDescription="@null"
android:scaleType="centerCrop"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:src="@drawable/ic_launcher_background" />
<TextView
android:id="@+id/articleListTitle"
android:layout_width="match_parent"
android:layout_height="70dp"
android:gravity="center"
android:textColor="@color/white"
android:textSize="18dp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="@+id/articleListImg"
app:layout_constraintStart_toStartOf="parent" />
androidx.constraintlayout.widget.ConstraintLayout>
androidx.cardview.widget.CardView>
public class PaletteImageAdapter extends BaseCompatAdapter<ArticleBean, BaseViewHolder> {
public PaletteImageAdapter(int layoutResId, List<ArticleBean> data) {
super(layoutResId, data);
}
@Override
protected void convert(BaseViewHolder helper, ArticleBean item) {
// 使用Glide.asBitmap()方法转换
FutureTarget<Bitmap> bitmap = Glide.with(mContext)
.asBitmap()
.load(item.getImageUrl())
.submit();
// 在子线程中执行提取颜色任务,Palette提取颜色根据图片质量耗时不同,属于比较耗时的操作
new Thread(() -> {
try {
setPalette(bitmap.get(), helper, item);
} catch (Exception e) {
e.printStackTrace();
}
}).start();
}
private void setPalette(Bitmap bitmap, BaseViewHolder helper, ArticleBean item) {
Palette.from(bitmap).generate(new Palette.PaletteAsyncListener() {
@Override
public void onGenerated(@Nullable Palette palette) {
//获取某种特性颜色的样品
Palette.Swatch lightVibrantSwatch = palette.getVibrantSwatch();
if (lightVibrantSwatch == null) {
for (Palette.Swatch swatch : palette.getSwatches()) {
lightVibrantSwatch = swatch;
break;
}
}
//谷歌推荐的:图片的整体的颜色rgb的混合值---主色调
int rgb = lightVibrantSwatch.getRgb();
//谷歌推荐:图片中间的文字颜色
int bodyTextColor = lightVibrantSwatch.getBodyTextColor();
//谷歌推荐:作为标题的颜色(有一定的和图片的对比度的颜色值)
int titleTextColor = lightVibrantSwatch.getTitleTextColor();
helper.setText(R.id.articleListTitle, item.getTitle())
.setTextColor(R.id.articleListTitle, titleTextColor)
.setBackgroundColor(R.id.articleListTitle, getTranslucentColor(0.8f, rgb));
((ImageView) helper.getView(R.id.articleListImg)).setImageBitmap(bitmap);
}
});
}
}
其实前面代码注释里已经提到了,Palette 加载不能在主线程中进行,如果是列表展示图片时,会报错。因为 Palette 提取图片色彩的操作是比较耗时的,所以一定要在子线程中执行。
加载方式有同步加载和异步加载两种:
由于他们很可能会比较耗时(在分析大图片或者所需颜色较多时),所以它们不应该在主线程中执行。你应该先在别的线程中使用这两个函数进行解析,解析成功之后再使用。
有时候你不会在加载图片的线程(非主线程)中使用解析出的颜色,所以 Palette 提供了异步方法,他们与之前的函数的区别就是需要传入 PaletteAsyncListener,提供在图片解析完成后的回调函数。
源码下载 源码包含 Material Design 系列控件集合,定时更新,敬请期待!
现在很多流行的 APP 列表界面垂直滑动时,会根据内容色调动态更改 ToolBar 的颜色,这种效果就可以借助 Palette 来采取图片颜色实现。其实 Palette 并不只适用于我写的示例,Palette 应用的情况很多,感兴趣的朋友可以在官网详细学习!
我的微信:Jaynm888
欢迎点评,
诚邀 Android 程序员加入微信交流群
,公众号回复加群或者加我微信邀请入群。