1. 程式人生 > >自定義控制元件之繪圖篇(三):區域(Range)

自定義控制元件之繪圖篇(三):區域(Range)

前言:最近幾天對畫圖的研究有些緩慢,專案開始寫程式碼了,只能在晚上空閒的時候捯飭一下自己的東西,今天給大家講講區域的相關知識,已經想好後面兩篇的內容了,這幾天有時間趕緊寫出來給大家。有關介面開發的東東內容確實比較多,慢慢來吧,總有一天會不一樣。

我自己的一句警言,送給大家:

想要跟別人不一樣,你就要跟別人不一樣。----- Harvic 

相關文章:

一、構造Region

1、基本建構函式

 public Region()  //建立一個空的區域  
 public Region(Region region) //拷貝一個region的範圍  
 public Region(Rect r)  //建立一個矩形的區域  
 public Region(int left, int top, int right, int bottom) //建立一個矩形的區域  


上面的四個建構函式,第一個還要配合其它函式使用,暫時不提。
第二個建構函式是通過其它的Region來複制一個同樣的Region變數
第三個,第四個才是正規常的,根據一個矩形或矩形的左上角和右下角點構造出一個矩形區域

2、間接構造

 public void setEmpty()  //置空
 public boolean set(Region region)   
 public boolean set(Rect r)   
 public boolean set(int left, int top, int right, int bottom)   
 public boolean setPath(Path path, Region clip)//後面單獨講


這是Region所具有的一系列Set方法,我這裡全部列了出來,下面一一對其講解:
注意:無論呼叫Set系列函式的Region是不是有區域值,當呼叫Set系列函式後,原來的區域值就會被替換成Set函式裡的區域。
SetEmpty():從某種意義上講置空也是一個建構函式,即將原來的一個區域變數變成了一個空變數,要再利用其它的Set方法重新構造區域。
set(Region region):利用新的區域值來替換原來的區域
set(Rect r):利用矩形所代表的區域替換原來的區域
set(int left, int top, int right, int bottom):同樣,根據矩形的兩個點構造出矩形區域來替換原來的區域值
setPath(Path path, Region clip):根據路徑的區域與某區域的交集,構造出新區域,這個後面具體講解


舉個小例子,來說明一個Set系列函式的替換概念:

關於重寫新建一個類,並派生自view,並且要重寫OnDraw函式的問題我就不再講了,有問題的同學,可以參考下《android Graphics(一):概述及基本幾何圖形繪製》,當然最後我也會給出相關的原始碼,直接看原始碼也行。

下面寫了一個函式,先把Set函式註釋起來,看看畫出來的區域的位置,然後開啟Set函式,然後再看畫出來的區域
注:裡面有個函式drawRegion(Canvas canvas,Region rgn,Paint paint),只知道它可以畫出指定的區域就可以了,具體裡面是什麼意思,後面我們再仔細講。

public class MyRegionView extends View {

	public MyRegionView(Context context) {
		super(context);
		// TODO Auto-generated constructor stub
	}
	
	@Override
	protected void onDraw(Canvas canvas) {
		// TODO Auto-generated method stub
		super.onDraw(canvas);
		
		//初始化畫筆
		Paint paint = new Paint();
		paint.setColor(Color.RED);
		paint.setStyle(Style.FILL);
		paint.setStrokeWidth(2);
		
		Region rgn = new Region(10,10,100,100);

//		rgn.set(100, 100, 200, 200);
		drawRegion(canvas, rgn, paint);
		
	}
	
	//這個函式不懂沒關係,下面會細講
	private void drawRegion(Canvas canvas,Region rgn,Paint paint)
	{
		RegionIterator iter = new RegionIterator(rgn);
		Rect r = new Rect();
		
		while (iter.next(r)) {
		  canvas.drawRect(r, paint);
		} 
	}

}
看下效果:
                            未開啟Set函式時                                                                                           

                       使用Set函式後,替換為新區域

3、使用SetPath()構造不規則區域

boolean setPath (Path path, Region clip)

引數說明:
Path path:用來構造的區域的路徑
Region clip:與前面的path所構成的路徑取交集,並將兩交集設定為最終的區域

由於路徑有很多種構造方法,而且可以輕意構造出非矩形的路徑,這就擺脫了前面的建構函式只能構造矩形區域的限制。但這裡有個問題是要指定另一個區域來取共同的交集,當然如果想顯示路徑構造的區域,Region clip引數可以傳一個比Path範圍大的多的區域,取完交集之後,當然是Path引數所對應的區域嘍。機智的孩子。

下面,先構造一個橢圓路徑,然後在SetPath時,傳進去一個比Path小的矩形區域,讓它們兩個取交集

public class MyRegionView extends View {

	public MyRegionView(Context context) {
		super(context);
		// TODO Auto-generated constructor stub
	}
	
	@Override
	protected void onDraw(Canvas canvas) {
		// TODO Auto-generated method stub
		super.onDraw(canvas);
		//初始化Paint
		Paint paint = new Paint();
		paint.setColor(Color.RED);
		paint.setStyle(Style.FILL);
		paint.setStrokeWidth(2);
		//構造一個橢圓路徑
		Path ovalPath = new Path();
		RectF rect =  new RectF(50, 50, 200, 500);  
		ovalPath.addOval(rect, Direction.CCW);
		//SetPath時,傳入一個比橢圓區域小的矩形區域,讓其取交集
		Region rgn = new Region();
		rgn.setPath(ovalPath,new  Region(50, 50, 200, 200));
		//畫出路徑
		drawRegion(canvas, rgn, paint);
	}
	
	//這個函式不懂沒關係,下面會細講
	private void drawRegion(Canvas canvas,Region rgn,Paint paint)
	{
		RegionIterator iter = new RegionIterator(rgn);
		Rect r = new Rect();
		
		while (iter.next(r)) {
		  canvas.drawRect(r, paint);
		} 
	}
}
結果如下:

二、矩形集列舉區域——RegionIterator類

對於特定的區域,我們都可以使用多個矩形來表示其大致形狀。事實上,如果矩形足夠小,一定數量的矩形就能夠精確表示區域的形狀,也就是說,一定數量的矩形所合成的形狀,也可以代表區域的形狀。RegionIterator類,實現了獲取組成區域的矩形集的功能,其實RegionIterator類非常簡單,總共就兩個函式,一個建構函式和一個獲取下一個矩形的函式;
RegionIterator(Region region) //根據區域構建對應的矩形集
boolean next(Rect r) //獲取下一個矩形,結果儲存在引數Rect r 中

由於在Canvas中沒有直接繪製Region的函式,我們想要繪製一個區域,就只能通過利用RegionIterator構造矩形集來逼近的顯示區域。用法如下:

private void drawRegion(Canvas canvas,Region rgn,Paint paint)
{
	RegionIterator iter = new RegionIterator(rgn);
	Rect r = new Rect();
	
	while (iter.next(r)) {
	  canvas.drawRect(r, paint);
	} 
}
上面我們也都看到了它的用法,首先,根據區域構建一個矩形集,然後利用next(Rect r)來逐個獲取所有矩形,繪製出來,最終得到的就是整個區域,如果我們將上面的畫筆Style從FILL改為STROKE,重新繪製橢圓路徑,會看得更清楚。


三、區域的合併、交叉等操作

無論是區域還是矩形,都會涉及到與另一個區域的一些操作,比如取交集、取並集等,涉及到的函式有:

public final boolean union(Rect r)   
public boolean op(Rect r, Op op) {  
public boolean op(int left, int top, int right, int bottom, Op op)   
public boolean op(Region region, Op op)   
public boolean op(Rect rect, Region region, Op op)   

除了Union(Rect r)是指定合併操作以外,其它四個op()建構函式,都是指定與另一個區域的操作。其中最重要的指定Op的引數,Op的引數有下面四個:

假設用region1  去組合region2   
public enum Op {  
        DIFFERENCE(0), //最終區域為region1 與 region2不同的區域  
        INTERSECT(1), // 最終區域為region1 與 region2相交的區域  
        UNION(2),      //最終區域為region1 與 region2組合一起的區域  
        XOR(3),        //最終區域為region1 與 region2相交之外的區域  
        REVERSE_DIFFERENCE(4), //最終區域為region2 與 region1不同的區域  
        REPLACE(5); //最終區域為為region2的區域  
 } 
至於這六個引數的具體意義,後面給個具體的圖給大家顯示出來,先舉個取交集的例子。

效果圖:


先構造兩個相交叉的矩形,並畫出它們的輪廓
//構造兩個矩形
Rect rect1 = new Rect(100,100,400,200);
Rect rect2 = new Rect(200,0,300,300);

//構造一個畫筆,畫出矩形輪廓
Paint paint = new Paint();
paint.setColor(Color.RED);
paint.setStyle(Style.STROKE);
paint.setStrokeWidth(2);

canvas.drawRect(rect1, paint);
canvas.drawRect(rect2, paint);
然後利用上面的兩年rect,(rect1和rect2)來構造區域,並在rect1的基礎上取與rect2的交集
//構造兩個Region
Region region = new Region(rect1);
Region region2= new Region(rect2);

//取兩個區域的交集		
region.op(region2, Op.INTERSECT);
最後構造一個填充畫筆,將所選區域用綠色填充起來
Paint paint_fill = new Paint();
paint_fill.setColor(Color.GREEN);
paint_fill.setStyle(Style.FILL);
drawRegion(canvas, region, paint_fill);
全部程式碼為:
/**
 * created by harvic
 * 2014/9/4
 */
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Region;
import android.graphics.Paint.Style;
import android.graphics.Region.Op;
import android.graphics.RegionIterator;
import android.view.View;

public class MyRegionView extends View {

	public MyRegionView(Context context) {
		super(context);
		// TODO Auto-generated constructor stub
	}
	
	@Override
	protected void onDraw(Canvas canvas) {
		// TODO Auto-generated method stub
		super.onDraw(canvas);
		
		//構造兩個矩形
		Rect rect1 = new Rect(100,100,400,200);
		Rect rect2 = new Rect(200,0,300,300);
		
		//構造一個畫筆,畫出矩形輪廓
		Paint paint = new Paint();
		paint.setColor(Color.RED);
		paint.setStyle(Style.STROKE);
		paint.setStrokeWidth(2);
		
		canvas.drawRect(rect1, paint);
		canvas.drawRect(rect2, paint);
		
		
		
		//構造兩個Region
		Region region = new Region(rect1);
		Region region2= new Region(rect2);

		//取兩個區域的交集		
		region.op(region2, Op.INTERSECT);
		
		//再構造一個畫筆,填充Region操作結果
		Paint paint_fill = new Paint();
		paint_fill.setColor(Color.GREEN);
		paint_fill.setStyle(Style.FILL);
		drawRegion(canvas, region, paint_fill);

	}
	

private void drawRegion(Canvas canvas,Region rgn,Paint paint)
{
	RegionIterator iter = new RegionIterator(rgn);
	Rect r = new Rect();
	
	while (iter.next(r)) {
	  canvas.drawRect(r, paint);
	} 
}
}
其它引數的操作與這個類似,其實只需要改動region.op(region2, Op.INTERSECT);的Op引數值即可,下面就不再一一列舉,給出操作後的對比圖。


四、其它一些方法

Region類除了上面的一些重要的方法以外,還有一些比較容易理解的方法,我就不再一一列舉用法了,下面一併列出給大家
/**幾個判斷方法*/  
public native boolean isEmpty();//判斷該區域是否為空  
public native boolean isRect(); //是否是一個矩陣  
public native boolean isComplex();//是否是多個矩陣組合  
  
  
/**一系列的getBound方法,返回一個Region的邊界*/  
public Rect getBounds()   
public boolean getBounds(Rect r)   
public Path getBoundaryPath()   
public boolean getBoundaryPath(Path path)   
  
  
/**一系列的判斷是否包含某點 和是否相交*/  
public native boolean contains(int x, int y);//是否包含某點  
public boolean quickContains(Rect r)   //是否包含某矩形
public native boolean quickContains(int left, int top, int right,  
                                        int bottom) //是否沒有包含某矩陣形 
 public boolean quickReject(Rect r) //是否沒和該矩形相交  
 public native boolean quickReject(int left, int top, int right, int bottom); //是否沒和該矩形相交  
 public native boolean quickReject(Region rgn);  //是否沒和該矩形相交  
  
/**幾個平移變換的方法*/  
public void translate(int dx, int dy)   
public native void translate(int dx, int dy, Region dst);  
public void scale(float scale) //hide  
public native void scale(float scale, Region dst);//hide  

本篇所涉及到的程式碼,我集合在了一個工程中,大家可以下載