1. 程式人生 > >自定義控制元件(6)---PorterDuffXfermode圖形過濾器之橡皮擦應用

自定義控制元件(6)---PorterDuffXfermode圖形過濾器之橡皮擦應用


activity_main.xml

    <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.imooc.guaguaka.view.GuaGuaKa  
            android:layout_width="fill_parent"  
            android:layout_height="fill_parent" />  
      
    </RelativeLayout>  

MainActivity
package com.imooc.guaguaka;

import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;

public class MainActivity extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
	}

}

GuaGuaKa
package com.imooc.guaguaka.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.Paint.Style;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

public class GuaGuaKa extends View {

	/** 記憶體中建立的Canvas */
	private Canvas mCanvas;
	/** 繪製線條的Paint,即使用者手指繪製Path */
	private Paint mOutterPaint = new Paint();
	/** 記錄使用者繪製的Path */
	private Path mPath = new Path();
	/** mCanvas繪製內容在其上 */
	private Bitmap mBitmap;

	private int mLastX;
	private int mLastY;

	/** 繪製字型 */
	private Paint mBackPint = new Paint();
	private Rect mTextBound = new Rect();
	private String mText = "500,0000,000";

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

	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		super.onMeasure(widthMeasureSpec, heightMeasureSpec);

		int width = getMeasuredWidth();
		int height = getMeasuredHeight();
		mBitmap = Bitmap.createBitmap(width, height, Config.ARGB_8888);
		// 在canvas初始化的時候就傳入了一個空的bitmap
		// 最後canvas中繪畫的內容都被繪製到了bitmap中,從而得到了我們需要的bitmap
		mCanvas = new Canvas(mBitmap);
		setUpBackPaint();
		setOutPaint();

		// 表面繪製一層灰色
		mCanvas.drawColor(Color.parseColor("#c0c0c0"));
	}

	private void setOutPaint() {
		// 設定畫筆
		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);
	}

	private void setUpBackPaint() {
		mBackPint.setStyle(Style.FILL);
		mBackPint.setTextScaleX(2f);
		mBackPint.setColor(Color.DKGRAY);
		mBackPint.setTextSize(22);
		// getTextBounds(String text, int start, int end, Rect bounds)
		mBackPint.getTextBounds(mText, 0, mText.length(), mTextBound);
	}

	@Override
	protected void onDraw(Canvas canvas) {
		canvas.drawText(mText, getWidth() / 2 - mTextBound.width() / 2,
				getHeight() / 2 + mTextBound.height() / 2, mBackPint);

		mOutterPaint
				.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
		mCanvas.drawPath(mPath, mOutterPaint);
		canvas.drawBitmap(mBitmap, 0, 0, null);

	}

	@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:

		        mPath.lineTo(x, y);

			mLastX = x;
			mLastY = y;
			break;
		}

		invalidate();
		return true;
	}

}

*************************************************二*******************************************


activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#FFFFFF"
    android:orientation="vertical" >

    <com.aigestudio.customviewdemo.views.EraserView
        android:id="@+id/main_cv"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>

MainActivity
package com.aigestudio.customviewdemo.activities;

import android.app.Activity;
import android.os.Bundle;

import com.aigestudio.customviewdemo.R;

/**
 * 主介面
 */
public class MainActivity extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
	}
}

MeasureUtil
package com.aigestudio.customviewdemo.utils;

import android.app.Activity;
import android.util.DisplayMetrics;

/**
 * 測繪工具類
 */
public final class MeasureUtil {
	/**
	 * 獲取螢幕尺寸
	 * 
	 * @param activity
	 *            Activity
	 * @return 螢幕尺寸畫素值,下標為0的值為寬,下標為1的值為高
	 */
	public static int[] getScreenSize(Activity activity) {
		DisplayMetrics metrics = new DisplayMetrics();
		activity.getWindowManager().getDefaultDisplay().getMetrics(metrics);
		return new int[] { metrics.widthPixels, metrics.heightPixels };
	}
}

EraserView
package com.aigestudio.customviewdemo.views;

import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
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 com.aigestudio.customviewdemo.R;
import com.aigestudio.customviewdemo.utils.MeasureUtil;

/**
 * 橡皮檫View
 */
public class EraserView extends View {
	private static final int MIN_MOVE_DIS = 5;// 最小的移動距離:如果我們手指在螢幕上的移動距離小於此值則不會繪製

	private Bitmap fgBitmap, bgBitmap;// 前景橡皮擦的Bitmap和背景我們底圖的Bitmap
	private Canvas mCanvas;// 繪製橡皮擦路徑的畫布
	private Paint mPaint;// 橡皮檫路徑畫筆
	private Path mPath;// 橡皮擦繪製路徑

	private int screenW, screenH;// 螢幕寬高
	private float preX, preY;// 記錄上一個觸控事件的位置座標

	public EraserView(Context context, AttributeSet set) {
		super(context, set);

		// 計算引數
		cal(context);

		// 初始化物件
		init(context);
	}

	/**
	 * 計算引數
	 * 
	 * @param context
	 *            上下文環境引用
	 */
	private void cal(Context context) {
		// 獲取螢幕尺寸陣列
		int[] screenSize = MeasureUtil.getScreenSize((Activity) context);

		// 獲取螢幕寬高
		screenW = screenSize[0];
		screenH = screenSize[1];
	}

	/**
	 * 初始化物件
	 */
	private void init(Context context) {
		// 例項化路徑物件
		mPath = new Path();

		// 例項化畫筆並開啟其抗鋸齒和抗抖動
		mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);

		// 設定畫筆透明度為0是關鍵!我們要讓繪製的路徑是透明的,然後讓該路徑與前景的底色混合“摳”出繪製路徑
		mPaint.setARGB(128, 255, 0, 0);

		// 設定混合模式為DST_IN
		mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));

		// 設定畫筆風格為描邊
		mPaint.setStyle(Paint.Style.STROKE);

		// 設定路徑結合處樣式
		mPaint.setStrokeJoin(Paint.Join.ROUND);

		// 設定筆觸型別
		mPaint.setStrokeCap(Paint.Cap.ROUND);

		// 設定描邊寬度
		mPaint.setStrokeWidth(50);

		// 生成前景圖Bitmap
		fgBitmap = Bitmap.createBitmap(screenW, screenH, Config.ARGB_8888);

		// 將其注入畫布
		mCanvas = new Canvas(fgBitmap);

		// 繪製畫布背景為中性灰
		mCanvas.drawColor(0xFF808080);

		// 獲取背景底圖Bitmap
		bgBitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.a4);

		// 縮放背景底圖Bitmap至螢幕大小
		bgBitmap = Bitmap.createScaledBitmap(bgBitmap, screenW, screenH, true);
	}

	@Override
	protected void onDraw(Canvas canvas) {
		// 繪製背景
		canvas.drawBitmap(bgBitmap, 0, 0, null);

		// 繪製前景
		canvas.drawBitmap(fgBitmap, 0, 0, null);

		/*
		 * 這裡要注意canvas和mCanvas是兩個不同的畫布物件
		 * 當我們在螢幕上移動手指繪製路徑時會把路徑通過mCanvas繪製到fgBitmap上
		 * 每當我們手指移動一次均會將路徑mPath作為目標影象繪製到mCanvas上,而在上面我們先在mCanvas上繪製了中性灰色
		 * 兩者會因為DST_IN模式的計算只顯示中性灰,但是因為mPath的透明,計算生成的混合影象也會是透明的
		 * 所以我們會得到“橡皮擦”的效果
		 */
		mCanvas.drawPath(mPath, mPaint);
	}

	/**
	 * View的事件將會在7/12詳解
	 */
	@Override
	public boolean onTouchEvent(MotionEvent event) {
		/*
		 * 獲取當前事件位置座標
		 */
		float x = event.getX();
		float y = event.getY();

		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:// 手指接觸螢幕重置路徑
			mPath.reset();
			mPath.moveTo(x, y);
			preX = x;
			preY = y;
			break;
		case MotionEvent.ACTION_MOVE:// 手指移動時連線路徑
			float dx = Math.abs(x - preX);
			float dy = Math.abs(y - preY);
			if (dx >= MIN_MOVE_DIS || dy >= MIN_MOVE_DIS) {
				mPath.quadTo(preX, preY, (x + preX) / 2, (y + preY) / 2);
				preX = x;
				preY = y;
			}
			break;
		}

		// 重繪檢視
		invalidate();
		return true;
	}
}