Android塗鴉技術及刮刮樂樣本分析,android刮刮樂
概述:
很早之前就想研究一下Android中的塗鴉,其實也說不上是研究了,畢竟都是一些相對比較簡單的知識點。下面就對基於畫布(Canvas)和觸摸事件(onTouchEvent)來實現塗鴉和刮刮樂。
參考:
http://blog.csdn.net/lmj623565791/article/details/40162163
此人的部落格的確很好,想學習的同學也可以去參考一下這個大牛的其他部落格。
http://blog.csdn.net/t12x3456/article/details/10432935
樣本分析:
以下是兩個簡單的入門樣本:塗鴉技術和刮刮樂的一些簡單分析和效果展示。
1.塗鴉 思路分析及代碼展示
學習過Canvas的同學應該都知道我們可以通過在一個View上覆蓋一個canvas,並實現View的onTouchEvent方法就可以在Canvas上留下觸控螢幕幕時的軌跡,對於軌跡的記錄還有一個類需要去瞭解——Path。關於Canvas更多的知識請點擊這裡查看。
Android在繪製介面的時候會獲得布局中控制項的大小、位置等參數之後再去繪製。而這裡我們只是通過onMeasure和onDraw來繪製,沒有用到onLayout是因為這裡只有一個控制項,沒有太多動態布局需要處理。對於路徑的記錄則需要onTouchEvent實現。
測量大小:
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int width = getMeasuredWidth(); int height = getMeasuredHeight(); // 初始化bitmap mBitmap = Bitmap.createBitmap(width, height, Config.ARGB_8888); mCanvas = new Canvas(mBitmap); }
繪製:
protected void onDraw(Canvas canvas) { drawPath(); canvas.drawBitmap(mBitmap, 0, 0, null); }
路徑繪製:
我們通過Path儲存我們觸摸的路徑軌跡。如下:
private void drawPath() { mFingerPaint.setStyle(Paint.Style.STROKE); mCanvas.drawPath(mPath, mFingerPaint); }
觸摸事件:
對於觸摸事件有一個非常重要而且不可忽視的類就是MotionEvent。它有以下三個常用的動作事件:
1.MotionEvent.ACTION_DOWN// 觸摸按下時
2.MotionEvent.ACTION_MOVE// 觸摸在移動過程中
3.MotionEvent.ACTION_UP // 觸摸離開時
下面就看看onTouchEvent事件的實現過程:
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: actionMotionEventDown(x, y); break; case MotionEvent.ACTION_MOVE: actionMotionEventMove(x, y); break; } invalidate(); return true; }
上面的代碼中,我們在按下的時候實現了按下的邏輯,在手指在螢幕上移動的時候實現了Move的邏輯。還有別忘了invalidate()。invalidate()函數的主要作用是請求View樹進行重繪,如果你不去調用它,結果就是什麼事情都不會發生。
2.刮刮樂 思路分析及代碼展示
分析:其實刮刮樂的實現思路跟塗鴉很像,都是在一塊地方瞎畫,並留下痕迹(說笑了,不過也不無道理。^_^)。不過有一點不同的就是我們在刮刮樂的繪製過程中畫筆經過的地方,是變成了透明的了。這裡你可能會說,那簡單了,不就是要我去覆蓋兩層圖片,在去繪製觸摸路徑,只是觸摸路徑的顏色是透明的。真的是這樣的嗎?你可以試一試。當然,這樣是行不通的,關於實踐的最終效果大家可以自行嘗試。這裡的關鍵點在於我們要把上面的蒙層擦除且保留下面的底層。這裡就用到了圖形混合技術了。
圖形混合技術一聽名稱是不是就是感覺很高深,不過的確是很牛的技術,不過Java已經給我們封裝好了,我們只要知道怎麼使用即可,而使用它則就不那麼艱難了。
關於圖形混合的詳細描述,大家可以參考這裡,我就不重複製造輪子了。不過我還是要簡單介紹一下Xfermode三個子類下的一個:PorterDuffXfermode。這是一個非常強大的轉換模式,使用它,可以使用映像合成的16條Porter-Duff規則的任意一條來控制Paint如何與已有的Canvas映像進行互動。
Porter-Duff規則如下:
PorterDuff.Mode為枚舉類,一共有16個枚舉值:
1.PorterDuff.Mode.CLEAR
所繪製不會提交到畫布上。
2.PorterDuff.Mode.SRC
顯示上層繪製圖片
3.PorterDuff.Mode.DST
顯示下層繪製圖片
4.PorterDuff.Mode.SRC_OVER
正常繪製顯示,上下層繪製疊蓋。
5.PorterDuff.Mode.DST_OVER
上下層都顯示。下層居上顯示。
6.PorterDuff.Mode.SRC_IN
取兩層繪製交集。顯示上層。
7.PorterDuff.Mode.DST_IN
取兩層繪製交集。顯示下層。
8.PorterDuff.Mode.SRC_OUT
取上層繪製非交集部分。
9.PorterDuff.Mode.DST_OUT
取下層繪製非交集部分。
10.PorterDuff.Mode.SRC_ATOP
取下層非交集部分與上層交集部分
11.PorterDuff.Mode.DST_ATOP
取上層非交集部分與下層交集部分
12.PorterDuff.Mode.XOR
異或:去除兩圖層交集部分
13.PorterDuff.Mode.DARKEN
取兩圖層全部地區,交集部分顏色加深
14.PorterDuff.Mode.LIGHTEN
取兩圖層全部,點亮交集部分顏色
15.PorterDuff.Mode.MULTIPLY
取兩圖層交集部分疊加後顏色
16.PorterDuff.Mode.SCREEN
取兩圖層全部地區,交集部分變為透明色
我們需要的正是:DstOut這一條。代碼中我們是這樣實現的:
private void drawPath() { mFingerPaint.setStyle(Paint.Style.STROKE); // 設定兩張圖片相交時的模式(取下層繪製非交集部分) mFingerPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT)); mCanvas.drawPath(mPath, mFingerPaint); }
測量和繪製過程如下:
@Override protected void onDraw(Canvas canvas) { canvas.drawText(mText, getWidth() / 2 - mTextBound.width() / 2, getHeight() / 2 + mTextBound.height() / 2, mBackPint); if (!isComplete) { drawPath(); canvas.drawBitmap(mBitmap, 0, 0, null); } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int width = getMeasuredWidth(); int height = getMeasuredHeight(); // 初始化bitmap mBitmap = Bitmap.createBitmap(width, height, Config.ARGB_8888); mCanvas = new Canvas(mBitmap); // 繪製遮蓋層 mFingerPaint.setStyle(Paint.Style.FILL); mCanvas.drawRoundRect(new RectF(0, 0, width, height), 30, 30, mFingerPaint); mCanvas.drawBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.mask), null, new RectF(0, 0, width, height), null); }
此外還有一篇也是使用了此技術的部落格,點擊這裡進行查看。
源碼下載:
http://download.csdn.net/detail/u013761665/8737527