需求背景分析:
产品和美工给了个根据专辑封面取主题色做背景,并且专辑封面还要融入背景的效果图,一开始看到取色觉得简单啊,不就是之前看过的palette嘛,可是专辑封面渐变消失融入背景怎么做呢,我们一步步分析。
1. 首先是背景取色,Palette
这个比较简单,因为已经有现成的API让我们调用
Palette.from(bitmap).generate(new Palette.PaletteAsyncListener() {
@Override
public void onGenerated(Palette palette) {
//todo
}
}
});
palette可以获取到6种颜色,而且palette还有其他功能,这里就不介绍了,网上有很多案例,我们继续。
因为背景色是渐变的,由深变浅,所以要这里取出两个颜色通过paint的shader绘制一张渐变效果的bitmap,具体代码如下
//......省略一些
Palette.from(resource).generate(new Palette.PaletteAsyncListener() {
@Override
public void onGenerated(Palette palette) {
//记得判空
if(palette==null)return;
//palette取色不一定取得到某些特定的颜色,这里通过取多种颜色来避免取不到颜色的情况
if (palette.getDarkVibrantColor(Color.TRANSPARENT) != Color.TRANSPARENT) {
createLinearGradientBitmap(palette.getDarkVibrantColor(Color.TRANSPARENT), palette.getVibrantColor(Color.TRANSPARENT));
} else if (palette.getDarkMutedColor(Color.TRANSPARENT) != Color.TRANSPARENT) {
createLinearGradientBitmap(palette.getDarkMutedColor(Color.TRANSPARENT), palette.getMutedColor(Color.TRANSPARENT));
} else {
createLinearGradientBitmap(palette.getLightMutedColor(Color.TRANSPARENT), palette.getLightVibrantColor(Color.TRANSPARENT));
}
}
});
//创建线性渐变背景色
private void createLinearGradientBitmap(int darkColor,int color) {
int bgColors[] = new int[2];
bgColors[0] = darkColor;
bgColors[1] = color;
if(bgBitmap==null){
bgBitmap= Bitmap.createBitmap(ivBg.getWidth(),ivBg.getHeight(), Bitmap.Config.ARGB_4444);
}
if(mCanvas==null){
mCanvas=new Canvas();
}
if(mPaint==null){
mPaint=new Paint();
}
mCanvas.setBitmap(bgBitmap);
mCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
LinearGradient gradient=new LinearGradient(0, 0, 0, bgBitmap.getHeight(),bgColors[0],bgColors[1], Shader.TileMode.CLAMP);
mPaint.setShader(gradient);
RectF rectF=new RectF(0,0,bgBitmap.getWidth(),bgBitmap.getHeight());
// mCanvas.drawRoundRect(rectF,16,16,mPaint); 这个用来绘制圆角的哈
mCanvas.drawRect(rectF,mPaint);
ivBg.setImageBitmap(bgBitmap);
}
先给大家看看目前效果,免得枯燥
ok,目前背景色就实现到这了
2. 接着是最蛋疼的让他渐变融入到背景色中了
我目前有两种思路(两种我都用过了,推荐第二种)
思路 | 优点 | 缺点 |
---|---|---|
在图片上面叠加一层颜色渐变的图片 | 方便简单,只需多加一个控件 | 需要获取旁边背景色对应的颜色值,这个比较难,而且我目前才用的算法获得有时候不准确 |
通过修改图片的透明度来达到渐变效果 | 无需计算旁边颜色,最终效果较佳 | 图片较大时,计算量比较大,速度较慢 |
ok,由于我最后还是采用了第二种,这里就只介绍第二种方法了,思路很简单,就是获取图片的bitmap数组,通过遍历来判断修改相应的透明度,具体代码如下:
//修改透明度
public static Bitmap getImageToChange(Bitmap mBitmap) {
Log.d(TAG,"with="+mBitmap.getWidth()+"--height="+mBitmap.getHeight());
Bitmap createBitmap = Bitmap.createBitmap(mBitmap.getWidth(), mBitmap.getHeight(), Bitmap.Config.ARGB_4444);
int mWidth = mBitmap.getWidth();
int mHeight = mBitmap.getHeight();
for (int i = 0; i < mHeight; i++) {
for (int j = 0; j < mWidth; j++) {
int color = mBitmap.getPixel(j, i);
int g = Color.green(color);
int r = Color.red(color);
int b = Color.blue(color);
int a = Color.alpha(color);
float index=i*1.0f/mHeight;
if(index>0.5f ){
float temp=i-mHeight/2.0f;
a= 255-(int) (temp/375*255);
}
color = Color.argb(a, r, g, b);
createBitmap.setPixel(j, i, color);
}
}
return createBitmap;
}
(ps:代码中采用的一些宽高和数值,要看具体效果来设置,这里仅做参考!)
2.1 增加一种修改透明度的方法
该算法使用位移和模运算来单独修改透明值,不用把ARGB的四个颜色都取出来,会比之前的速度快很多
//透明渐变
int[] argb=new int[ALBUM_SIZE*ALBUM_SIZE];
localBitmap.getPixels(argb, 0, localBitmap.getWidth(), 0, 0, localBitmap.getWidth(), localBitmap.getHeight());
//循环开始的下标,设置从什么时候开始改变
int start = argb.length / 2;
int mid = argb.length * 83 / 100;
int lines = ((mid - start) / localBitmap.getHeight()) + 2;
int width = localBitmap.getWidth();
for (int i = 0; i < lines; i++) {
for (int j = 0; j < width; j++) {
int index = start - width + i * width + j;
//由于默认图片透明色为0,所以要增加判断,不然后续处理的颜色会变为黑色
if(argb[index]!=0){
argb[index] = ((int) ((1 - ((float) i / lines)) * 255) << 24) | (argb[index] & 0x00FFFFFF);
}
}
}
for (int i = mid; i < argb.length; i++) {
argb[i] = (argb[i] & 0x00FFFFFF);
}
localBitmap = Bitmap.createBitmap(argb, localBitmap.getWidth(), localBitmap.getHeight(), Bitmap.Config.ARGB_8888);
(ps:算法中的数值也是仅供参考,具体还要看自己想要实现的效果,比如 <<24 和 0x00FFFFFF 是由于bitmap采用Config.ARGB_8888,大家根据自身需求修改就好,这里只提供思路)
2.2 适配图片透明处理
对于原图中带有透明的情况,获取像素点的时候透明的位置颜色值为0,所以在做透明渐变的时候要判断一下,避免后续颜色变成黑色
if(argb[index]!=0){
argb[index] = ((int) ((1 - ((float) i / lines)) * 255) << 24) | (argb[index] & 0x00FFFFFF);
}
最终效果如下:
总结
本次内容不难,重要是实现思路,我一开始采用第一种叠加图片的方法,寻找计算线性渐变某一位置的值,后来找到了但最终效果不如上述的第二种方法,但是我发现第二种方法在计算比较大的Bitmap的时候速度是真很慢的(遍历一个Bitmap数组,脑壳痛),所以具体还是看大家的需求吧
(ps:好久没写过文章了啊,自今年毕业来就一直忙东忙西,终于有点时间写东西了 TAT)