前言
Palette 是 Android L SDK 中的新特性,可以使用 Palette 从图像中提取出突出的颜色(主色调),获取到颜色之后我们再将这个颜色值赋给 ActionBar、状态栏等。从而达到界面色调的统一,使界面美观协调。
Palette 原理:通过得到一个 bitmap,通过方法进行分析,取出
LightVibrantSwatch,DarkVibrantSwatch,LightMutedSwatch,DarkMutedSwatch
这些样本,然后得到 rgb 值。
一、Palette 相关方法
方法 | 介绍 |
---|---|
Palette.Builder | 生成器类,生成 Palette 实例 |
Palette.Filter | 过滤器接口,使 Palette 有更加细腻的颜色过滤 |
Palette.PaletteAsyncListener | 异步加载监听 |
pattle.Swatch | 提供获取结果的色彩样本 |
from(List<Palette.Switch> switches) | 通过预设的 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.Switch> switches)
通过预设的 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)
获取图片中一个最柔和的颜色(可传默认值)
二、基本使用
1、导入依赖
implementation 'androidx.palette:palette:1.0.0'
2、Palette 创建
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) {
}
}
3、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 + CardView 案例
通过上述简单介绍,我们已经大致清除 Palette 是干什么的,接下来就根据开发中常见需求实现效果。
在列表页加载卡片的情况下,经常会在图片底部或者顶部添加一片半透明区域,用来显示文本信息,以前常规做法是设置一个半透明颜色,但是这种做法在暗灰色的图片上,展示效果极差。Google 提供 Palette 之后,就可以根据图片分析出最适合的底色作为背景色,让图片和整列表更加优雅的展示。
本文主要内容是 Palette,如果对 CardView 不熟悉的朋友可以查看前文:
1、颜色样本
创建完一个 Palette 实例之后,我们还需要得到一种采集的样本(swatch),有 6 中样本(swatch)
Palette.getVibrantSwatch()
返回一个鲜明(有活力)的样本类Palette.getDarkVibrantSwatch()
返回一个鲜明(有活力)的暗色调样本类Palette.getLightVibrantSwatch()
返回一个鲜明(有活力)的亮色调样本类Palette.getMutedSwatch()
返回一个柔和的样本类Palette.getDarkMutedSwatch()
返回一个柔和的暗色调样本类Palette.getLightMutedSwatch()
返回一个柔和的亮色调样本类
2、创建鲜明(有活力)的样本类
以上 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
异常。
3、设置颜色透明度
当我们获取到图片的整体的颜色 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);
}
4、加载网络图片
因为发现很多文章都是采用本地资源库图片,获取 BitMap 方式比较简单,在实际项目中,如果加载网络图片的话,可以使用 Glide.asBitMap()
方法来实现,这里使用了一个 Adapter 的三方库,防止朋友们看懵逼,贴上 Adapter 完整代码,如果想学习的小伙伴,建议在文末下载源码学习。
<?xml version="1.0" encoding="utf-8"?>
<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 提取图片色彩的操作是比较耗时的,所以一定要在子线程中执行。
加载方式有同步加载和异步加载两种:
1、同步加载
由于他们很可能会比较耗时(在分析大图片或者所需颜色较多时),所以它们不应该在主线程中执行。你应该先在别的线程中使用这两个函数进行解析,解析成功之后再使用。
2、异步加载
有时候你不会在加载图片的线程(非主线程)中使用解析出的颜色,所以 Palette 提供了异步方法,他们与之前的函数的区别就是需要传入 PaletteAsyncListener,提供在图片解析完成后的回调函数。
源码下载 源码包含 Material Design 系列控件集合,定时更新,敬请期待!
总结
现在很多流行的 APP 列表界面垂直滑动时,会根据内容色调动态更改 ToolBar 的颜色,这种效果就可以借助 Palette 来采取图片颜色实现。其实 Palette 并不只适用于我写的示例,Palette 应用的情况很多,感兴趣的朋友可以在官网详细学习!
我的微信:Jaynm888
欢迎点评,
诚邀 Android 程序员加入微信交流群
,公众号回复加群或者加我微信邀请入群。