概述
詳細
一、簡介:
趁着元旦假期之際,首先在這里,我祝福大家在新的2019年都一個個的新健康,新收入,新順利,新如意!!!
上一偏,我介紹了用Xfermode實現自定義圓角和橢圓圖片view的博文《Android實現自定義圓形、圓角和橢圓ImageView(使用Xfermode圖形渲染方法)》,
今天我們來看看如何實現電商app里常用到的刮刮卡效果的view組件,其實原理和實現圓角圖片的差不多,都是使用Xfermode渲染模式來實現的。
基本原理步驟是這樣的:
1.首先繪制下層(即Dst層),即:刮刮卡背景圖層
2.設置Xfermode模式為DST_OUT
3.繪制刮掃的路徑,繪制上層
這樣通過這三步,就可以達到實現刮刮卡的效果啦,因為 使用了DST_OUT模式,這樣就是取上下層交集的下層部分,下面我們看看具體效果吧
二、效果圖:



三、Xfermode渲染模式簡介:
xfermode影響在Canvas已經有的圖像上繪制新的顏色的方式
* 正常的情況下,在圖像上繪制新的形狀,如果新的Paint不是透明的,那么會遮擋下面的顏色.
* 如果新的Paint是透明的,那么會被染成下面的顏色
下面的Xfermode子類可以改變這種行為:
AvoidXfermode 指定了一個顏色和容差,強制Paint避免在它上面繪圖(或者只在它上面繪圖)。
PixelXorXfermode 當覆蓋已有的顏色時,應用一個簡單的像素XOR操作。
PorterDuffXfermode 這是一個非常強大的轉換模式,使用它,可以使用圖像合成的16條Porter-Duff規則的任意一條來控制Paint如何與已有的Canvas圖像進行交互。
這里不得不提到那個經典的圖:

上面的16種模式的說明如下:
從上面我們可以看到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
取兩圖層全部區域,交集部分變為透明色
四、自定義刮刮卡效果View組件的實現:
1.繪制下層的背景圖層
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// TODO Auto-generated method stub
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = getMeasuredWidth();
int height = getMeasuredHeight();
//初始化bitmap
mBitmap = Bitmap.createBitmap(width, height, Config.ARGB_8888);
//初始化canvas
mCanvas = new Canvas(mBitmap);
//設置畫筆的一些屬性
setOutterPaint();
setOutBmpPaint();
setTextPaint();
//繪制一層刮刮卡圓角背景圖層
mCanvas.drawRoundRect(new RectF(0,0,width,height), 30, 30, mOutBmpPaint);
mCanvas.drawBitmap(mOutterBitmap, null, new RectF(0,0,width,height),null);
}
2.設置Xfermode模式並繪制上層路徑層
/**
* 設置Xfermode模式為DST_OUT,並繪制掃的路徑
*/
private void drawPath() {
// TODO Auto-generated method stub
mOutterPaint.setXfermode(new PorterDuffXfermode(Mode.DST_OUT));
mCanvas.drawPath(mPath, mOutterPaint);
}
3.最后在ondraw里面繪制出來:
@Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
//繪制文字
canvas.drawText(mText, getWidth()/2-mTextBound.width()/2, getHeight()/2+mTextBound.height()/2, mTextPaint);
//刮掃完成回調
if(mCompleted){
if(null != mOnCompleteListener){
mOnCompleteListener.complete();
}
}
//判斷是否完成,如果完成了就不繪制遮蓋層
if(!mCompleted){
drawPath();
canvas.drawBitmap(mBitmap,0,0,null);
}
}
4.手勢觸摸記錄路徑的實現:
@Override
public boolean onTouchEvent(MotionEvent event) {
// TODO Auto-generated method stub
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;
case MotionEvent.ACTION_UP:
new Thread(mRunnable).start();
break;
default:
break;
}
invalidate();
return true;
}
5. 刮掃區域面積的計算以及刮掃完成的實現,為了不影響繪制,單獨在子線程里實現該部分
/**
* 起一個線程來計算已經掃的面積及占總區域的比例
* 根據區域來判斷是否完成
*/
private Runnable mRunnable = new Runnable(){
@Override
public void run() {
int w = getWidth();
int h = getHeight();
float wipeArea = 0;
float totalArea = w * h ;
Bitmap bitmap = mBitmap;
int[] mPixels = new int[w * h];
//獲取bitmap的所有像素信息
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.v("czm", "percent="+percent);
if(percent > 70){
//清除圖層區域
mCompleted = true;
postInvalidate();
}
}
};
};
到此,自定義刮刮卡效果View的核心模塊代碼都介紹完畢了。下面就看看使用該view的布局的實現,其實很簡單。
五、視圖布局的實現
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.czm.xcguaguaka.XCGuaguakaView
android:id="@+id/ggk"
android:layout_width="300dp"
android:layout_height="100dp"
android:layout_centerInParent="true" />
</RelativeLayout>
六、使用並測試自定義刮刮卡效果View
上面直接繪制的自定義View寫完了,下面就是使用這個自定義的View了,使用方法和普通的View一樣,當作普通控件使用即可。
package com.czm.xcguaguaka;
import com.czm.xcguaguaka.XCGuaguakaView.OnCompleteListener;
import android.app.Activity;
import android.app.ActionBar;
import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import android.os.Build;
/**
* 使用並測試自定義刮刮卡效果View
* @author caizhiming
*
*/
public class MainActivity extends Activity {
private XCGuaguakaView xcGuaguakaView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
xcGuaguakaView = (XCGuaguakaView)findViewById(R.id.ggk);
xcGuaguakaView.setOnCompleteListener(new OnCompleteListener() {
@Override
public void complete() {
// TODO Auto-generated method stub
Toast.makeText(getApplicationContext(), "您已經刮的差不多啦", Toast.LENGTH_SHORT).show();
}
});
}
}
七、項目代碼結構目錄圖

