高级UI汇总
##一、Xfermode
设置或清除传输模式对象。传输模式定义了如何进行源像素(由绘图命令生成)与目标像素(渲染目标内容)。
通俗的说就是将绘制的图形的像素和Canvas上对应位置的像素按照一定的规则进行混合,形成新的像素,再更新到Canvas中形成最终的图形
##二 使用效果:
使用方法:Paint.setXfermode
我们一个像素的颜色都是由四个分量组成,即ARGB,A表示的是我们Alpha值,RGB表示的是颜色;
SRC [Sa, Sc] 表示的是原像素,原像素的值表示[Sa,Sc] Sa表示的就是源像素的Alpha值,Sc表示源像素的颜色值;
DST [ Da, Dc] 表示的是目标像素,目标像素的值表示[Da,Dc] Da表示的就是目标像素的Alpha值
SRC类:包含蓝色矩形的整个区域
DST类:包含黄色圆形的整个区域
-
ADD:
饱和相加,对图像饱和度进行相加,不常用
Saturate(S + D) -
CLEAR:
清除图像
[0, 0] -
DARKEN:
变暗,较深的颜色覆盖较浅的颜色,若两者深浅程度相同则混合
[Sa + Da - SaDa, Sc(1 - Da) + Dc*(1 - Sa) + min(Sc, Dc)] -
DST:
只显示目标图像
[Da, Dc] -
DST_ATOP:
在源图像和目标图像相交的地方绘制【目标图像】,在不相交的地方绘制【源图像】,相交处的效果受到源图像和目标图像alpha的影响
[Sa, Sa * Dc + Sc * (1 - Da)] -
DST_IN:
只在源图像和目标图像相交的地方绘制【目标图像】,绘制效果受到源图像对应地方透明度影响
[Sa * Da, Sa * Dc] -
DST_OUT:
只在源图像和目标图像不相交的地方绘制【目标图像】,在相交的地方根据源图像的alpha进行过滤,源图像完全不透明则完全过滤,完全透明则不过滤
[Da * (1 - Sa), Dc * (1 - Sa)] -
DST_OVER:
将目标图像放在源图像上方
[Sa + (1 - Sa)*Da, Rc = Dc + (1 - Da)*Sc] -
LIGHTEN:
变亮,与DARKEN相反,DARKEN和LIGHTEN生成的图像结果与Android对颜色值深浅的定义有关
[Sa + Da - SaDa, Sc(1 - Da) + Dc*(1 - Sa) + max(Sc, Dc)] -
MULTIPLY:
正片叠底,源图像素颜色值乘以目标图像素颜色值除以255得到混合后图像像素颜色值
[Sa * Da, Sc * Dc] -
OVERLAY:
叠加
( alpha_{out} = alpha_{src} + alpha_{dst} - alpha_{src} * alpha_{dst} )
(C_{out} = begin{cases} 2 * C_{src} * C_{dst} & 2 * C_{dst} lt alpha_{dst} * alpha_{src} * alpha_{dst} - 2 ( alpha_{dst} - C_{src}) ( alpha_{src} - C_{dst}) & otherwise end{cases}) -
SCREEN:
滤色,色调均和,保留两个图层中较白的部分,较暗的部分被遮盖
[Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] -
SRC:
只显示源图像
[Sa, Sc] -
SRC_ATOP:
在源图像和目标图像相交的地方绘制【源图像】,在不相交的地方绘制【目标图像】,相交处的效果受到源图像和目标图像alpha的影响
[Da, Sc * Da + (1 - Sa) * Dc] -
SRC_IN:
只在源图像和目标图像相交的地方绘制【源图像】
[Sa * Da, Sc * Da] -
SRC_OUT:
只在源图像和目标图像不相交的地方绘制【源图像】,相交的地方根据目标图像的对应地方的alpha进行过滤,目标图像完全不透明则完全过滤,完全透明则不过滤
[Sa * (1 - Da), Sc * (1 - Da)] -
SRC_OVER:
将源图像放在目标图像上方
[Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] -
XOR:
在源图像和目标图像相交的地方之外绘制它们,在相交的地方受到对应alpha和色值影响,如果完全不透明则相交处完全不绘制
[Sa + Da - 2 * Sa * Da, Sc * (1 - Da) + (1 - Sa) * Dc]
##三、效果图代码
/**
- Xfermode
- 注意:
- 1. Src和DST的 大小最好一致(先创建一个Bitmap,再在Bitmap中设置内容,从而分别形成Src和Dist,要有src和dst重合然后绘制的概念)
- 2. canvas 要saveLayer
- 3. 有些效果要取消硬件加速
*/
public class XfermodeActivity extends AppCompatActivity {
// create a bitmap with a circle, used for the "dst" image
static Bitmap makeDst(int w, int h) {
Bitmap bm = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(bm);
Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
p.setColor(0xFFFFCC44);
c.drawOval(new RectF(0, 0, w * 3 / 4, h * 3 / 4), p);
return bm;
}
// create a bitmap with a rect, used for the "src" image
static Bitmap makeSrc(int w, int h) {
Bitmap bm = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(bm);
Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
p.setColor(0xFF66AAFF);
c.drawRect(w / 3, h / 3, w * 19 / 20, h * 19 / 20, p);
return bm;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new SampleView(this));
}
private static class SampleView extends View {
private static final int W = 250;
private static final int H = 250;
private static final int ROW_MAX = 4; // number of samples per row
private Bitmap mSrcB;
private Bitmap mDstB;
private Shader mBG; // background checker-board pattern
private static final Xfermode[] sModes = {
new PorterDuffXfermode(PorterDuff.Mode.CLEAR),
new PorterDuffXfermode(PorterDuff.Mode.SRC),
new PorterDuffXfermode(PorterDuff.Mode.DST),
new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER),
new PorterDuffXfermode(PorterDuff.Mode.DST_OVER),
new PorterDuffXfermode(PorterDuff.Mode.SRC_IN),
new PorterDuffXfermode(PorterDuff.Mode.DST_IN),
new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT),
new PorterDuffXfermode(PorterDuff.Mode.DST_OUT),
new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP),
new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP),
new PorterDuffXfermode(PorterDuff.Mode.XOR),
new PorterDuffXfermode(PorterDuff.Mode.DARKEN),
new PorterDuffXfermode(PorterDuff.Mode.LIGHTEN),
new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY),
new PorterDuffXfermode(PorterDuff.Mode.SCREEN)
};
private static final String[] sLabels = {
"Clear", "Src", "Dst", "SrcOver",
"DstOver", "SrcIn", "DstIn", "SrcOut",
"DstOut", "SrcATop", "DstATop", "Xor",
"Darken", "Lighten", "Multiply", "Screen"
};
public SampleView(Context context) {
super(context);
mSrcB = makeSrc(W, H);
mDstB = makeDst(W, H);
// make a ckeckerboard pattern
Bitmap bm = Bitmap.createBitmap(new int[]{0xFFFFFFFF, 0xFFCCCCCC,
0xFFCCCCCC, 0xFFFFFFFF}, 2, 2,
Bitmap.Config.RGB_565);
mBG = new BitmapShader(bm,
Shader.TileMode.REPEAT,
Shader.TileMode.REPEAT);
Matrix m = new Matrix();
m.setScale(6, 6);
mBG.setLocalMatrix(m);
}
@Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.WHITE);
Paint labelP = new Paint(Paint.ANTI_ALIAS_FLAG);
labelP.setTextAlign(Paint.Align.CENTER);
Paint paint = new Paint();
paint.setFilterBitmap(false);
canvas.translate(15, 35);
int x = 0;
int y = 0;
for (int i = 0; i < sModes.length; i++) {
// draw the border
paint.setStyle(Paint.Style.STROKE);
paint.setShader(null);
canvas.drawRect(x - 0.5f, y - 0.5f,
x + W + 0.5f, y + H + 0.5f, paint);
// draw the checker-board pattern
paint.setStyle(Paint.Style.FILL);
paint.setShader(mBG);
canvas.drawRect(x, y, x + W, y + H, paint);
// draw the src/dst example into our offscreen bitmap
@SuppressLint("WrongConstant") int sc = canvas.saveLayer(x, y, x + W, y + H, null,
Canvas.MATRIX_SAVE_FLAG |
Canvas.CLIP_SAVE_FLAG |
Canvas.HAS_ALPHA_LAYER_SAVE_FLAG |
Canvas.FULL_COLOR_LAYER_SAVE_FLAG |
Canvas.CLIP_TO_LAYER_SAVE_FLAG);
canvas.translate(x, y);
canvas.drawBitmap(mDstB, 0, 0, paint);
paint.setXfermode(sModes[i]);
canvas.drawBitmap(mSrcB, 0, 0, paint);
paint.setXfermode(null);
canvas.restoreToCount(sc);
// draw the label
labelP.setAntiAlias(true);
labelP.setTextSize(24);
canvas.drawText(sLabels[i],
x + W / 2, y - labelP.getTextSize() / 2, labelP);
x += W + 10;
// wrap around when we've drawn enough for one row
if ((i % ROW_MAX) == ROW_MAX - 1) {
x = 0;
y += H + 60;
}
}
}
}
}
##四 基本使用
- 保存layer
int layerId = canvas.saveLayer(0, 0, getWidth(), getHeight(), null, Canvas.ALL_SAVE_FLAG);
- 先画目标图Dst
canvas.drawBitmap(BmpSRC,0,0,mBitPaint);
- 设置xfermode
mBitPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
- 画源图Src
canvas.drawBitmap(BmpRevert,0,0,mBitPaint);
- 清空xfermode
mBitPaint.setXfermode(null);
- canvas恢复到layerId
canvas.restoreToCount(layerId);
##五 注意事项
- 需要设置layer然后,在绘制,最终canvas回退到保存的layerId
- 示例中绘制的是Bitmap,而且绘制的区域是重合的,注意SrcBitmap和DstBitmap的宽高,不仅仅是圆形区域或者矩形区域,实际如果绘制的是path的效果时,结果可能和示例中不一样
- xfermode的效果炫酷,代码简单,但是效果转换成代码困难,可以这样理解:我们先在canvas创建一个layer;layer画效果如圆角、半透明效果;再画实际我们要的内容,两个内容会合并到一起去;canvas回去,paint清空Xfermode
##六 实例
倒影图片
圆角图片
橡皮擦效果 (刮刮卡效果)
心电图效果( 目标图片 —心电图、源图片 ---- 不透明的图 就是通过改变透明图片的不透明区域的宽度,来实现心电图的动画效果)
不规则水波纹效果,当然也可以做SRC_IN 的效果(注意选择谁为源图片,谁为目标图片)