今天閑來(lái)無(wú)事做楚堤,就想著鞏固一下PorterDuffXfermode
的使用吧,先是做了圓形和圓角矩形頭像效果含懊,然后就做了個(gè)刮刮卡效果身冬。如下圖所示
當(dāng)然 PorterDuffXfermode
的詳細(xì)使用在這里我就不在強(qiáng)調(diào)了,可以去 Google
一下岔乔,下面主要來(lái)介紹一下效果的實(shí)現(xiàn)原理酥筝。
其實(shí)這個(gè)效果十分的簡(jiǎn)單,主要就是 PorterDuffXfermode
模式的設(shè)置和 Paint
的屬性設(shè)置雏门,下面來(lái)看看核心代碼
mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.testpicture);
mDstBitmap = Bitmap.createBitmap(mBitmap.getWidth(), mSrcBitmap.getHeight(), Bitmap.Config.ARGB_8888);
mPath = new Path();
paint = new Paint();
paint.setAntiAlias(true);
paint.setAlpha(0);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(50);
paint.setStrokeCap(Paint.Cap.ROUND);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
mCanvas = new Canvas(mDstBitmap);
mCanvas.drawColor(Color.GRAY);
看看 paint
的屬性設(shè)置嘿歌,占了代碼的 70%
,看看都是哪些效果茁影,
首先設(shè)置防鋸齒效果宙帝,將畫(huà)筆設(shè)置成 stroke
模式并且設(shè)置寬度為 50
,這么設(shè)置有什么用呢募闲?這就決定了我們每次滑動(dòng)步脓,路徑的寬度是多少。
至于 StrokeCap
和 StrokeJoin
的屬性就是設(shè)置讓我們的畫(huà)筆點(diǎn)下去和滑動(dòng)的時(shí)候造成的效果更圓滑,仔細(xì)看看上面的效果演示沪编,是不是路徑的兩端都是圓弧形狀。
將 PorterDuffXfermode
設(shè)置成PorterDuff.Mode.DST_IN
模式年扩,然后調(diào)用Canvas.drawColor()
方法,這個(gè)時(shí)候?qū)嶋H上我們就是完成繪制一個(gè)和圖像相同大小的灰色區(qū)域蚁廓,也就是我們將要刮去的部分。
這個(gè)時(shí)候就很奇怪了厨幻,只是在 mDstBitmap
上進(jìn)行繪制相嵌,我們并沒(méi)有 mSrcBitmap
,更別提進(jìn)行什么操作了况脆,怎么實(shí)現(xiàn)效果呢饭宾?實(shí)際上這個(gè)時(shí)候我們可以將mSrcBitmap
虛擬化出來(lái),就當(dāng)它是存在的格了,而且是一個(gè)透明圖看铆,這也就是為什么我們將 畫(huà)筆的 Alpha
設(shè)置為0
,這樣我們每次手指滑動(dòng)的時(shí)候?qū)嶋H上就是在動(dòng)態(tài)增加 mSrcBitmap
的面積盛末,那么我們的mDstBitmap
的灰色區(qū)域也就被擦除了弹惦。
下面看看OnDraw()
方法
@Override
protected void onDraw(Canvas canvas) {
canvas.drawBitmap(mBitmap, 0, 0, null);
canvas.drawBitmap(mDstBitmap, 0, 0, null);
}
這個(gè)時(shí)候一定要畫(huà)mBitmap
再畫(huà) mDstBitmap
,這樣的話就像是 圖形我已經(jīng)畫(huà)出來(lái)了悄但,不過(guò)被 mDstBitmap
擋住了棠隐,這樣當(dāng)我們滑動(dòng)的時(shí)候,不斷擦除 mDstBitmap
檐嚣,也就將圖形顯示出來(lái)了助泽。
下面粘貼出源碼,實(shí)際上嚎京,我們還可以測(cè)試圖的寬度和長(zhǎng)度嗡贺,對(duì)當(dāng)前屏幕進(jìn)行適配,以免出現(xiàn)圖形大于屏幕挖藏,部分圖形無(wú)法顯示的問(wèn)題暑刃,實(shí)現(xiàn)起來(lái)也不是很復(fù)雜,所以在此就不講述膜眠。
public class XferModeView extends View {
private Bitmap mBitmap;
private Bitmap mDstBitmap;
private Canvas mCanvas;
private Path mPath;
private Paint paint;
public XferModeView(Context context) {
this(context, null);
}
public XferModeView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public XferModeView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView();
}
private void initView() {
mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.testpicture);
mDstBitmap = Bitmap.createBitmap(mBitmap.getWidth(), mBitmap.getHeight(), Bitmap.Config.ARGB_8888);
mPath = new Path();
paint = new Paint();
paint.setAntiAlias(true);
paint.setAlpha(0);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(50);
paint.setStrokeCap(Paint.Cap.ROUND);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
mCanvas = new Canvas(mDstBitmap);
mCanvas.drawColor(Color.GRAY);
}
@Override
protected void onDraw(Canvas canvas) {
canvas.drawBitmap(mBitmap, 0, 0, null);
canvas.drawBitmap(mDstBitmap, 0, 0, null);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mPath.reset();
mPath.moveTo(x, y);
break;
case MotionEvent.ACTION_MOVE:
mPath.lineTo(x, y);
break;
}
mCanvas.drawPath(mPath, paint);
invalidate();
return true;
}
}