Android 2D Graphics學習(二)、Canvas篇2、Canvas裁剪和Region、RegionIterator
請先閱讀:
Android 2D Graphics學習(一)、android.graphics介紹
Android 2D Graphics學習(二)、Canvas篇1、Canvas基本使用
canvas 還提供裁剪的功能。
裁剪功能由Canvas提供的一系列的clip...方法 和quickReject方法來完成。 前面已經提到,真正提供可繪制區域的是Canvas內部的mutable bitmap。 Canvas更像是一個圖層,我們只能在這上面的圖層來繪制東西。
1、首先介紹Region類
Region,中文意思即區域的意思,它表示的是canvas圖層上的某一塊封閉的區域。/**構造方法*/ public Region() //創建一個空的區域 public Region(Region region) //拷貝一個region的范圍 public Region(Rect r) //創建一個矩形的區域 public Region(int left, int top, int right, int bottom) //創建一個矩形的區域 /**一系列set方法,這些set方法,和上面構造方法形式差不多*/ public void setEmpty() { public boolean set(Region region) public boolean set(Rect r) public boolean set(int left, int top, int right, int bottom) /*往一個Region中添加一個Path只有這種方法,參數clip代表這個整個Region的區域,在在里面裁剪出path范圍的區域*/ public boolean setPath(Path path, Region clip) //用指定的Path和裁剪范圍構建一個區域 /**幾個判斷方法*/ 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 /**一系列組合的方法*/ 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)
上面幾乎是Region的所有API,很好理解,主要說明一下最后的一組關于Region組合的方式。組合即當前的Region和另外的一個Region組合,可以用不同的Op方式來進行組合。
Op是一個枚舉,定義在Region類中。
假設用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的區域 }
ApiDemo中已經提供了一個關于組合的例子,在最后面給出。
Android還提供了一個RegionIterator來對Region中的所有矩陣進行迭代,可以使用該類,獲得某個Region的所有矩陣。比較簡單。
2、什么是裁剪
裁剪Clip,即裁剪Canvas圖層,我們繪制的東西,只能在裁剪區域的范圍能才能顯示出來。
@Override protected void onDraw(Canvas canvas) { Paint paint=new Paint(); canvas.save(); canvas.clipRect(new Rect(100,100,300,300)); canvas.drawColor(Color.BLUE);//裁剪區域的rect變為藍色 canvas.drawRect(new Rect(0,0,100,100), paint);//在裁剪的區域之外,不能顯示 canvas.drawCircle(150,150, 50, paint);//在裁剪區域之內,能顯示 canvas.restore(); }
裁剪并不像Matrix變換,它相對于mutable bitmap的坐標是不會改變的。所以超出裁剪區域的繪制不會被顯示
3、裁剪的保存和回滾
在之前已經提到了,canvas.save()和canvas.restore()不僅對matrix有效,同樣對clip有類似的效果。
4、裁剪的方式
Canvas提供了三種裁剪的方式:
1、最基本的clipRect,裁剪一個矩形
2、clipPath,裁剪Path包括的范圍,Path所包括的范圍不是空的才有效。
3、clipRegion。
Region在前面已經介紹過了,其實Region就是一個對區域組合的一個封裝。但是它和clipRect和clipPath的最大區別在于下面:
clipRect() and clipPath() which transform their arguments by the current matrix, clipRegion() assumes its argument is already in the coordinate system of the current layer's bitmap, and so not transformation is performed.
與clipRect和clipPath要使用當前的matrix進行變換不同。clipRegion不會進行轉換。也就是說canvas的matrix對clipRegion沒有影響。
Paint paint=new Paint(); canvas.scale(0.5f, 0.5f); canvas.save(); canvas.clipRect(new Rect(100,100,200,200));//裁剪區域實際大小為50*50 canvas.drawColor(Color.RED); canvas.restore(); canvas.drawRect(new Rect(0,0,100,100), paint);//矩形實際大小為50*50 canvas.clipRegion(new Region(new Rect(300,300,400,400)));//裁剪區域實際大小為100*100 canvas.drawColor(Color.BLACK);
可以看到,Canvas的變換 對clipRegion沒有作用。
ApiDemo中關于組合的例子:
public class Clipping extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(new SampleView(this)); } private static class SampleView extends View { private Paint mPaint; private Path mPath; public SampleView(Context context) { super(context); setFocusable(true); mPaint = new Paint(); mPaint.setAntiAlias(true); mPaint.setStrokeWidth(6); mPaint.setTextSize(16); mPaint.setTextAlign(Paint.Align.RIGHT); mPath = new Path(); } private void drawScene(Canvas canvas) { canvas.clipRect(0, 0, 100, 100); canvas.drawColor(Color.WHITE); mPaint.setColor(Color.RED); canvas.drawLine(0, 0, 100, 100, mPaint); mPaint.setColor(Color.GREEN); canvas.drawCircle(30, 70, 30, mPaint); mPaint.setColor(Color.BLUE); canvas.drawText("Clipping", 100, 30, mPaint); } @Override protected void onDraw(Canvas canvas) { canvas.drawColor(Color.GRAY); canvas.save(); canvas.translate(10, 10); drawScene(canvas); canvas.restore(); canvas.save(); canvas.translate(160, 10); canvas.clipRect(10, 10, 90, 90); canvas.clipRect(30, 30, 70, 70, Region.Op.DIFFERENCE); drawScene(canvas); canvas.restore(); canvas.save(); canvas.translate(10, 160); mPath.reset(); canvas.clipPath(mPath); // makes the clip empty mPath.addCircle(50, 50, 50, Path.Direction.CCW); canvas.clipPath(mPath, Region.Op.REPLACE); drawScene(canvas); canvas.restore(); canvas.save(); canvas.translate(160, 160); canvas.clipRect(0, 0, 60, 60); canvas.clipRect(40, 40, 100, 100, Region.Op.UNION); drawScene(canvas); canvas.restore(); canvas.save(); canvas.translate(10, 310); canvas.clipRect(0, 0, 60, 60); canvas.clipRect(40, 40, 100, 100, Region.Op.XOR); drawScene(canvas); canvas.restore(); canvas.save(); canvas.translate(160, 310); canvas.clipRect(0, 0, 60, 60); canvas.clipRect(40, 40, 100, 100, Region.Op.REVERSE_DIFFERENCE); drawScene(canvas); canvas.restore(); }}
}</pre>
效果圖:
5、裁剪的一個小用處
public class ClippingRegion extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(new SampleView(this)); } private class SampleView extends View { private Bitmap mBitmap; private int limitLength = 0; private int width; private int heigth; private static final int CLIP_HEIGHT = 30; private boolean status = HIDE;//顯示還是隱藏的狀態,最開始為HIDE private static final boolean SHOW = true;//顯示圖片 private static final boolean HIDE = false;//隱藏圖片 public SampleView(Context context) { super(context); mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.image1); limitLength = width = mBitmap.getWidth(); heigth = mBitmap.getHeight(); } @Override protected void onDraw(Canvas canvas) { Region region = new Region(); int i = 0; while (i CLIP_HEIGHT <= heigth) {//計算clip的區域 if (i % 2 == 0) { region.union(new Rect(0, i CLIP_HEIGHT, limitLength, (i + 1) CLIP_HEIGHT)); } else { region.union(new Rect(width - limitLength, i CLIP_HEIGHT, width, (i + 1)* CLIP_HEIGHT)); } i++; } canvas.clipRegion(region); canvas.drawBitmap(mBitmap, 0, 0, new Paint()); if (status == HIDE) {//如果此時是隱藏 limitLength -= 5; if(limitLength<=0) status=SHOW; } else {//如果此時是顯示 limitLength += 5; if(limitLength>=width) status=HIDE; } invalidate(); } }
}</pre>
效果就是一直這樣交叉的隱藏和顯示圖片