概述
詳細
一、簡介:
趁着元旦假期之際,首先在這里,我祝福大家在新的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(); } }); } }
七、項目代碼結構目錄圖