先看效果圖

GIF.gif
技術點:
1.畫筆的XferMode,XferMode是指定兩張圖相交后的效果,這里主要是灰色涂層和手指移動痕跡的相交效果。
2.根據onTouchEvent點擊事件記錄手指移動的坐標,再用這些坐標用drawPath來畫出不規(guī)則的移動痕跡。
Xfermode
Xfermode有三個子類:
- AvoidXfermode 指定了一個顏色和容差,強制Paint避免在它上面繪圖(或者只在它上面繪圖)。
- PixelXorXfermode 當覆蓋已有的顏色時,應用一個簡單的像素XOR操作。
- PorterDuffXfermode 有16種規(guī)則來控制Paint如何與已有的Canvas圖像進行交互,我們用的就是這個。
其中16種規(guī)則效果如下圖所示,圓為Dst,正方形為Src。

xfermode效果.png
public enum Mode {
/** [0, 0] */
CLEAR (0),
/** [Sa, Sc] */
SRC (1),
/** [Da, Dc] */
DST (2),
/** [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] */
SRC_OVER (3),
/** [Sa + (1 - Sa)*Da, Rc = Dc + (1 - Da)*Sc] */
DST_OVER (4),
/** [Sa * Da, Sc * Da] */
SRC_IN (5),
/** [Sa * Da, Sa * Dc] */
DST_IN (6),
/** [Sa * (1 - Da), Sc * (1 - Da)] */
SRC_OUT (7),
/** [Da * (1 - Sa), Dc * (1 - Sa)] */
DST_OUT (8),
/** [Da, Sc * Da + (1 - Sa) * Dc] */
SRC_ATOP (9),
/** [Sa, Sa * Dc + Sc * (1 - Da)] */
DST_ATOP (10),
/** [Sa + Da - 2 * Sa * Da, Sc * (1 - Da) + (1 - Sa) * Dc] */
XOR (11),
/** [Sa + Da - Sa*Da, Sc*(1 - Da) + Dc*(1 - Sa) + min(Sc, Dc)] */
DARKEN (16),
/** [Sa + Da - Sa*Da, Sc*(1 - Da) + Dc*(1 - Sa) + max(Sc, Dc)] */
LIGHTEN (17),
/** [Sa * Da, Sc * Dc] */
MULTIPLY (13),
/** [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] */
SCREEN (14),
/** Saturate(S + D) */
ADD (12),
OVERLAY (15);
Mode(int nativeInt) {
this.nativeInt = nativeInt;
}
/**
* @hide
*/
public final int nativeInt;
}
RabbleView代碼
全部代碼都在下面了,其中注釋已經說的很清楚了。
public class RabbleView extends TextView{
//要擦拭的背景
private Bitmap mBitmap;
//擦拭背景顏色
private int bgColor = 0XFFCECECE;
//擦拭畫布
private Canvas mCanvas;
//擦拭畫筆
private Paint mPaint;
//擦拭畫筆寬度
private int mStrokeWidth = DisplayUtils.dp2px(getContext(),40);
//擦拭痕跡
private Path mPath;
private float mX, mY;
private boolean isUp = false;
public RabbleView(Context context) {
this(context,null);
}
public RabbleView(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
public RabbleView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initRabble();
}
private void initRabble(){
mPaint = new Paint();
//透明
mPaint.setAlpha(0);
//抗鋸齒
mPaint.setDither(true);
//防抖動
mPaint.setAntiAlias(true);
// 此處不能為透明色
mPaint.setColor(Color.BLACK);
//兩圖的相交模式
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
mPaint.setStyle(Paint.Style.STROKE);
//設置結合處的樣子,Miter:結合處為銳角, Round:結合處為圓?。築EVEL:結合處為直線
mPaint.setStrokeJoin(Paint.Join.ROUND); // 前圓角
//當畫筆樣式為STROKE或FILL_OR_STROKE時,設置筆刷的圖形樣式,如圓形樣式
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeWidth(mStrokeWidth);
// 痕跡
mPath = new Path();
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
//初始化擦拭背景
mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
mCanvas.drawColor(bgColor);
}
@Override
protected void onDraw(Canvas canvas){
super.onDraw(canvas);
//要是寬度或者高度為0就沒必要加上擦拭涂層了
if (getWidth() == 0 || getHeight() == 0) {
return;
}
if (!isUp) {
mCanvas.drawPath(mPath, mPaint);
} else{
//當手指離開就把涂層變成透明的
mBitmap.eraseColor(0X00);
}
canvas.drawBitmap(mBitmap, 0, 0, null);
}
@Override
public boolean onTouchEvent(MotionEvent event){
super.onTouchEvent(event);
switch (event.getActionMasked()){
case MotionEvent.ACTION_DOWN:
down(event.getX(),event.getY());
break;
case MotionEvent.ACTION_MOVE:
move(event.getX(),event.getY());
break;
case MotionEvent.ACTION_UP:
up(event.getX(),event.getY());
break;
}
return true;
}
private void down(float x, float y){
mPath.reset();
mX = x;
mY = y;
mPath.moveTo(x,y);
}
private void move(float x, float y){
float dx = Math.abs(x - mX);
float dy = Math.abs(y - mY);
if (dx >= 3 || dy >= 3) {
mPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
// mPath.lineTo(x,y);
mX = x;
mY = y;
}
invalidate();
}
private void up(float x, float y){
mPath.lineTo(x, y);
mPath.reset();
isUp = true;
invalidate();
}