Paint意思就是画笔,保存了绘制几何图形,文本,位图的样式和颜色信息。在android的API中Paint类里面存在大量的setter和getter方法,用来获取或者设置我们上面所说的样式和颜色信息。
画笔常用的API 和使用方式
private void init(){
mPaint = new Paint();
mPaint.setColor(Color.RED); //设置画笔的颜色
mPaint.setAntiAlias(true); //设置画笔是否抗锯齿
mPaint.setARGB(1,255,255,255); //设置颜色 用argb 模式 a表示透明度
mPaint.setStyle(Paint.Style.FILL); //设置画笔描边的样式 fill 表示填充 STROKE 表示空心
mPaint.setStrokeWidth(4); //设置描边的宽度
mPaint.setTextAlign(Paint.Align.LEFT); //设置文本的放置方式
mPaint.setUnderlineText(true); //设置下划线
mPaint.setTextScaleX(2);// 设置文本缩放倍数
mPaint.setStrokeCap(Paint.Cap.ROUND); //设置描边结束除的圆角效果
mPaint.setStrokeJoin(Paint.Join.ROUND); //设置画笔连接处的效果
mPaint.setShader(new SweepGradient(200, 200, Color.BLUE, Color.RED)); //设置环形渲染器
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DARKEN)); //设置图层混合模式
mPaint.setColorFilter(new LightingColorFilter(0x00ffff, 0x000000)); //设置颜色过滤器
mPaint.setFilterBitmap(true); //设置双线性过滤
mPaint.setMaskFilter(new BlurMaskFilter(10, BlurMaskFilter.Blur.NORMAL));//设置画笔遮罩滤镜 ,传入度数和样式
}
上面其中的一些API都很明了他们的意思,但是有些API 不是那么好理解和不同的地方:
1.paint.setStrokeWidth此方法是设置画笔的宽度,但是这里的看度不是我们字面理解的宽度,网上借一张图表示:
其实我们画一个圆的时候,如果设置了画笔的宽度,那么最终得到的圆的半径就是我们调用drawCircle设置的值应该是上图原始圆的半径。
private void init() {
mPaint = new Paint();
mPaint.setColor(Color.RED);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(80);
mRectPaint = new Paint();
mRectPaint.setStyle(Paint.Style.STROKE);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
RectF rect0 = new RectF(100, 100, 500, 500);
canvas.drawRect(rect0, mRectPaint);
canvas.drawArc(rect0, 0, 90, false, mPaint);
}
上面代码画了一个矩形,和以矩形位边界,画了一段弧:
看到最终的效果是我们说话的弧形,并没有内切于正方形,而是有一半的裸露在外面了要想实现内切弧形就得使用到上面strokeWidth的计算方式:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
RectF rect0 = new RectF(100, 100, 500, 500);
canvas.drawRect(rect0, mRectPaint);
canvas.drawArc(rect0, 0, 90, false, mPaint);
mPaint.setColor(Color.GREEN);
// 80为画笔的宽度
//RectF rect1 = new RectF(140, 140, 460, 460);
RectF rect1 = new RectF(100+(80/2), 100+(80/2), 500-(80/2), 500-(80/2),);
canvas.drawArc(rect1, 0, -90, false, mPaint);
canvas.drawCircle(300,300,160,mRectPaint);
}
其实最后计算的内切弧形的半径位我们如图所示的圆的半径。
2.Paint.Cap 画笔的线冒样式 这个枚举类有3个值,BUTT(0),ROUND(1),SQUARE(2),分为为“平头”,“圆头”,“方头”,默认为BUTT
虚线左边是实际长度,虚线的右边是"线头"
3.Paint.Join.试着画笔连接处的状态由三种状态MITER(0),ROUNT(1),BEVEL(2),分别代表"尖角","圆角"和"平角",可以用下图直观的描述:
4.paint.setMaskFilter。MaskFilter翻译过来叫遮罩滤镜,它可以为Paint边缘的alpha通道应用转换。目前有两个子类:BlurMaskFilter 和 EmbossMaskFilter。它们分别可实现出模糊效果和浮雕效果:
BlurMaskFilter:模糊遮罩滤镜 改变图像的透明度值来实现的
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(Color.RED);
//关闭硬件加速 才会有效果
setLayerType(View.LAYER_TYPE_SOFTWARE, paint);
paint.setMaskFilter(new BlurMaskFilter(30, BlurMaskFilter.Blur.NORMAL));
canvas.drawRect(100, 100, 250, 250, paint);
paint.setMaskFilter(new BlurMaskFilter(30, BlurMaskFilter.Blur.SOLID));
canvas.drawRect(100, 350, 250, 500, paint);
paint.setMaskFilter(new BlurMaskFilter(30, BlurMaskFilter.Blur.OUTER));
canvas.drawRect(100, 600, 250, 750, paint);
paint.setMaskFilter(new BlurMaskFilter(30, BlurMaskFilter.Blur.INNER));
canvas.drawRect(100, 850, 250, 1000, paint);
}
public BlurMaskFilter(float radius, Blur style) {
native_instance = nativeConstructor(radius, style.native_int);
}
float radius :此参数是代表阴影的半径
Blur style :代表阴影的样式 有四个值
BlurMaskFilter.Blur.SOLID:图像边界外产生一层与图像颜色一致阴影效果
BlurMaskFilter.Blur.NORMAL:整个图像都被模糊掉
BlurMaskFilter.Blur.OUTER:图像边界外产生一层阴影,并且将图像变成透明效果
BlurMaskFilter.Blur.INNER:在图像内部边沿产生模糊效果
5.paint.setShader。设置着色器,在Android的API中着色器主要有Shader的以下几个子类来实现,LinearGradient(线性着色器),
SweepGradient(扫描着色器),RadialGradient(辐射着色器),BitmapShader(位图着色器),ComposeShader(组合着色器)。
LinearGradient
线性着色器,顾名思义是线性渐变的的着色器。先看其API:有2个构造函数,但是最终都是调用下面的构造函数
//LinearGradient的构造函数
public LinearGradient(float x0, float y0, float x1, float y1, @ColorInt int colors[],
float positions[], TileMode tile) {
}
上面构造函数参数的意义:
float x0,线性着色器的起始x坐标值为 x0;
float y0, 线性着色器的起始y坐标值y0;
float x1,线性着色器的终止位置的x坐标值x1;
float y1,线性着色器的终止位置的y坐标值y1;
int colors[ ] ,颜色值的数组,从起始位置到终止位置的不同的颜色值。
float positions[],位置数组,分别代表每个颜色值代表的比率(下面详细解释)(值可以为null)
TileMode tile ,Shader的不同的类型。有三个值,分别为CLAMP,REPEAT,MIRROR(下面解释)
看一个例子自定义View很简单的例子(只写了onDraw方法):
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
LinearGradient gradient = new LinearGradient(200,200,700,700,new int[]{Color.RED,Color.BLUE},null,Shader.TileMode.CLAMP);
mPaint.setShader(gradient);
canvas.drawRect(200,200,700,700,mPaint);
}
运行出来的效果为下图(可能自己截图,缩放的原因,有点歪)
按照上面构造函数的每个变量的意思是,在坐标点(200,200)到(700,700)之间,以RED为起始色,以BLUE为中之色,TileMode为CLAMP,画一个矩形,如上图的效果。
//红绿蓝的效果源代码 int position[]的值为null
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
LinearGradient gradient = new LinearGradient(200,200,700,700,new int[]{Color.RED,Color.GREEN,Color.BLUE},null,Shader.TileMode.CLAMP);
mPaint.setShader(gradient);
canvas.drawRect(200,200,700,700,mPaint);
}
// int postion[]数组的值为 {0,0.2f,1}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
LinearGradient gradient = new LinearGradient(200,200,700,700,new int[]{Color.RED,Color.GREEN,Color.BLUE},new float[]{0,0.2f,1},Shader.TileMode.CLAMP);
mPaint.setShader(gradient);
canvas.drawRect(200,200,700,700,mPaint);
}
上面区别只是一个里面的pisition[]数组值为null,另外一个数组值不为null。
上图中,左边的图就是LinearGradient构造函数position值为null的默认显示,有图为{0,0.2f,1}的显示效果,可以大胆的推测,int pisition[ ]的值代表的就是int color[ ] 数组各个颜色值所占的比率。大致意思就是0-0.2f内是RED到GREEN的渐变,0.2f-1是GREEN到BLUE的渐变。而且还有一个细节就是int position[ ]数组只存在2中情况,一种是为null,另外一种必须和int color[ ] 数组长度值一致,否则在运行的时候会bao
SweepGradient
扫描着色器,类似扫描效果,直接看源码和图
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
SweepGradient gradient = new SweepGradient(450,450,Color.RED,Color.GREEN);
mPaint.setShader(gradient);
canvas.drawRect(200,200,700,700,mPaint);
}
效果图:
public SweepGradient(float cx, float cy, @ColorInt int color0, @ColorInt int color1) {
}
cx,cy是扫描中心起始点的坐标。color0和color1的颜色分别代表起始颜色和终止颜色。按照顺时针旋转。
RadialGradient(辐射着色器)
辐射着色器就是从中间向两边扩散:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
RadialGradient gradient = new RadialGradient(400,400,200,Color.RED,Color.BLUE,Shader.TileMode.CLAMP);
mPaint.setShader(gradient);
canvas.drawCircle(400,400,200,mPaint);
}
效果如下:
BitmapShader(位图着色器)
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
BitmapShader shader = new BitmapShader(mBitmap,Shader.TileMode.REPEAT,Shader.TileMode.MIRROR);
mPaint.setShader(shader);
canvas.drawRect(0,0,600,600,mPaint);
}
效果图:
ComposeShader(组合着色器)
混合着色器,是由多个着色器共同组成,包括2个着色器:
//构造函数,着色器分别为shaderA shaderB
// 模式
public ComposeShader(@NonNull Shader shaderA, @NonNull Shader shaderB,
@NonNull PorterDuff.Mode mode) {
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
BitmapShader bitmapShader = new BitmapShader(mBitmap, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
LinearGradient linearGradient = new LinearGradient(0, 0, 1000, 1600, new int[]{Color.RED, Color.GREEN, Color.BLUE}, null, Shader.TileMode.CLAMP);
ComposeShader mShader = new ComposeShader(bitmapShader, linearGradient, PorterDuff.Mode.MULTIPLY);
mPaint.setShader(mShader);
canvas.drawCircle(250, 250, 250, mPaint);
}
BitmapShader和LinearGradient的组合效果。
TileMode
TitleMode这个字面意思,就是断点范围之外的着色规则。有三个值CLAMP(0),REPEAT(1),MIRROR(2)。
CLAMP。从效果图或者API字面的意思就是拉伸最后一个像素点
REPEATE。意思就是像素重复
MIRROR。像素镜像显示。
上面的三种模式的显示,都是画布上所画的区域,都是在自身大小比当前区域小的时候,才会以这种模式去补足剩余的像素
//TileMode.CLAMP
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
BitmapShader shader = new BitmapShader(mBitmap,Shader.TileMode.CLAMP,Shader.TileMode.CLAMP);
mPaint.setShader(shader);
canvas.drawRect(0, 0, 700,700, mPaint);
}
//TileMode.REPEAT
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
BitmapShader shader = new BitmapShader(mBitmap,Shader.TileMode.REPEAT,Shader.TileMode.REPEAT);
mPaint.setShader(shader);
canvas.drawRect(0, 0, 700,700, mPaint);
}
//TileMode.MIRROR
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
BitmapShader shader = new BitmapShader(mBitmap,Shader.TileMode.MIRROR,Shader.TileMode.MIRROR);
mPaint.setShader(shader);
canvas.drawRect(0, 0, 700,700, mPaint);
}
原图
原图为上图 左边第一张图 ,原图大小为164*164但是我们在drawRect时候传递的矩形为700*700比原图要打,所以需要进行末尾的像素填充,2图为CLAMP模式,就是将最后一个像素点无限复制,直至达到700*700,3图为REPEATE模式,就是将位图进行复制以达到700*700,4图为MIRROR模式,也即镜像模式,和3图的区别就是图像颠倒了
PorterDuff.Mode
在上面写ComposeShader的时候,将BitmapShader和LinearGradient镜像组合使用的时候,使用了属性PorterDuff.Mode.MULTIPLY,使得线性的颜色值和底部的图片同时存在。PorterDuff.Mode
是用来指定两个图像共同绘制时的颜色策略的。它是一个 enum,不同的 Mode
可以指定不同的策略。「颜色策略」的意思,就是说把源图像绘制到目标图像处时应该怎样确定二者结合后的颜色。官方文档介绍在这里。它将所绘制图形的像素与Canvas中对应位置的像素按照一定的规则进行混合形成新的像素值,从而更新Canvas中最终的像素颜色值。
借用google官方文档上面的图来解释着每一种模式的合成的结果,原图像为蓝色正方形,目标图像为红色的圆形
源图像 目标图像
PorterDuff.Mode.SRC 显示上层绘制的图像(也就是谁后绘制,就显示谁
PorterDuff.Mode.SRC_OVER 正常绘制图层,上下图层绘制叠盖
PorterDuff.Mode.SRC_IN 取两个图像的交集并交集显示为上层图像
PorterDuff.Mode.SRC_ATOP 取交集部分的上层,和下层未交集的部分
PorterDuff.Mode.DST 显示下层绘制的图像
PorterDuff.Mode.DST_OVER 上下层都显示,下层居上显示
PorterDuff.Mode.DST_IN 取上下交集的部分 并显示交集部分的下层
PorterDuff.Mode.DST_ATOP 取下层交集部分和上层未交集部分
PorterDuff.Mode.CLEAR 所有的绘制不会提交到画布上
PorterDuff.Mode.SRC_OUT 取上层并且不属于交集的部分,交集部分变透明
PorterDuff.Mode.DST_OUT 取下层非交集部分,交集部分变透明
PorterDuff.Mode.XOR 取上下层非交集的部分 ,并且交集部分变透明
PorterDuff.Mode.DARKEN 取两图层全部区域,交集部分颜色变深
PorterDuff.Mode.LIGTHEN 取两图层全部区域,交集部分颜色点亮
PorterDuff.Mode.MULTIPLY 取两层交集部分,颜色叠加
PorterDuff.Mode.SCREEN 取两层全部区域,交集部分滤色
PorterDuff.Mode.OVERLAY 取两层的全部区域,交集部分叠加
//上面图中的所有模式原图像sourceImage代表上层图像(后绘制的图像)
//下层图像为destinationImage为下层图像(后绘制的图像)
Paint paint = new Paint();
canvas.drawBitmap(destinationImage, 0, 0, paint);
PorterDuff.Mode mode = // choose a mode
paint.setXfermode(new PorterDuffXfermode(mode));
canvas.drawBitmap(sourceImage, 0, 0, paint);
滤镜效果
在Paint的Api中有setColorFilter的一个函数,设置滤镜效果,平时像照相机的胶片效果,各种图片处理软件(美图,PS)都是这样处理来完成的。
public ColorFilter setColorFilter(ColorFilter filter) {
// If mColorFilter changes, cached value of native shader aren't valid, since
// old shader's pointer may be reused by another shader allocation later
if (mColorFilter != filter) {
mNativeColorFilter = -1;
}
// Defer setting the filter natively until getNativeInstance() is called
mColorFilter = filter;
return filter;
}
常用的滤镜效果类有一下几个:LightingColorFilter,ColorMatrixColorFilter,PorterDuffColorFilter。下面分别讲解
LightingColorFilter
主要用来模拟简单的光照效果。构造函数如下
public LightingColorFilter(@ColorInt int mul, @ColorInt int add) {
mMul = mul;
mAdd = add;
}
参数mul和add都是和颜色值格式相同的int值,其中mul用来目标像素"相乘",add用来和目标像素"相加":
R' = R* mul.R/0xff + add.R
G' = G* mul.G/0xff + add.G
B' = B* mul.B/0xff + add.B
一个保持原样的LightingColorFilter的构造函数的参数值为mul = 0xffffff,add = 0x000000
R' = R* mul.R/0xff + add.R // R' = R
G' = G* mul.G/0xff + add.G // G' = G
B' = B* mul.B/0xff + add.B //B' = B
看下面两幅图片效果(科比镇楼):
//不添加任何LightColorFilter时候的效果
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawBitmap(mBitmap,0,0,mPaint);
}
//添加LightingColorFilter的效果
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
LightingColorFilter colorFilter = new LightingColorFilter(0x00ffff,0x000000);
mPaint.setColorFilter(colorFilter);
canvas.drawBitmap(mBitmap,0,0,mPaint);
}
上面有图中是使用了mlu = 0xffffff add = 0x000000之后的效果,根据上面的公式,可以看出,使用这个LightingColorFilter之后,其实就是去除了图片当中的红色效果。
R' = R* 0x00+ 0x00 // R' = 0x00
G' = G* 0xff + 0x00 // G' = G
B' = B* 0xff+ 0x00 //B' = B
如果想让原图的红色更亮 就可以在代码中设置add的值:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
LightingColorFilter colorFilter = new LightingColorFilter(0xffffff,0x660000);
mPaint.setColorFilter(colorFilter);
canvas.drawBitmap(mBitmap,0,0,mPaint);
}
原图中的红色这种色值被增强了,感觉红色更亮了,有点像张飞了(哈哈)
ColorMatrixColorFilter
ColorMatrixColorFilter是通过颜色矩阵来改变图层的颜色值的,构造方法为,也可以传入一个颜色数组,但是数组大小必须是>=20的,取钱20个数组元素,组成颜色矩阵:
public ColorMatrixColorFilter(@NonNull ColorMatrix matrix) {
}
public ColorMatrixColorFilter(@NonNull float[] array) {
if (array.length < 20) {
throw new ArrayIndexOutOfBoundsException();
}
mMatrix.set(array);
}
需要传递一个ColorMatrix的颜色矩阵的一般计算公式,假设有颜色矩阵:
//与[R,G,B,A,1]相乘 4*5 5*1
float [] colorMatrix = {
a,b,c,d,e, //red
f,g,h,i,j, //greed
k,l,m,n,o, //blue
p,q,r,s,t //alpha
};
例如设置了上面一个颜色矩阵之后,计算方式应该为
R' = R*a+G*b+B*c+A*d+e
G' = R*f+G*g+B*h+A*i+j
B' = R*k+G*l+B*m+A*n+o
A' = R*p+G*q+B*r+A*s+t
ColorMatrixColorFilter中颜色矩阵第一行决定颜色值中的一般计算公式就如上面所述,计算的新的R',G',B',A'。可以通过修改矩阵中不同的值得到不同的效果
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
/*PorterDuffColorFilter filter = new PorterDuffColorFilter(Color.RED,PorterDuff.Mode.DARKEN);
mPaint.setColorFilter(filter);
canvas.drawBitmap(mSrcBitmap,0,0,mPaint);*/
float [] colorMatrix = {
3,0,0,0,0, //red
0,1,0,0,0, //greed
0,0,1,0,0, //blue
0,0,0,1,0 //alpha
};
ColorMatrixColorFilter filter = new ColorMatrixColorFilter(colorMatrix);
mPaint.setColorFilter(filter);
canvas.drawBitmap(mSrcBitmap,0,0,mPaint);
}
上面的代码可以实现下面的效果,增加图层中的红色
上面看大,传递ColorMatrixColorFilter中的参数位一个颜色数组,我们也可以传入一个ColorMatrix,调用ColorMatrix的一些比较好用的方法。
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
ColorMatrix colorMatrix = new ColorMatrix();
colorMatrix.setScale(1,2,1,1); //增强绿色
ColorMatrixColorFilter filter = new ColorMatrixColorFilter(colorMatrix);
mPaint.setColorFilter(filter);
canvas.drawBitmap(mSrcBitmap,0,0,mPaint);
}
//其实就是改变了颜色矩阵中绿色的值
public void setScale(float rScale, float gScale, float bScale,
float aScale) {
final float[] a = mArray;
for (int i = 19; i > 0; --i) {
a[i] = 0;
}
a[0] = rScale;
a[6] = gScale;
a[12] = bScale;
a[18] = aScale;
}
效果如下:
ColorMatrix还有一个方法setSaturation(float sat)。设置图像的饱和度, sat = 0 表示没有色彩(类似黑白照片),默认值sat = 1,和原图片一样,sat>1 表示图片越温和。
PorterDuffColorFilter
PorterDuffColorFitler的作用就是制定一个颜色与制定的图层混合,混合之后得到的颜色。它只能作用于一种颜色值。
//构造函数位颜色值 和 合成模式(上面讲到的17中模式)
public PorterDuffColorFilter(@ColorInt int color, @NonNull PorterDuff.Mode mode) {
mColor = color;
mMode = mode;
}
例如下面代码的结果
PorterDuffColorFilter filter = new PorterDuffColorFilter(Color.RED,PorterDuff.Mode.DARKEN);
mPaint.setColorFilter(filter);
canvas.drawBitmap(mSrcBitmap,0,0,mPaint);
在后面Demo的ColorFilter类中,有各种类似美图里面的特殊效果的matrix,自己可以尝试玩一玩。
刮刮卡的效果实现
直接上代码,都是前面用到的知识:
public class EraserView extends View {
private Paint mPaint;
private Paint pathPaint;
private String text = "谢谢惠顾,祝君下次好运";
private Path mPath;
float textX;
private Bitmap mSrcBitmap,mDestBitmap;
private float eventX,eventY;
public EraserView(Context context) {
this(context,null);
}
public EraserView(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
public EraserView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(mSrcBitmap.getWidth(),mSrcBitmap.getHeight());
}
private void init(){
mPaint = new Paint();
mPaint.setColor(Color.BLACK);
mPaint.setStyle(Paint.Style.FILL);
mPaint.setTextSize(20);
pathPaint = new Paint();
pathPaint.setStrokeWidth(20);
pathPaint.setColor(Color.WHITE);
pathPaint.setStyle(Paint.Style.STROKE);
pathPaint.setStrokeCap(Paint.Cap.ROUND);
pathPaint.setStrokeJoin(Paint.Join.ROUND);
mPath = new Path();
setLayerType(View.LAYER_TYPE_SOFTWARE,null);
mSrcBitmap = BitmapFactory.decodeResource(getResources(),R.mipmap.guagua);
mDestBitmap = Bitmap.createBitmap(mSrcBitmap.getWidth(),mSrcBitmap.getHeight(),Bitmap.Config.ARGB_8888);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
textX = (getWidth()-mPaint.measureText(text))/2;
canvas.drawColor(Color.RED);
canvas.drawText(text,textX,getHeight()/2,mPaint);
int layerId = canvas.saveLayer(0,0,getWidth(),getHeight(),mPaint,Canvas.ALL_SAVE_FLAG);
Canvas desCanvas = new Canvas(mDestBitmap);
desCanvas.drawPath(mPath,pathPaint);
canvas.drawBitmap(mDestBitmap,0,0,mPaint);
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT));
canvas.drawBitmap(mSrcBitmap,0,0,mPaint);
mPaint.setXfermode(null);
canvas.restoreToCount(layerId);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:{
eventX = event.getX();
eventY = event.getY();
mPath.moveTo(eventX,eventY);
break;
}
case MotionEvent.ACTION_MOVE:{
float endX = (event.getX()-eventX)/2+eventX;
float endY = (event.getY()-eventY)/2+eventY;
mPath.quadTo(eventX,eventY,endX,endY);
eventX = event.getX();
eventY = event.getY();
break;
}
case MotionEvent.ACTION_UP:{
break;
}
}
invalidate();
return true;
}
}
参考文章: