Paint API之—— Xfermode與PorterDuff詳解(四)
本節(jié)引言:
上節(jié)我們寫(xiě)了關(guān)于Xfermode與PorterDuff使用的第一個(gè)例子:圓角&圓形圖片ImageView的實(shí)現(xiàn), 我們體會(huì)到了PorterDuff.Mode.DST_IN給我們帶來(lái)的好處,本節(jié)我們繼續(xù)來(lái)寫(xiě)例子練練手, 還記得8.3.2 繪圖類實(shí)戰(zhàn)示例給大家?guī)?lái)的拔掉美女衣服的實(shí)現(xiàn)嗎?
當(dāng)時(shí)我們的實(shí)現(xiàn)方案是,將手指觸碰區(qū)域附近的20*20個(gè)像素點(diǎn)設(shè)置為透明,效果圖是這樣的:
不知道你有沒(méi)有發(fā)現(xiàn)一個(gè)問(wèn)題,我們擦美女衣服的時(shí)候,擦拭的時(shí)候都是方塊的,但是我們畫(huà)圖板 畫(huà)圖的時(shí)候,劃線都是很平滑的,有沒(méi)有辦法將兩者結(jié)合起來(lái),我們擦衣服時(shí)也是圓滑的呢? 答案肯定是有的,就是使用Xfermode咯!本節(jié)我們使用另一個(gè)模式,DST_OUT模式! 在不相交的地方繪制目標(biāo)圖
如果你忘記了某個(gè)模式或者連18種模式都沒(méi)見(jiàn)過(guò)的話,那么請(qǐng)移步:Android基礎(chǔ)入門(mén)教程——8.3.5 Paint API之—— Xfermode與PorterDuff詳解(二)另外,還是要貼下PorterDuff.Mode的效果圖:
嗯,話不多說(shuō),開(kāi)始本節(jié)內(nèi)容~
1.要實(shí)現(xiàn)的效果圖以及實(shí)現(xiàn)流程分析:
要實(shí)現(xiàn)的效果圖:
嗯,不知道你看了那個(gè)Gif圖多少次了呢?不知道圖中是否適合大家的口味,小豬 是從別人的APP上扒下來(lái)的,別問(wèn)我番號(hào)或者留郵箱什么的,我什么都不知道~找番什么的, 問(wèn)群里的老司機(jī)——基神吧,好的,我們來(lái)分析下實(shí)現(xiàn)流程吧~
- 我們來(lái)說(shuō)說(shuō)原理,其實(shí)就是兩個(gè)Bitmap,一前一后,前面的是穿著衣服的,后面的是沒(méi)穿衣服的, 然后通過(guò)一個(gè)Path來(lái)記錄用戶繪制出來(lái)的圖形,然后為我們的畫(huà)筆設(shè)置DST_OUT的模式,那么 與Path重疊部分的DST(目標(biāo)圖),就是穿著衣服的圖,會(huì)變成透明!好噠,很簡(jiǎn)單! 我們?cè)俾?xì)化!
- 首先我們需要兩個(gè)Bitmap,用來(lái)存儲(chǔ)前后兩張圖片,這里我們讓兩個(gè)Bitmap都全屏!
- 接著設(shè)置下畫(huà)筆,圓角,筆寬,抗鋸齒等!
- 再接著定義一個(gè)畫(huà)Path,即用戶繪制區(qū)域的方法,設(shè)置Xfermode后畫(huà)區(qū)域而已!
- 然后重寫(xiě)onTouchEvent方法,這部分和之前的自定義畫(huà)圖板是一樣的!
- 最后重寫(xiě)onDraw()方法,先繪制背景圖片,調(diào)用用戶繪制區(qū)域的方法,再繪制前景圖片!
可能看上去有點(diǎn)復(fù)雜,其實(shí)不然,代碼超簡(jiǎn)單的說(shuō)~
2.代碼實(shí)現(xiàn):
直接就一個(gè)自定義View——StripMeiZi.java
/** * Created by Jay on 2015/10/25 0025. */ public class StripMeiZi extends View{ private Paint mPaint = new Paint(); private Path mPath = new Path(); private Canvas mCanvas; private Bitmap mBeforeBitmap; private Bitmap mBackBitmap; private int mLastX,mLastY; private int screenW, screenH; //屏幕寬高 private Xfermode mXfermode = new PorterDuffXfermode(PorterDuff.Mode.DST_OUT); public StripMeiZi(Context context) { this(context, null); } public StripMeiZi(Context context, AttributeSet attrs) { super(context, attrs); screenW = ScreenUtil.getScreenW(context); screenH = ScreenUtil.getScreenH(context); init(); } public StripMeiZi(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } private void init() { //背后圖片,這里讓它全屏 mBackBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.meizi_back); mBackBitmap = Bitmap.createScaledBitmap(mBackBitmap, screenW, screenH, false); //前面的圖片,并繪制到Canvas上 mBeforeBitmap = Bitmap.createBitmap(screenW, screenH, Bitmap.Config.ARGB_8888); mCanvas = new Canvas(mBeforeBitmap); mCanvas.drawBitmap(BitmapFactory.decodeResource(getResources(), R.mipmap.meizi_before), null, new RectF(0, 0, screenW, screenH), null); //畫(huà)筆相關(guān)的設(shè)置 mPaint.setAntiAlias(true); mPaint.setDither(true); mPaint.setStyle(Paint.Style.STROKE); mPaint.setStrokeJoin(Paint.Join.ROUND); // 圓角 mPaint.setStrokeCap(Paint.Cap.ROUND); // 圓角 mPaint.setStrokeWidth(80); // 設(shè)置畫(huà)筆寬 } private void drawPath() { mPaint.setXfermode(mXfermode); mCanvas.drawPath(mPath, mPaint); } @Override protected void onDraw(Canvas canvas) { canvas.drawBitmap(mBackBitmap, 0, 0, null); drawPath(); canvas.drawBitmap(mBeforeBitmap, 0, 0, null); } @Override public boolean onTouchEvent(MotionEvent event) { int action = event.getAction(); int x = (int) event.getX(); int y = (int) event.getY(); switch (action) { case MotionEvent.ACTION_DOWN: mLastX = x; mLastY = y; mPath.moveTo(mLastX, mLastY); break; case MotionEvent.ACTION_MOVE: int dx = Math.abs(x - mLastX); int dy = Math.abs(y - mLastY); if (dx > 3 || dy > 3) mPath.lineTo(x, y); mLastX = x; mLastY = y; break; } invalidate(); return true; } }
布局代碼activity_main.xml:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <com.jay.xfermodedemo2.StripMeiZi android:layout_width="match_parent" android:layout_height="match_parent"/></RelativeLayout>
3.代碼示例下載:
本節(jié)小結(jié):
好的,本節(jié)我們寫(xiě)了Xfermode與PorterDuff的另一個(gè)實(shí)戰(zhàn)例子——手撕美女衣服的Demo,相比起我們之前 那種撕美女衣服(讓觸摸點(diǎn)附近20*20的像素點(diǎn)變成透明)的方式斯文多了~代碼也簡(jiǎn)單很多是吧,有沒(méi)有 體會(huì)到Android圖像混排Xfermode給我們帶來(lái)的好處,或者對(duì)于自定義控件的重要性!嗯,還等什么, 打開(kāi)你的IDE,把代碼擼一遍,嘗嘗手撕美女衣服的快感吧~