产品给我推了这么一个图:
要我根据选择的色板的颜色改变画笔图标的颜色,然后给我预留了这么一堆图标:
然后我感到密集恐惧症爆发,而且表示不想加这么多Logo进去应用里面增肥。所以我想了大概1分钟,觉得既然Logo都是一样的轮廓只是颜色不一样,那么我可以把白色的Logo拿出来,分别按比例削弱或者增强每个像素的透明度、红绿蓝属性即可动态地使View里面的图标颜色从初始化的白色变化为任意的颜色。
原来就是通过位运算,把每个像素的ARGB 4个字节拆分,分别乘以不同的百分比,从而使某些颜色相对削弱和相对增强,重新混合后就是不同的颜色了,例如0xFFFFFFFF,第二个字节的FF乘以0.5,最后两个字节乘以0,就可以得到暗红色;RGB全部乘以0,则得到黑色。
具体实现代码可以看我的自定义控件Demo类,图片素材可以自己找其他其他白色图片:
package test.graphics;
import android.annotation.Nullable;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.View;
import java.nio.IntBuffer;
import cjz.project.maptry.R;
/**
* Created by cjz on 2019/8/8.
*/
public class PenButton extends View {
private int color = Color.WHITE;
private int id = R.drawable.pen_first_kind_philips;
public PenButton(Context context) {
super(context);
}
public PenButton(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public PenButton(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
/**设置颜色**/
public void setColor(int color){
this.color = color;
invalidate();
}
public void setDrawableID(int id){
this.id = id;
invalidate();
}
@Override
protected void onDraw(Canvas canvas) {
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), id);
int pixels[] = new int[bitmap.getHeight() * bitmap.getWidth()];
//复制像素出来
bitmap.copyPixelsToBuffer(IntBuffer.wrap(pixels));
//计算颜色通道
int color = (this.color & 0xFF00FF00 //alpha和green排列一致直接或运算取值
| this.color >> 16 & 0x000000FF //blue位移16bit到最后一字节,或运算保存到最后一字节
| this.color << 16 & 0x00FF0000); //red位移16bit到第二个字节,或运算保存到第二个字节
float alpha = ((color >> 24) & 0xFF) / 255f;
float rChannelAlpha = ((color >> 16) & 0xFF) / 255f * alpha;
float gChannelAlpha = ((color >> 8) & 0xFF) / 255f * alpha;
float bChannelAlpha = ((color) & 0xFF) / 255f * alpha;
//对图像进行单一颜色通道过滤(其实就是按color的颜色通道按比例决定保留哪种颜色,还有保留多少比例)
for(int i = 0; i < pixels.length; i ++){
int newA = (int)(((pixels[i] >> 24) & 0xFF) * alpha);
int newR = (int)(((pixels[i] >> 16) & 0xFF) * rChannelAlpha);
int newG = (int)(((pixels[i] >> 8) & 0xFF) * gChannelAlpha);
int newB = (int)(((pixels[i]) & 0xFF) * bChannelAlpha);
pixels[i] = 0;
//载入新颜色
pixels[i] |= (newA << 24);
pixels[i] |= (newR << 16);
pixels[i] |= (newG << 8);
pixels[i] |= newB;
}
bitmap.copyPixelsFromBuffer(IntBuffer.wrap(pixels));
canvas.drawBitmap(bitmap, new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()),new Rect(0, 0, getWidth(), getHeight()), null);
super.onDraw(canvas);
}
}
布局文件:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="@mipmap/ic_launcher">
<test.graphics.PenButton
android:id="@+id/pb_test_button"
android:layout_width="50dp"
android:layout_height="200dp"
android:layout_gravity="center_horizontal"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#FFFFFFFF"
android:text="透明度"/>
<SeekBar
android:id="@+id/alpha_channel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:progress="255"
android:max="255"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#FFFFFFFF"
android:text="红"/>
<SeekBar
android:id="@+id/r_channel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:progress="255"
android:max="255"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#FFFFFFFF"
android:text="绿"/>
<SeekBar
android:id="@+id/g_channel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:progress="255"
android:max="255"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#FFFFFFFF"
android:text="蓝"/>
<SeekBar
android:id="@+id/b_channel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:progress="255"
android:max="255"/>
</LinearLayout>
MainActivity:
其中诸如color ^= 0xFF000000 & color; 这类代码,实际上是先通过与运算只保留要消除的像素颜色分量, 再通过异或运算抵消color变量原本该分量的所有1,达到清空该分量颜色的目的。
package test.graphics;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.widget.SeekBar;
import cjz.project.maptry.R;
/**
* Created by cjz on 2019/8/8.
*/
public class MainActivity extends Activity{
private PenButton pb_test_button;
private SeekBar alpha_channel;
private SeekBar r_channel;
private SeekBar g_channel;
private SeekBar b_channel;
private int color = 0xFFFFFFFF;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.test_change_color_button);
pb_test_button = findViewById(R.id.pb_test_button);
alpha_channel = findViewById(R.id.alpha_channel);
r_channel = findViewById(R.id.r_channel);
g_channel = findViewById(R.id.g_channel);
b_channel = findViewById(R.id.b_channel);
SeekBar.OnSeekBarChangeListener onSeekBarChangeListener = new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (seekBar.getId() == alpha_channel.getId()){
color ^= 0xFF000000 & color; //清空透明度
color |= ((progress & 0xFF) << 24); //给新的透明度
} else if (seekBar.getId() == r_channel.getId()) {
color ^= 0x00FF0000 & color;
color |= ((progress & 0xFF) << 16);
} else if (seekBar.getId() == g_channel.getId()) {
color ^= 0x0000FF00 & color;
color |= ((progress & 0xFF) << 8);
} else if (seekBar.getId() == b_channel.getId()) {
color ^= 0x000000FF & color;
color |= progress & 0xFF ;
}
Log.i("progress", "" + progress);
Log.i("color", String.format("0x%8X", color));
pb_test_button.setColor(color);
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
};
alpha_channel.setOnSeekBarChangeListener(onSeekBarChangeListener);
r_channel.setOnSeekBarChangeListener(onSeekBarChangeListener);
g_channel.setOnSeekBarChangeListener(onSeekBarChangeListener);
b_channel.setOnSeekBarChangeListener(onSeekBarChangeListener);
}
}
实现效果
然后因为我自己写这个东西比查资料来得更快,所以完成任务之后查了一下其实会不会安卓SDK自身就能做到这个事情,发现其实是可以的,方法我直接粘贴别人的文章好了: