自定義控件教程:
1宅楞,http://blog.csdn.net/aigestudio/article/details/41212583
2,http://blog.csdn.net/aigestudio/article/details/41316141
3种蝶,http://blog.csdn.net/aigestudio/article/details/41447349
4趟佃,http://blog.csdn.net/aigestudio/article/details/41960507
5客税,http://blog.csdn.net/aigestudio/article/details/42677973
6惨恭,http://blog.csdn.net/aigestudio/article/details/42989325
1,Paint類
private void init() {
paint = new Paint();
paint.setAntiAlias(true);//反鋸齒
paint.setColor(Color.RED);
/*
* 設(shè)置畫筆樣式為描邊,圓環(huán)嘛……當(dāng)然不能填充不然就么意思了
*
* 畫筆樣式分三種:
* 1.Paint.Style.STROKE:描邊
* 2.Paint.Style.FILL_AND_STROKE:描邊并填充
* 3.Paint.Style.FILL:填充
*/
paint.setStyle(Paint.Style.FILL);
/*
* 設(shè)置描邊的粗細(xì)通熄,單位:像素px
* 注意:當(dāng)setStrokeWidth(0)的時(shí)候描邊寬度并不為0而是只占一個(gè)像素
*/
paint.setStrokeWidth(paintStrokeWidth);
}
Paint的其他方法:
setStrokeCap(Paint.Cap cap)
方法唆涝,該方法用來設(shè)置我們畫筆的筆觸風(fēng)格,上面的例子中我使用的是ROUND唇辨,表示是圓角的筆觸廊酣,那么什么叫筆觸呢,其實(shí)很簡單赏枚,就像我們現(xiàn)實(shí)世界中的筆亡驰,如果你用圓珠筆在紙上戳一點(diǎn),那么這個(gè)點(diǎn)一定是個(gè)圓饿幅,即便很小凡辱,它代表了筆的筆觸形狀,如果我們把一支鉛筆筆尖削成方形的栗恩,那么畫出來的線條會是一條彎曲的“矩形”透乾,這就是筆觸的意思。除了ROUND磕秤,Paint.Cap還提供了另外兩種類型:SQUARE和BUTTsetStrokeJoin(Paint.Join join)
這個(gè)方法用于設(shè)置結(jié)合處的形態(tài)乳乌,就像上面的代碼中我們雖說是花了一條心電線,但是這條線其實(shí)是由無數(shù)條小線拼接成的市咆,拼接處的形狀就由該方法指定汉操。setShadowLayer(float radius, float dx, float dy, int shadowColor)
該方法為我們繪制的圖形添加一個(gè)陰影層效果:
TextPaint專門用戶繪制文字的畫筆
/*
* 初始化文字畫筆
*/
textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG | Paint.SUBPIXEL_TEXT_FLAG);
textPaint.setColor(Color.WHITE);
textPaint.setTextSize(30);
textPaint.setTextAlign(Paint.Align.CENTER);
與畫筆顏色相關(guān)的類
詳情見博客:2,http://blog.csdn.net/aigestudio/article/details/41316141
PorterDuffXfermode類蒙兰,圖形混合模式的意思
2,View類的invalidate()方法,和postInvalidate()方法
在Android中提供了一個(gè)叫invalidate()的方法來讓我們重繪我們的View磷瘤。poistInvalidate()方法和invaliadate方法相同其弊,只是它可以在子線程中運(yùn)行.
nvalidate()方法只能在主線程中調(diào)用,而postInvalidate()方法可以在子線程中調(diào)用膀斋。
3,Shader類(著色器)
Shader類呢也是個(gè)灰潮匝牛灰常簡單的類仰担,它有五個(gè)子類,像PathEffect一樣每個(gè)子類都實(shí)現(xiàn)了一種Shader绩社,Shader在三維軟件中我們稱之為著色器摔蓝,其作用嘛就像它的名字一樣是來給圖像著色的或者更通俗的說法是上色!這么說該懂了吧愉耙!再不懂去廁所哭去贮尉!這五個(gè)Shader里最異類的是BitmapShader,因?yàn)橹挥兴窃试S我們載入一張圖片來給圖像著色朴沿,那我們還是先來看看這個(gè)怪胎吧BitmapShader只有一個(gè)含參的構(gòu)造方法BitmapShader (Bitmap bitmap, Shader.TileMode tileX, Shader.TileMode tileY)
BitmapShader (Bitmap bitmap, Shader.TileMode tileX, Shader.TileMode tileY)的第一個(gè)參數(shù)是位圖這個(gè)很顯然猜谚,而后兩個(gè)參數(shù)則分別表示XY方向上的著色模式。
Shader.TileMode里有三種模式:CLAMP赌渣、MIRROR和REPETA魏铅。MIRROR鏡像效果,REPETA重復(fù)坚芜,CLAMP的意思就是邊緣拉伸的意思
4,Canvas 畫布
- canvas.save()览芳;鎖定畫布
- canvas.restore(); 釋放畫布,即將畫布釋放到之前鎖定的位置鸿竖。先調(diào)用sava()方法沧竟,才能調(diào)用restore()方法進(jìn)行釋放。
在Android中我們可以使用Canvas的saveXXX和restoreXXX方法來模擬圖層的類似效果
Canvas我們一直稱其為畫布缚忧,其實(shí)更準(zhǔn)確地說Canvas是一個(gè)容器悟泵,如果把Canvas理解成畫板,那么我們的“層”就像張張夾在畫板上的透明的紙搔谴,而這些紙對應(yīng)到Android則是一個(gè)個(gè)封裝在Canvas中的Bitmap魁袜。
除了save()方法Canvas還給我們提供了一系列的saveLayerXXX方法給我們保存畫布,與save()方法不同的是敦第,saveLayerXXX方法會將所有的操作存到一個(gè)新的Bitmap中而不影響當(dāng)前Canvas的Bitmap峰弹,而save()方法則是在當(dāng)前的Bitmap中進(jìn)行操作,并且只能針對Bitmap的形變和裁剪進(jìn)行操作芜果,saveLayerXXX方法則無所不能鞠呈,當(dāng)然兩者還有很多的不同.
save和saveLayerXXX方法有著本質(zhì)的區(qū)別,saveLayerXXX方法會將所有操作在一個(gè)新的Bitmap中進(jìn)行右钾,而save則是依靠stack棧來進(jìn)行
詳情:http://blog.csdn.net/aigestudio/article/details/42677973
- canvas.translate(x, y);平移畫布蚁吝,就相當(dāng)于將畫布的原點(diǎn)移到x,y的位置 旱爆,即相當(dāng)于坐標(biāo)系移動了。
- canvas.rotate(degrees);旋轉(zhuǎn)畫布窘茁,將畫布按照一定角度進(jìn)行旋轉(zhuǎn)怀伦,即相當(dāng)于坐標(biāo)系進(jìn)行了旋轉(zhuǎn)。
注意:畫布的平移旋轉(zhuǎn)同樣也會影響畫布的自身坐標(biāo)
5山林,Canvas類(畫布)
Canvas所提供的各種方法根據(jù)功能來看大致可以分為幾類房待,第一是以drawXXX為主的繪制方法,第二是以clipXXX為主的裁剪方法驼抹,第三是以scale桑孩、skew、translate和rotate組成的Canvas變換方法框冀,最后一類則是以saveXXX和restoreXXX構(gòu)成的畫布鎖定和還原流椒。
- drawBitmapMesh (Bitmap bitmap, int meshWidth, int meshHeight, float[] verts, int vertOffset, int[] colors, int colorOffset, Paint paint)
很吊毛的方法,但是計(jì)算復(fù)雜度很高明也,所有被沉默了宣虾。(一般不推薦使用,因?yàn)橛?jì)算難度很大)
詳細(xì)原理http://blog.csdn.net/aigestudio/article/details/41960507
drawBitmapMesh是個(gè)很屌毛的方法温数,為什么這樣說呢安岂?因?yàn)樗梢詫itmap做幾乎任何改變,是的帆吻,你沒聽錯域那,是任何,幾乎無所不能猜煮,這個(gè)屌毛方法我曾一度懷疑谷歌那些逗比為何將它屈尊在Canvas下次员,因?yàn)樗鼘itmap的處理實(shí)在在強(qiáng)大了。上一節(jié)我們在講到Matrix的時(shí)候說過Matrix可以對我們的圖像做多種變換王带,實(shí)際上drawBitmapMesh也可以淑蔚,只不過需要一點(diǎn)計(jì)算,比如我們可以使用drawBitmapMesh來模擬錯切skew的效果愕撰。
- Canvas(Bitmap bitmap);含參數(shù)的構(gòu)造刹衫。
可以實(shí)現(xiàn)在一張Bitmap的基礎(chǔ)上進(jìn)行繪制。- canvas.setBitmap(Bitmap bitmap); 作用同含參數(shù)的構(gòu)造.
例如:
ivMain = (ImageView) findViewById(R.id.main_iv);
Bitmap bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
canvas.drawColor(Color.RED);
ivMain.setImageBitmap(bitmap);
- canvas.translate()和rotate()方法:
此類方法是對畫布進(jìn)行平移旋轉(zhuǎn)等操作搞挣。其實(shí)可以理解為對坐標(biāo)軸的旋轉(zhuǎn)平移等操作带迟。
-canvas.clipXXX()方法:對畫布的一種剪切方式,剪切后只能在剪切的部分顯示需要繪制的內(nèi)容囱桨。當(dāng)前畫布被“裁剪”了,只有剪裁的部分能夠出現(xiàn)我們繪制的內(nèi)容仓犬,如果我們所繪制的東西在該區(qū)域外部,即便繪制了你也看不到
clipPath(Path path, Region.Op op)
clipRect(Rect rect, Region.Op op)
clipRect(RectF rect, Region.Op op)
clipRect(float left, float top, float right, float bottom, Region.Op op)
clipRegion(Region region, Region.Op op)
Canvas中有關(guān)裁剪的方法舍肠,你會發(fā)現(xiàn)有一大堆帶有Region.Op參數(shù)的重載方法.要明白這些方法的Region.Op參數(shù)那么首先要了解Region為何物搀继。Region的意思是“區(qū)域”窘面,在Android里呢它同樣表示的是一塊封閉的區(qū)域。
詳情請看 一下8中有關(guān)Region的簡介
-canvas.drawPath(Path path, Paint paint) 繪制路徑叽躯。
-canvas.drawTextOnPath (String text, Path path, float hOffset, float vOffset, Paint paint) ; 繪制文字在Path路徑上财边。
6 Rect和RectF類
Rect和RectF是類似的,只不過RectF中涉及計(jì)算的時(shí)候數(shù)值類型均為float型点骑,兩者均表示一塊規(guī)則矩形制圈。
重要的方法:
- intersect(RectF rectF) / intersect (float left, float top, float right, float bottom) :此方法表示兩個(gè)矩形相交的部分。取兩個(gè)區(qū)域的相交區(qū)域作為最終區(qū)域畔况。
intersect方法的計(jì)算方式是相當(dāng)有趣的,它不是單純地計(jì)算相交而是去計(jì)算相交區(qū)域最近的左上端點(diǎn)和最近的右下端點(diǎn)
public class CanvasView extends View {
private Rect mRect;
public CanvasView(Context context, AttributeSet attrs) {
super(context, attrs);
mRect = new Rect(0, 0, 500, 500);
mRect.intersect(250, 250, 750, 750);
}
@Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.BLUE);
canvas.clipRect(mRect);
canvas.drawColor(Color.RED);
}
注意:黃色線框?yàn)楹笃诩由系妮o助線非程序生成
- union(RectF rect) / union(float left, float top, float right, float bottom) 方法:
union方法與intersect相反慧库,取的是相交區(qū)域最遠(yuǎn)的左上端點(diǎn)作為新區(qū)域的左上端點(diǎn)跷跪,而取最遠(yuǎn)的右下端點(diǎn)作為新區(qū)域的右下端點(diǎn)
- mRect.union(250, 250, 750, 750);
運(yùn)行后我們會看到如下結(jié)果:
是不是覺得不是我們想象中的那樣單純地兩個(gè)區(qū)域相加。此方法是指的左上角的點(diǎn)和右下角的點(diǎn)的最遠(yuǎn)距離齐板。
7 Path 類
此類表示一個(gè)路徑
- lineTo(float x, float y): 將路徑連接至某個(gè)點(diǎn)坐標(biāo)吵瞻。(不適用moveTo方法時(shí)默認(rèn)起點(diǎn)坐標(biāo)為原點(diǎn))。
我們可以考慮多次調(diào)用lineTo方法來繪制更復(fù)雜的圖形.
- moveTo(float x, float y):將Path的起始點(diǎn)移動到某個(gè)特定的點(diǎn)甘磨。(Path默認(rèn)起始點(diǎn)為原點(diǎn))
- close() 此方法含義師閉合曲線橡羞。
// 實(shí)例化路徑
mPath = new Path();
// 移動點(diǎn)至[300,300]
mPath.moveTo(100, 100);
// 連接路徑到點(diǎn)
mPath.lineTo(300, 100);
mPath.lineTo(400, 200);
mPath.lineTo(200, 200);
// 閉合曲線
mPath.close();
- XXXTo() 貝塞爾曲線相關(guān)方法
這些方法幫助我們繪制各類直線、曲線
-- quadTo(float x1, float y1, float x2, float y2) 繪制二階貝塞爾曲線济舆。quadTo的前兩個(gè)參數(shù)為控制點(diǎn)的坐標(biāo)卿泽,后兩個(gè)參數(shù)為終點(diǎn)坐標(biāo), 當(dāng)然起點(diǎn)坐標(biāo)就是path的起點(diǎn)坐標(biāo)
-- cubicTo(float x1, float y1, float x2, float y2, float x3, float y3) 繪制三階貝塞爾曲線。與quadTo類似滋觉,前四個(gè)參數(shù)表示兩個(gè)控制點(diǎn)签夭,最后兩個(gè)參數(shù)表示終點(diǎn):
// 實(shí)例化路徑
mPath = new Path();
// 移動點(diǎn)至[100,100]
mPath.moveTo(100, 100);
// 連接路徑到點(diǎn)
mPath.cubicTo(200, 200, 300, 0, 400, 100);
- arcTo (RectF oval, float startAngle, float sweepAngle) 用來生成弧線的方法。說白了就是從圓或者橢圓上截取一部分而已椎侠。
// 實(shí)例化路徑
mPath = new Path();
// 移動點(diǎn)至[100,100]
mPath.moveTo(100, 100);
// 連接路徑到點(diǎn)
RectF oval = new RectF(100, 100, 200, 200);
mPath.arcTo(oval, 0, 90);
運(yùn)行后效果跟我們想想的有點(diǎn)不一樣第租,
這是因?yàn)槭褂肞ath生成的路徑必定都是連貫的,雖然我們使用arcTo繪制的時(shí)一段狐我纪,但是其最終都會與我們Path的起點(diǎn)鏈接起來慎宾。如果你不想要鏈接,那么path也提供了一種方法浅悉,而已設(shè)置是否強(qiáng)制鏈接起點(diǎn)趟据。
arcTo (RectF oval, float startAngle, float sweepAngle, boolean forceMoveTo) 該方法只是多了一個(gè)布爾值,值為true時(shí)將會把弧的起點(diǎn)作為Path的起點(diǎn)
Path中除了上面介紹的幾個(gè)XXXTo方法外還有一套rXXXTo方法:
rCubicTo(float x1, float y1, float x2, float y2, float x3, float y3)
rLineTo(float dx, float dy)
rMoveTo(float dx, float dy)
rQuadTo(float dx1, float dy1, float dx2, float dy2)
這一系列rXXXTo方法其實(shí)跟上面的那些XXXTo差不多的术健,唯一的不同是rXXXTo方法的參考坐標(biāo)是相對的而XXXTo方法的參考坐標(biāo)始終是參照畫布原點(diǎn)坐標(biāo)之宿。相對的指的是,終點(diǎn)的坐標(biāo)是相對于起點(diǎn)的坐標(biāo)的苛坚,例如rMoveTo(100,100) ; rLineTo(200,200) ,表示這個(gè)線段的長度是200而不是100.
- addXXX()方法比被,Path的這一系列的add方法允許我們直接往Path中添加一些曲線色难。
比如:addArc(RectF oval, float startAngle, float sweepAngle) 方法允許我們將一段弧形添加至Path,注意這里我用到了“添加”這個(gè)詞匯等缀,也就是說枷莉,通過addXXX方法添加到Path中的曲線是不會和上一次的曲線進(jìn)行連接的。
-- 除了addArc(RectF oval, float startAngle, float sweepAngle)方法之外還有這一系列的add方法尺迂。
addCircle(float x, float y, float radius, Path.Direction dir)
addOval(float left, float top, float right, float bottom, Path.Direction dir)
addRect(float left, float top, float right, float bottom, Path.Direction dir)
addRoundRect(float left, float top, float right, float bottom, float rx, float ry, Path.Direction dir)
這些方法和addArc有很明顯的區(qū)別笤妙,就是多了一個(gè)Path.Direction參數(shù)Path.Direction只有兩個(gè)常量值CCW和CW分別表示逆時(shí)針方向閉合和順時(shí)針方向閉合
mPath.addOval(oval, Path.Direction.CW);
順時(shí)針
mPath.addOval(oval, Path.Direction.CCW);
逆時(shí)針public class PathView extends View {
private Path mPath;// 路徑對象
private Paint mPaint;// 路徑畫筆對象
public PathView(Context context, AttributeSet attrs) {
super(context, attrs);
/*
* 實(shí)例化畫筆并設(shè)置屬性
*/
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setColor(Color.CYAN);
mPaint.setStrokeWidth(5);
// 實(shí)例化路徑
mPath = new Path();
// 移動點(diǎn)至[100,100]
mPath.moveTo(100, 100);
// 連接路徑到點(diǎn)
mPath.lineTo(200, 200);
// 添加一條弧線到Path中
RectF oval = new RectF(100, 100, 300, 400);
mPath.addArc(oval, 0, 90);
}
@Override
protected void onDraw(Canvas canvas) {
// 繪制路徑
canvas.drawPath(mPath, mPaint);
}
}
8 Region
回顧C(jī)anvas中有關(guān)裁剪的方法,你會發(fā)現(xiàn)有一大堆帶有Region.Op參數(shù)的重載方法:
clipPath(Path path, Region.Op op) clipRect(Rect rect, Region.Op op) clipRect(RectF rect, Region.Op op) clipRect(float left, float top, float right, float bottom, Region.Op op) clipRegion(Region region, Region.Op op)
要明白這些方法的Region.Op參數(shù)那么首先要了解Region為何物噪裕。Region的意思是“區(qū)域”蹲盘,在Android里呢它同樣表示的是一塊封閉的區(qū)域,Region中的方法都非常的簡單膳音,我們重點(diǎn)來瞧瞧Region.Op召衔,Op是Region的一個(gè)枚舉類,里面呢有六個(gè)枚舉常量:
那么Region.Op究竟有什么用呢祭陷?其實(shí)它就是個(gè)組合模式苍凛,我們曾學(xué)過一個(gè)叫圖形混合模式的,而在本節(jié)開頭我們也曾講過Rect也有類似的組合方法兵志,Region.Op灰常簡單醇蝴,如果你看過圖形混合模式的話。這里我就給出一段測試代碼想罕,大家可以嘗試去改變不同的組合模式看看效果
public class CanvasView extends View {
private Region mRegionA, mRegionB;// 區(qū)域A和區(qū)域B對象
private Paint mPaint;// 繪制邊框的Paint
public CanvasView(Context context, AttributeSet attrs) {
super(context, attrs);
// 實(shí)例化畫筆并設(shè)置屬性
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setColor(Color.WHITE);
mPaint.setStrokeWidth(2);
// 實(shí)例化區(qū)域A和區(qū)域B
mRegionA = new Region(100, 100, 300, 300);
mRegionB = new Region(200, 200, 400, 400);
}
@Override
protected void onDraw(Canvas canvas) {
// 填充顏色
canvas.drawColor(Color.BLUE);
canvas.save();
// 裁剪區(qū)域A
canvas.clipRegion(mRegionA);
// 再通過組合方式裁剪區(qū)域B
canvas.clipRegion(mRegionB, Region.Op.DIFFERENCE);
// 填充顏色
canvas.drawColor(Color.RED);
canvas.restore();
// 繪制框框幫助我們觀察
canvas.drawRect(100, 100, 300, 300, mPaint);
canvas.drawRect(200, 200, 400, 400, mPaint);
}
}
以下是各種組合模式的效果
DIFFERENCE
最終區(qū)域?yàn)榈谝粋€(gè)區(qū)域與第二個(gè)區(qū)域不同的區(qū)域悠栓。
INTERSECT
最終區(qū)域?yàn)榈谝粋€(gè)區(qū)域與第二個(gè)區(qū)域相交的區(qū)域。
REPLACE
最終區(qū)域?yàn)榈诙€(gè)區(qū)域按价。
REVERSE_DIFFERENCE
最終區(qū)域?yàn)榈诙€(gè)區(qū)域與第一個(gè)區(qū)域不同的區(qū)域闸迷。
UNION
最終區(qū)域?yàn)榈谝粋€(gè)區(qū)域加第二個(gè)區(qū)域。
XOR
最終區(qū)域?yàn)榈谝粋€(gè)區(qū)域加第二個(gè)區(qū)域并減去兩者相交的區(qū)域俘枫。
Region.Op就是這樣腥沽,它和我們之前講到的圖形混合模式幾乎一模一樣換湯不換藥……我在做示例的時(shí)候僅僅是使用了一個(gè)Region,實(shí)際上Rect鸠蚪、Cricle今阳、Ovel等封閉的曲線都可以使用Region.Op,介于篇幅茅信,而且也不難以理解就不多說了盾舌。
有些童鞋會問那么Region和Rect有什么區(qū)別呢?
首先最重要的一點(diǎn)蘸鲸,Region表示的是一個(gè)區(qū)域妖谴,而Rect表示的是一個(gè)矩形,這是最根本的區(qū)別之一,其次膝舅,Region有個(gè)很特別的地方是它不受Canvas的變換影響嗡载,Canvas的local不會直接影響到Region自身,
什么意思呢仍稀?我們來看一個(gè)simple你就會明白:
public class CanvasView extends View {
private Region mRegion;// 區(qū)域?qū)ο?
private Rect mRect;// 矩形對象
private Paint mPaint;// 繪制邊框的Paint
public CanvasView(Context context, AttributeSet attrs) {
super(context, attrs);
// 實(shí)例化畫筆并設(shè)置屬性
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setColor(Color.DKGRAY);
mPaint.setStrokeWidth(2);
// 實(shí)例化矩形對象
mRect = new Rect(0, 0, 200, 200);
// 實(shí)例化區(qū)域?qū)ο?
mRegion = new Region(200, 200, 400, 400);
}
@Override
protected void onDraw(Canvas canvas) {
canvas.save();
// 裁剪矩形
canvas.clipRect(mRect);
canvas.drawColor(Color.RED);
canvas.restore();
canvas.save();
// 裁剪區(qū)域
canvas.clipRegion(mRegion);
canvas.drawColor(Color.RED);
canvas.restore();
// 為畫布繪制一個(gè)邊框便于觀察
canvas.drawRect(0, 0, canvas.getWidth(), canvas.getHeight(), mPaint);
}
}
大家看到洼滚,我在[0, 0, 200, 200]和[200, 200, 400, 400]的位置分別繪制了Rect和Region,它們兩個(gè)所占大小是一樣的:
畫布因?yàn)楹推聊灰粯哟蠹寂耍瑂o~~我們看不出描邊的效果遥巴,這時(shí),我們將Canvas縮放至75%大小享幽,看看會發(fā)生什么:
@Override
protected void onDraw(Canvas canvas) {
// 縮放畫布
canvas.scale(0.75F, 0.75F);
canvas.save();
// 裁剪矩形
canvas.clipRect(mRect);
canvas.drawColor(Color.RED);
canvas.restore();
canvas.save();
// 裁剪區(qū)域
canvas.clipRegion(mRegion);
canvas.drawColor(Color.RED);
canvas.restore();
// 為畫布繪制一個(gè)邊框便于觀察
canvas.drawRect(0, 0, canvas.getWidth(), canvas.getHeight(), mPaint);
}
這時(shí)我們會看到铲掐,Rect隨著Canvas的縮放一起縮放了,但是Region依舊泰山不動地淡定: