Android通過自定義view實(shí)現(xiàn)刮刮樂效果詳解
前言
已經(jīng)有兩個(gè)月沒有更新博客了,其實(shí)這篇文章我早在兩個(gè)月前就寫好了,一直保存在草稿箱里沒有發(fā)布出來。原因是有一些原理性的東西還沒了解清楚,最近抽時(shí)間研究了一下混合模式,終于也理解了刮刮樂是怎么實(shí)現(xiàn)的,所以想繼續(xù)分享一下自己的一些心得,先上效果圖。
效果圖:
實(shí)現(xiàn)原理
其實(shí)刮刮樂實(shí)現(xiàn)原理也不算很復(fù)雜,最關(guān)鍵的還是需要了解Paint的混合模式。因?yàn)楣喂螛肥怯蓛蓚€(gè)bitmap組成的,一個(gè)是源圖另一個(gè)是目標(biāo)圖,我們需要把目標(biāo)圖的顏色改成灰色,在源圖上面蓋上了一張灰色的目標(biāo)圖。當(dāng)手指滑動(dòng)屏幕時(shí)paint會(huì)在新的canvas上畫出路徑,由于新的canvas會(huì)持有一個(gè)新的bitmap,最終兩個(gè)bitmap的像素點(diǎn)重疊時(shí)就顯示源圖的部分,從而實(shí)現(xiàn)了刮刮樂的效果。這里用的是混合模式中的PorterDuff.Mode.DST_IN。
關(guān)鍵代碼:
pathPaint.setXfermode(new PorterDuffXfermode((PorterDuff.Mode.DST_IN)));
關(guān)鍵步驟
創(chuàng)建bitmap
@Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); mBitmapBackground = getBitmap(mBitmapBackground, w, h); mBitmapFront = Bitmap.createBitmap(mBitmapBackground.getWidth(), mBitmapBackground.getHeight(), Bitmap.Config.ARGB_8888); mCanvas.setBitmap(mBitmapFront); drawText(mCanvas, w, h); }
onSizeChanged方法里面創(chuàng)建了兩個(gè)bitmap,一個(gè)是背景圖,另一個(gè)是覆蓋在背景圖上面的bitmap。然后是在上面的bitmap上面繪制文字。
繪制文字
private void drawText(Canvas canvas, int mWidth, int mHeight) { String text = "趕緊刮開吧"; canvas.drawColor(Color.GRAY); Paint.FontMetrics fm = mPaintText.getFontMetrics(); int mTxtWidth = (int) mPaintText.measureText(text, 0, text.length()); int mTxtHeight = (int) Math.ceil(fm.descent - fm.ascent); int x = mWidth / 2 - mTxtWidth / 2; //文字在畫布中的x坐標(biāo) int y = mHeight / 2 + mTxtHeight / 4; //文字在畫布中的y坐標(biāo) canvas.drawText(text, x, y, mPaintText); }
調(diào)用canvas的drawText方法繪制文字,x,y是文字中心位置的坐標(biāo)。測(cè)量文字寬高有兩種方式,這里用的是更精確的getFontMetrics方法。
畫路徑
@Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: path.reset(); path.moveTo(event.getX(), event.getY());//原點(diǎn)移動(dòng)至手指的觸摸點(diǎn) break; case MotionEvent.ACTION_MOVE: path.lineTo(event.getX(), event.getY()); break; } mCanvas.drawPath(path, pathPaint); invalidate(); return true; }
最終效果圖
完整代碼
import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Path; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import androidx.annotation.Nullable; import com.example.androidprogressbar.R; public class ScratchCard extends View { private Bitmap mBitmapBackground; private Bitmap mBitmapFront; private Canvas mCanvas; private Paint pathPaint; private Path path; private Paint mPaintText; public ScratchCard(Context context) { super(context); init(); } public ScratchCard(Context context, @Nullable AttributeSet attrs) { super(context, attrs); init(); } public ScratchCard(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { pathPaint = new Paint(); pathPaint.setAlpha(0); pathPaint.setStyle(Paint.Style.STROKE); pathPaint.setStrokeWidth(70); pathPaint.setXfermode(new PorterDuffXfermode((PorterDuff.Mode.DST_IN)));//混合模式 pathPaint.setStrokeJoin(Paint.Join.ROUND);//線段之間連接處的樣式 pathPaint.setStrokeCap(Paint.Cap.ROUND);//設(shè)置畫筆的線冒樣式 path = new Path(); mBitmapBackground = BitmapFactory.decodeResource(getResources(), R.drawable.card); mCanvas = new Canvas(); mPaintText = new Paint(); mPaintText.setColor(Color.WHITE); mPaintText.setTextSize(100); mPaintText.setStrokeWidth(20); } @Override protected void onDraw(Canvas canvas) { canvas.drawBitmap(mBitmapBackground, 0, 0, null); canvas.drawBitmap(mBitmapFront, 0, 0, null); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); mBitmapBackground = getBitmap(mBitmapBackground, w, h); mBitmapFront = Bitmap.createBitmap(mBitmapBackground.getWidth(), mBitmapBackground.getHeight(), Bitmap.Config.ARGB_8888); mCanvas.setBitmap(mBitmapFront); drawText(mCanvas, w, h); } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: path.reset(); path.moveTo(event.getX(), event.getY());//原點(diǎn)移動(dòng)至手指的觸摸點(diǎn) break; case MotionEvent.ACTION_MOVE: path.lineTo(event.getX(), event.getY()); break; } mCanvas.drawPath(path, pathPaint); invalidate(); return true; } private void drawText(Canvas canvas, int mWidth, int mHeight) { String text = "趕緊刮開吧"; canvas.drawColor(Color.GRAY); Paint.FontMetrics fm = mPaintText.getFontMetrics(); int mTxtWidth = (int) mPaintText.measureText(text, 0, text.length()); int mTxtHeight = (int) Math.ceil(fm.descent - fm.ascent); int x = mWidth / 2 - mTxtWidth / 2; //文字在畫布中的x坐標(biāo) int y = mHeight / 2 + mTxtHeight / 4; //文字在畫布中的y坐標(biāo) canvas.drawText(text, x, y, mPaintText); } public Bitmap getBitmap(Bitmap bm, int newWidth, int newHeight) { // 獲得圖片的寬高 int width = bm.getWidth(); int height = bm.getHeight(); // 計(jì)算縮放比例 float scaleWidth = ((float) newWidth) / width; float scaleHeight = ((float) newHeight) / height; // 取得想要縮放的matrix參數(shù) Matrix matrix = new Matrix(); matrix.postScale(scaleWidth, scaleHeight); // 得到新的圖片 Bitmap newbm = Bitmap.createBitmap(bm, 0, 0, width, height, matrix, true); return newbm; } }
以上就是Android通過自定義view實(shí)現(xiàn)刮刮樂效果詳解的詳細(xì)內(nèi)容,更多關(guān)于Android刮刮樂的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
android屏幕圓角實(shí)現(xiàn)方法的示例代碼
本篇文章主要介紹了android屏幕圓角實(shí)現(xiàn)方法的示例代碼,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-11-11Android 應(yīng)用的全屏和非全屏實(shí)現(xiàn)代碼
這篇文章主要介紹了Android 應(yīng)用的全屏和非全屏實(shí)現(xiàn)代碼的相關(guān)資料,需要的朋友可以參考下2017-05-05android viewpager實(shí)現(xiàn)豎屏滑動(dòng)效果
這篇文章主要為大家詳細(xì)介紹了android viewpager實(shí)現(xiàn)豎屏滑動(dòng)效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-07-07總結(jié)Android App內(nèi)存優(yōu)化之圖片優(yōu)化
網(wǎng)上有很多大拿分享的關(guān)于Android性能優(yōu)化的文章,主要是通過各種工具分析,使用合理的技巧優(yōu)化APP的體驗(yàn),提升APP的流暢度,但關(guān)于內(nèi)存優(yōu)化的文章很少有看到。下面是我在實(shí)踐過程中使用的一些方法,很多都是不太成熟的項(xiàng)目,只是將其作為一種處理方式分享給大家。2016-08-08關(guān)于Android Activity之間跳轉(zhuǎn)問題(Intent)
這篇文章主要介紹了Android Activity之間跳轉(zhuǎn)Intent,當(dāng)一個(gè)Acitivity需要啟動(dòng)另一個(gè)Activity時(shí),通過Intent來表達(dá)自己的意圖,告知系統(tǒng)啟動(dòng)哪個(gè)Activity,本文給大家詳細(xì)講解,需要的朋友可以參考下2022-10-10實(shí)例詳解Android 獲取短信會(huì)話列表
本文通過實(shí)例詳解android獲取短信會(huì)話列表的全部?jī)?nèi)容,涉及到android獲取短信列表的相關(guān)知識(shí),對(duì)android會(huì)話列表相關(guān)知識(shí)感興趣的朋友一起學(xué)習(xí)吧2015-12-12