1. 程式人生 > >Android 自己定義控件實現刮刮卡效果 真的就僅僅是刮刮卡麽

Android 自己定義控件實現刮刮卡效果 真的就僅僅是刮刮卡麽

pat 微信 protect 有一點 高仿 記錄 ren bounds size

轉載請標明出處:http://blog.csdn.net/lmj623565791/article/details/40162163 , 本文出自:【張鴻洋的博客】

非常久以前也過一個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();

	}

	@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);
		// 設置畫筆
		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);
	}

	@Override
	protected void onDraw(Canvas canvas)
	{
		drawPath();
		canvas.drawBitmap(mBitmap, 0, 0, null);

	}

	/**
	 * 繪制線條
	 */
	private void drawPath()
	{
		mCanvas.drawPath(mPath, mOutterPaint);

	}

	@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;
	}

}

代碼量比較少。我們在內存中搞了一個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、繪制遮蓋層

事實上遮蓋層就是一個顏色:

@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);
		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裏面也要做一點改動

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

好了。到此完畢。事實上就加入了幾行代碼,就完畢了我們簡易畫板到刮刮卡的轉變;

以下看效果:

技術分享

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

到此我們的刮刮卡的原理,以及初步的實現結束了~~還是非常easy的~接下來就是興許的完好工作


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);
	}
	
	@Override
	protected 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;

		@Override
		public 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時。我們直接展現刮獎區

@Override
	protected 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 (一) 。


源代碼點擊下載

---------------------------------------------------------------------------------------------------------

建了一個QQ群,方便大家交流。

群號:55032675

----------------------------------------------------------------------------------------------------------

博主部分視頻已經上線,假設你不喜歡枯燥的文本,請猛戳(初錄。期待您的支持):

1、高仿微信5.2.1主界面及消息提醒

2、高仿QQ5.0側滑



Android 自己定義控件實現刮刮卡效果 真的就僅僅是刮刮卡麽