Android 自訂控制項實現刮刮卡效果 真的就只是刮刮卡麼

來源:互聯網
上載者:User

標籤:android   style   blog   http   io   color   os   ar   使用   

很久以前也過一個html5的刮刮卡效果~~上次看到有人寫Android的刮刮卡效果~~於是乎產生了本篇部落格~~此類例子也比較多了,大家可以百度看看~不過還是通過本例子,帶大家發掘一下,裡面隱含的知識~

1、Xfermode以及PorterDuff

如果大家還記得,曾經在部落格:完美實現圖片圓角和圓形 簡單介紹過圓角的實現原理也是基於這個。

首先我們看一下官方的例子,很好的展示了16種Mode的效果:


註:先繪製的Dst,再繪製的Src。

好了,看了這個圖,我來問大家幾個問題:

問題1、如果我想實現圓形圖片,怎麼實現?

答:先繪製我們的圖片,然後在上面繪製一個圓,最後產生的效果就是圓形圖片;等等,怎麼就產生了,請看上面的SrcIn這種模式;

先繪製的Dst,然後設定DstIn,然後繪製Src;最後效果是留下了二者交集且是Dst的部分;下面我們把我們的答案帶進去。

先繪製圖片,然後設定DstIn,然後繪製圓形;最後效果是留下了二者交集且是圖片的部分;嗯,交集是什麼,圓形;圓形內容是什麼,圖片;搜噶,有點感覺了。

----

等等,我還有有個思路,先繪製圓形,然後設定SrcIn,再繪製我們的圖片;也能產生我們的圓形圖片。我們來看看:

SrcIn最終保留的依然是交集,但是顯示為後繪製的,也就是我們的圖片,搜噶,這樣也可以。

 

問題2、如果我想實現圓角圖片,怎麼實現?

答:擦,看了上面的答案,你還沒思路麼。把繪製圓形,改成繪製圓角矩形。請問你還有什麼問的,額,,,木有了。

嗯,把問題1的圓形改成圓角,按照相同的繪製過程就實現了我們的圓角圖片了。

 

問題3、這和我們的刮刮卡有毛線關係?

答:怎麼沒有關係,,,你先繪製刮獎層,然後設定DST_OUT,然後把使用者手觸摸的線條繪製上去;使用者觸摸到刮獎層的部分(交集部分)顯示就是使用者觸摸的線條,那麼我們這個線條如果是透明的,也是就說刮獎層被我們擦掉了~

這不就是刮獎麼。等等,獎呢?

獎無非就是文本,或者圖片,提前繪製一下,然後在其上繪製刮獎層,設定DST_OUT,然後把使用者觸摸繪製上去;這樣消失以後就能看到背後的獎了~~~對了,現在還有個app叫脫什麼妹子衣服,先繪製妹子,然後繪製衣服,然後擦~~這個和刮獎像不像,額,我什麼都沒說。

 

搜噶,經過上面的3個問題,大家應該明白了,什麼圓角,圓形,刮刮卡,其實原理就這麼簡單,,,

 

2、簡易畫板的實現

我們的刮刮卡需要掌握繪圖,當然了這裡不要求你有美術天分,會瞎塗鴉就可以了~~

下面開始我們的一個簡易的畫板,其實就是可以在上面畫點線條,當然你也可以簽個名,我們的View的叫做GuaGuaKa:

1、初步GuaGuaKa
package com.zhy.view;import android.content.Context;import android.graphics.Bitmap;import android.graphics.Bitmap.Config;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.Path;import android.util.AttributeSet;import android.view.MotionEvent;import android.view.View;public class GuaGuaKa extends View{/** * 繪製線條的Paint,即使用者手指繪製Path */private Paint mOutterPaint = new Paint();/** * 記錄使用者繪製的Path */private Path mPath = new Path();/** * 記憶體中建立的Canvas */private Canvas mCanvas;/** * mCanvas繪製內容在其上 */private Bitmap mBitmap;private int mLastX;private int mLastY;public GuaGuaKa(Context context){this(context, null);}public GuaGuaKa(Context context, AttributeSet attrs){this(context, attrs, 0);}public GuaGuaKa(Context context, AttributeSet attrs, int defStyle){super(context, attrs, defStyle);init();}private void init(){mPath = new Path();}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){super.onMeasure(widthMeasureSpec, heightMeasureSpec);int width = getMeasuredWidth();int height = getMeasuredHeight();// 初始化bitmapmBitmap = Bitmap.createBitmap(width, height, Config.ARGB_8888);mCanvas = new Canvas(mBitmap);// 設定畫筆mOutterPaint.setColor(Color.RED);mOutterPaint.setAntiAlias(true);mOutterPaint.setDither(true);mOutterPaint.setStyle(Paint.Style.STROKE);mOutterPaint.setStrokeJoin(Paint.Join.ROUND); // 圓角mOutterPaint.setStrokeCap(Paint.Cap.ROUND); // 圓角// 設定畫筆寬度mOutterPaint.setStrokeWidth(20);}@Overrideprotected void onDraw(Canvas canvas){drawPath();canvas.drawBitmap(mBitmap, 0, 0, null);}/** * 繪製線條 */private void drawPath(){mCanvas.drawPath(mPath, mOutterPaint);}@Overridepublic 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;}}


代碼量比較少,我們在記憶體中搞了一個mCanvas,建立了一個mBitmap,然後通過mCanvas使用我們預先設定的mOuterPaint在我們的mBitmap上繪製mPath;

mPath裡面的資料怎麼搞呢?就是onTouchEvent裡面不斷的moveTo,lineTo就好了~~代碼還是很隨意的

最後,注意我們繪製記憶體上的mBitmap上面,然後我們通過view的canvas,把我們的mBitmap展現。咦,怎麼有點雙緩衝的感腳。

好了,現在你就可以在我們的畫板上肆掠了:

下面看布局檔案以及運行效果:

2、布局檔案及運行效果

布局檔案:

<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" >    <com.zhy.view.GuaGuaKa        android:layout_width="match_parent"        android:layout_height="match_parent" /></RelativeLayout>


運行效果:

看到我渾厚的字型沒有,等以後寫不動程式了,我就去當書法家~

 

好了,我們的簡易畫板完成以後,我們開始考慮正題,一步一步逼近我們的刮刮板,現在我們準備這樣做,首先在背後繪製一張圖片,然後繪製一個遮蓋層,然後我們繪畫的過程就是擦除遮蓋層。

 

3、擦除的第一次實現

鑒於很多朋友的意見,我決定這次的圖片用風景圖,遠離xxx , 感謝seven提供的圖片~

1、繪製遮蓋層

其實遮蓋層就是一個顏色:

@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){super.onMeasure(widthMeasureSpec, heightMeasureSpec);int width = getMeasuredWidth();int height = getMeasuredHeight();// 初始化bitmapmBitmap = Bitmap.createBitmap(width, height, Config.ARGB_8888);mCanvas = new Canvas(mBitmap);setUpOutPaint();//繪製這改成mCanvas.drawColor(Color.parseColor("#c0c0c0"));}


和上面貼的代碼就多了最後一行,另外我們的paint的設定抽取出去了~

2、drawPath

文章起初的原理終於要用上了,我們在繪製Path的時候,需要設定一個模式,這裡是DST_OUT ,想想有點小激動~

private void drawPath(){mOutterPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));mCanvas.drawPath(mPath, mOutterPaint);}

 

3、onDraw

onDraw裡面也要做一點修改

@Overrideprotected void onDraw(Canvas canvas){canvas.drawBitmap(mBackBitmap, 0, 0, null);drawPath();canvas.drawBitmap(mBitmap, 0, 0, null);}


好了,到此完成,其實就添加了幾行代碼,就完成了我們簡易畫板到刮刮卡的轉變;

下面看效果:

有沒有撥開雲霧見天明的感覺~~

到此我們的刮刮卡的原理,以及初步的實現結束了~~還是很簡單的~接下來就是後續的完善工作

 

4、刮刮卡的完善

我們準備把獎項改為字型,將字型繪製在螢幕的中間;

那麼直接把上例繪製圖片改為繪製字型就行了,不過多了一個繪製字型畫筆的設定:

有變化的代碼:

private Paint mBackPint = new Paint();private Rect mTextBound = new Rect();private String mText = "500,0000,000";/** * 初始化canvas的繪製用的畫筆 */private void setUpBackPaint(){mBackPint.setStyle(Style.FILL);mBackPint.setTextScaleX(2f);mBackPint.setColor(Color.DKGRAY);mBackPint.setTextSize(22);mBackPint.getTextBounds(mText, 0, mText.length(), mTextBound);}@Overrideprotected void onDraw(Canvas canvas){// canvas.drawBitmap(mBackBitmap, 0, 0, null);//繪製獎項canvas.drawText(mText, getWidth() / 2 - mTextBound.width() / 2,getHeight() / 2 + mTextBound.height() / 2, mBackPint);drawPath();canvas.drawBitmap(mBitmap, 0, 0, null);}


下面看看我中了多錢:

好了,到此已經完全實現了,大家按照例子,結合自己需求修改即可,裡面所涉及的原理相信已經解釋清楚了;對了,差點忘了,刮刮卡一般都有一個功能,當你掛了差不多的時候,塗層會自動清除,下面我們嘗試添加該功能。

 

5、統計刮開地區已佔的百分比

我們在ACTION_UP的時候就行計算,首先我們還是給大家灌輸下計算的原理,如果大家用心看了,應該知道我們所有的操作基本都在mBitmap,現在我們獲得mBItmap上所有的像素點的資料,統計被清除的地區(被清除的像素為0);最後與我們圖片的總像素數做個除法元算,就可以拿到我們清除的百分比了;

不過,計算可能會是一個耗時的操作,具體速度跟圖片大小有關,所以我們決定使用非同步方式去計算:

/** * 統計擦除地區任務 */private Runnable mRunnable = new Runnable(){private int[] mPixels;@Overridepublic void run(){int w = getWidth();int h = getHeight();float wipeArea = 0;float totalArea = w * h;Bitmap bitmap = mBitmap;mPixels = new int[w * h];/** * 拿到所有的像素資訊 */bitmap.getPixels(mPixels, 0, w, 0, 0, w, h);/** * 遍曆統計擦除的地區 */for (int i = 0; i < w; i++){for (int j = 0; j < h; j++){int index = i + j * w;if (mPixels[index] == 0){wipeArea++;}}}/** * 根據所佔百分比,進行一些操作 */if (wipeArea > 0 && totalArea > 0){int percent = (int) (wipeArea * 100 / totalArea);Log.e("TAG", percent + "");if (percent > 70){isComplete = true;postInvalidate();}}}};


有了這個任務,我們在ACTION_UP的時候就行調用:

case MotionEvent.ACTION_UP:new Thread(mRunnable).start();break;

注意任務結束,會把一個isComplete設定為true;當為true時,我們直接展現刮獎區

@Overrideprotected void onDraw(Canvas canvas){drawBackText(canvas);if (!isComplete){drawPath();canvas.drawBitmap(mBitmap, 0, 0, null);}}


到此刮獎區的計算就結束了。

下面在示範前,我做了一些簡單的美化,具體大家到時候看源碼就可以。

到此我們的刮刮卡製作就結束了,另外如果大家希望再完善,可以把裡面很多常量設定成變數,添加對外的set方法,或者抽取成自訂屬性,在布局檔案進行定義都可以~~~

有一點需要說明一下,對於我們刮刮卡這個案例,我們布局檔案如果寬高設定為wrap_content,也會佔滿螢幕,主要是因為我覺得刮刮卡這個view沒必要wrap_content;但是如果你希望支援wrp_content,可以參考Android 自訂View (一) 。

 

Android 自訂控制項實現刮刮卡效果 真的就只是刮刮卡麼

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.