高級UI<第十五篇>:一起來演示Android畫布

Canvas唆途,即畫布蓝纲,是自定義View必須了解的基礎(chǔ)塔猾。

Canvas的常用操作速查表

操作類型 相關(guān)API 備注
繪制顏色 drawColor, drawRGB, drawARGB 使用單一顏色填充整個畫布
繪制基本形狀 drawPoint, drawPoints, drawLine, drawLines, drawRect, drawRoundRect, drawOval, drawCircle, drawArc 依次為 點驼抹、線柱锹、矩形、圓角矩形灾测、橢圓爆价、圓、圓弧
繪制圖片 drawBitmap, drawPicture 繪制位圖和圖片
繪制文本 drawText, drawPosText, drawTextOnPath 依次為 繪制文字媳搪、繪制文字時指定每個文字位置铭段、根據(jù)路徑繪制文字
繪制路徑 drawPath 繪制路徑,繪制貝塞爾曲線時也需要用到該函數(shù)
頂點操作 drawVertices, drawBitmapMesh 通過對頂點操作可以使圖像形變秦爆,drawVertices直接對畫布作用序愚、 drawBitmapMesh只對繪制的Bitmap作用
畫布剪裁 clipPath, clipRect 設(shè)置畫布的顯示區(qū)域
畫布快照 save, restore, saveLayerXxx, restoreToCount, getSaveCount 依次為 保存當(dāng)前狀態(tài)、 回滾到上一次保存的狀態(tài)等限、 保存圖層狀態(tài)爸吮、 回滾到指定狀態(tài)、 獲取保存次數(shù)
畫布變換 translate, scale, rotate, skew 依次為 位移望门、縮放形娇、 旋轉(zhuǎn)、傾斜
Matrix(矩陣) getMatrix, setMatrix, concat 實際畫布的位移筹误,縮放等操作的都是圖像矩陣Matrix桐早,只不過Matrix比較難以理解和使用,故封裝了一些常用的方法纫事。

首先隨意自定義一個View勘畔,重寫onDraw(Canvas canvas)方法,在onDraw方法里面演示畫布:

public class CustomView extends View {

    private Paint mPaint;

    public CustomView(Context context) {
    super(context);
    }

    public CustomView(Context context, @Nullable AttributeSet attrs) {
    super(context, attrs);
    }

    public CustomView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public CustomView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
    super(context, attrs, defStyleAttr, defStyleRes);
    }

    @Override
    protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);

    }
}

開始初始化畫筆:

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);

    //初始化畫筆
    initPaint();

}

/**
 * 初始化畫筆
 * @return
 */
private void initPaint(){
    if(mPaint == null){
        mPaint = new Paint();
    }
    //設(shè)置畫筆的顏色
    mPaint.setColor(Color.BLUE);
    //設(shè)置畫筆的大小
    mPaint.setStrokeWidth(10);
}

這里簡單設(shè)置了畫筆丽惶,本章目的是演示畫布炫七。
接下來拿起你的筆開始畫畫:

畫直線:

畫單條直線

//畫直線,前四個參數(shù)為直線兩端的坐標(biāo)
canvas.drawLine(100,200,300,300, mPaint);

效果如下


圖片.png

畫多條直線(一):

    float[] pts1 = {//聲明直線位置
            200,300,400,400,
            300,400,500,600
    };
    canvas.drawLines(pts1, mPaint);

效果如下


圖片.png

畫多條直線(二):

    float[] pts2 = {//數(shù)組的長度為12
            200,300,400,400,
            500,450,500,600,
            700,750,800,850
    };
    // 第一個參數(shù)傳遞數(shù)組
    // 第二個參數(shù)傳遞偏移量钾唬,也就是跳過前多少個數(shù)據(jù)
    // 第三個參數(shù)傳遞數(shù)量万哪,因為每四個數(shù)據(jù)確定一條直線,所以這里的傳值都是4的倍數(shù)
    canvas.drawLines(pts2,2,8,mPaint);//從數(shù)組的第三個數(shù)據(jù)開始以后取8個數(shù)據(jù)抡秆,每4個數(shù)據(jù)畫一條直線

效果如下


圖片.png

畫晦任 :

drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint) 

參數(shù)詳解:
oval:矩形對象
startAngle:開始繪制的角度
sweepAngle:繪制移動的角度
useCenter:是否使用矩形的中心點(true為扇形,false為非扇形)

drawArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle, boolean useCenter, Paint paint) 

參數(shù)詳解:
left:矩形的左邊位置
top:矩形的頂部位置
right:矩形的右邊位置
bottom:矩形的下部位置
startAngle:開始繪制的角度
sweepAngle:繪制幅度
useCenter:是否使用矩形的中心點(true為扇形儒士,false為非扇形)

那么弧是怎么繪制的呢的止?
(1)弧繪制的過程是按照順時針繪制的;
(2)繪制弧需要一個矩形作為參照物着撩;
(3)弧在矩形中是內(nèi)切的诅福;
(4)找到矩形的中心點匾委,可以想象從該點向右畫一條水平直線,其角度為0氓润,順時針角度為正數(shù)赂乐,逆時針角度為負(fù)數(shù),從startAngle角度開始順時針繪制咖气,繪制幅度為sweepAngle挨措。

假設(shè)矩形參數(shù)為(400, 400, 600, 800),從-30度位置開始繪制崩溪,繪制的幅度分別是60度,120度,220度浅役,為了方便演示,我們畫一個矩形作為底部伶唯。

    RectF rectF = new RectF(400, 400, 600, 800);//聲明矩形
    canvas.drawRect(rectF, new Paint());
    canvas.drawArc(rectF, -30, 60, true, mPaint);//60度扇形
    //canvas.drawArc(rectF, -30, 60, false, mPaint);//60度非扇形
    //canvas.drawArc(rectF, -30, 120, true, mPaint);//120度扇形
    //canvas.drawArc(rectF, -30, 120, false, mPaint);//120度非扇形
    //canvas.drawArc(rectF, -30, 220, true, mPaint);//220度扇形
    //canvas.drawArc(rectF, -30, 220, false, mPaint);//220度非扇形

60度效果如下


圖片.png
圖片.png

120度效果如下


圖片.png

圖片.png

220度效果如下
圖片.png

圖片.png

畫矩形

方法:

public void drawRect(RectF rect, Paint paint)担租;

public void drawRect(Rect r, Paint paint) ;

public void drawRect(float left, float top, float right, float bottom, Paint paint) 抵怎;

效果如下


圖片.png

畫圓

方法

drawCircle(float cx, float cy, float radius, Paint paint)

效果如下


圖片.png

畫橢圓

方法

public void drawOval(RectF oval, Paint paint) 

public void drawOval(float left, float top, float right, float bottom, Paint paint)

效果如下


圖片.png

畫矢量圖

方法

public void drawPicture(Picture picture)

public void drawPicture(Picture picture, RectF dst) 

public void drawPicture(Picture picture, Rect dst)

使用之前請先關(guān)閉硬件加速高級UI<第二十三篇>:Android開發(fā)如何關(guān)閉GPU硬件加速
但是關(guān)閉硬件加速會帶來一些異常問題,所以到底關(guān)閉還是開啟硬件加速岭参,是否使用drawPicture看情況而定反惕。

繪制位圖有四種方法:
(1)使用Picture提供的draw方法繪制

    if(picture == null){
        picture = new Picture();
    }
    Canvas ca = picture.beginRecording(50, 50);
    ca.drawColor(Color.RED);
    ca.drawText("床前明月光, 地上鞋兩雙演侯!", 200, 400, mPaint);
    picture.endRecording();
    picture.draw(canvas);

效果如下


圖片.png

我們發(fā)現(xiàn)即使我這里設(shè)置的Picture大小只有50x50姿染,紅色背景依然覆蓋了整個屏幕, 根據(jù)現(xiàn)象可以得知繪制Picture默認(rèn)是覆蓋整個當(dāng)前畫布的秒际。

(2)使用Canvas提供的drawPicture方法繪制悬赏,并且沒有矩形區(qū)域限制。

    Canvas ca = picture.beginRecording(50, 50);
    ca.drawColor(Color.RED);
    ca.drawText("床前明月光娄徊, 地上鞋兩雙闽颇!", 200, 400, mPaint);
    picture.endRecording();
    canvas.drawPicture(picture);

效果如下


圖片.png

(3)使用Canvas提供的drawPicture方法繪制,并且有矩形區(qū)域限制

    if(picture == null){
        picture = new Picture();
    }
    Canvas ca = picture.beginRecording(50, 50);
    ca.drawColor(Color.RED);
    ca.drawCircle(100, 100, 100, mPaint);
    picture.endRecording();
    RectF rectF = new RectF(50, 50, 200, 200);
    canvas.drawPicture(picture, rectF);

效果如下


圖片.png

(4)使用PictureDrawable繪制

    if(picture == null){
        picture = new Picture();
    }
    Canvas ca = picture.beginRecording(50, 50);
    ca.drawColor(Color.RED);
    ca.drawCircle(100, 100, 100, mPaint);
    picture.endRecording();
    PictureDrawable drawable = new PictureDrawable(picture);
    drawable.setBounds(0,0,500, 500);
    drawable.draw(canvas);

效果如下


圖片.png

這個方法可以任意設(shè)置Picture在屏幕中的位置寄锐。

畫位圖

方法

drawBitmap(Bitmap bitmap, float left, float top, Paint paint) 
drawBitmap(Bitmap bitmap, Rect src, RectF dst, Paint paint) 
drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint)
drawBitmap(int[] colors, int offset, int stride, float x, float y, int width, int height, boolean hasAlpha, Paint paint)
drawBitmap(int[] colors, int offset, int stride, int x, int y, int width, int height, boolean hasAlpha, Paint paint) 
drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint) 
drawBitmapMesh(Bitmap bitmap, int meshWidth, int meshHeight, float[] verts, int vertOffset, int[] colors, int colorOffset, Paint paint)

(1) 根據(jù)圖片左上角的位置繪畫

canvas.drawBitmap(bitmap, 100,100, mPaint);

效果如下


圖片.png

(2)取bitmap的某一塊填充的制定區(qū)域

    //取bitmap某塊的區(qū)域
    Rect rect = new Rect(0,0,bitmap.getWidth()/2,bitmap.getHeight()/2);
    //將要填充的區(qū)域
    RectF rectF = new RectF(200,200,800,800);
    canvas.drawBitmap(bitmap, rect,rectF,mPaint);

效果如下


圖片.png

(3)已過時的方法

drawBitmap(int[] colors, int offset, int stride, float x, float y, int width, int height, boolean hasAlpha, Paint paint)
drawBitmap(int[] colors, int offset, int stride, int x, int y, int width, int height, boolean hasAlpha, Paint paint) 

這兩個方法不需要在意兵多。

(4)根據(jù)矩陣作用于將要繪制出的圖片

   Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher);
    Matrix matrix = new Matrix();
    //旋轉(zhuǎn)45度
    matrix.postRotate(45);
    //平移到屏幕的(300,400)位置
    matrix.postTranslate(300, 400);
    canvas.drawBitmap(bitmap, matrix, mPaint);

效果如下


圖片.png

(5)扭曲圖像的繪制

drawBitmapMesh(Bitmap bitmap, int meshWidth, int meshHeight, float[] verts, int vertOffset, int[] colors, int colorOffset, Paint paint);

參數(shù)說明:
bitmap    需要扭曲的源位圖
meshWidth   控制在橫向上把該源位圖劃成成多少格
meshHeight   控制在縱向上把該源位圖劃成成多少格
verts      長度為(meshWidth + 1) * (meshHeight + 1) * 2的數(shù)組橄仆,它記錄了扭曲后的位圖各頂點位置
vertOffset 控制verts數(shù)組中從第幾個數(shù)組元素開始才對bitmap進(jìn)行扭曲

效果如下


1535009798103.gif

代碼:

public class CustomView extends View {

    private Paint mPaint;
    private Bitmap bitmap;
    private int MESHWIDTH = 5;//圖片橫向劃分成5格
    private int MESHHEIHT = 5;//圖片縱向劃分成5格
    private int COUNT = (MESHWIDTH + 1) * (MESHHEIHT + 1);//圖中有多少個點
    private float[] verts;//所有坐標(biāo)的數(shù)據(jù)
    private float[] vertsChange;//改變之后的坐標(biāo)數(shù)據(jù)

    public CustomView(Context context) {
    super(context);
    initVerts();
    }

    public CustomView(Context context, @Nullable AttributeSet attrs) {
    super(context, attrs);
    initVerts();
    }

    public CustomView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    initVerts();
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public CustomView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
    super(context, attrs, defStyleAttr, defStyleRes);
    initVerts();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int widthMode = MeasureSpec.getMode(widthMeasureSpec);
    int heightMode = MeasureSpec.getMode(heightMeasureSpec);
    int width = MeasureSpec.getSize(widthMeasureSpec);
    int height = MeasureSpec.getSize(heightMeasureSpec);
    if(widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST){
        //如果都是wrap_content的時候
        setMeasuredDimension(bitmap.getWidth(), bitmap.getHeight());
    }else{
        setMeasuredDimension(width, height);
    }
    }

    @Override
    protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);

    //初始化畫筆
    initPaint();

    canvas.drawBitmapMesh(bitmap, MESHWIDTH, MESHHEIHT,verts, 0, null,0,mPaint);
    }

    private void initVerts(){
    bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.pic);
    verts = new float[COUNT * 2];//所有坐標(biāo)的數(shù)據(jù)
    vertsChange = new float[COUNT * 2];//改變之后的坐標(biāo)數(shù)據(jù)
    int index = 0;//數(shù)組坐標(biāo)
    for (int y = 0;y <= MESHHEIHT;y++){
        float fy = y * bitmap.getHeight()/MESHHEIHT;
        for (int x = 0;x <= MESHWIDTH;x++){
        float fx = x * bitmap.getWidth()/MESHWIDTH;
        vertsChange[index * 2 + 0] = verts[index * 2 + 0] = fx;
        vertsChange[index * 2 + 1] = verts[index * 2 + 1] = fy;
        index += 1;
        }
    }
    }
    /**
     * 初始化畫筆
     * @return
     */
    private void initPaint(){
    if(mPaint == null){
        mPaint = new Paint();
    }
    //設(shè)置畫筆的顏色
    mPaint.setColor(Color.BLUE);
    //設(shè)置畫筆的大小
    mPaint.setStrokeWidth(10);
    }

    /**
     * 觸摸之后改變數(shù)組
     * @param cx
     * @param cy
     */
    private void changeVerts(float cx, float cy) {

    for(int i = 0; i < COUNT * 2; i += 2) {
        float fx = cx - vertsChange[i + 0];
        float fy = cy - vertsChange[i + 1];
        float rr = fx * fx + fy * fy;
        //計算每個坐標(biāo)點與當(dāng)前點(cx,cy)之間的距離
        float r = (float)Math.sqrt(rr);
        //圖片坐標(biāo)離當(dāng)前點的距離比對角線的距離剩膘,求出變化幅度(這里的算法可以自定義)
        float fudu = (float) (r / Math.sqrt(bitmap.getWidth() * bitmap.getWidth() + bitmap.getHeight() * bitmap.getHeight()));
        verts[i + 0] = vertsChange[i + 0] + vertsChange[i + 0] * fudu;
        verts[i + 1] = vertsChange[i + 1] + vertsChange[i + 1] * fudu;
    }
    //重繪
    invalidate();
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
    changeVerts(event.getX(), event.getY());
    return super.onTouchEvent(event);
    }
}

畫背景色

方法

canvas.drawColor(Color.BLUE);
canvas.drawColor(Color.BLUE, PorterDuff.Mode.LIGHTEN);
canvas.drawRGB(0, 0, 255);
drawARGB(int a, int r, int g, int b) //帶透明度,第一個參數(shù)就是透明度

這四個方法的使用不難理解盆顾,唯一需要講解的是第二個方法中的Mode:

看枚舉

public static enum Mode {
    ADD,
    CLEAR,
    DARKEN,
    DST,
    DST_ATOP,
    DST_IN,
    DST_OUT,
    DST_OVER,
    LIGHTEN,
    MULTIPLY,
    OVERLAY,
    SCREEN,
    SRC,
    SRC_ATOP,
    SRC_IN,
    SRC_OUT,
    SRC_OVER,
    XOR;

    private Mode() {
    }
}

看圖

1344993853_6311.JPG

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
上下層都顯示汰规。

如果想了解更多PorterDuff.Mode汤功,請乘坐下方的飛機(jī):
各個擊破搞明白PorterDuff.Mode

本人剛才坐飛機(jī)剛回來,現(xiàn)在結(jié)合以上資料的案例一一給大家演示不同混合模式的效果:
高級UI<第十六篇>:Xfermode 詳解

注意溜哮,默認(rèn)情況下是給默認(rèn)畫布添加背景色滔金,當(dāng)有裁剪操作時,默認(rèn)畫布就會變成裁剪區(qū)域的畫布茂嗓。

畫圓角矩形

方法

public void drawRoundRect(RectF rect, float rx, float ry, Paint paint)

public void drawRoundRect(float left, float top, float right, float bottom, float rx, float ry, Paint paint) 

效果如下


圖片.png

畫路徑

方法

//矩形路徑
public void addRect(RectF rect, Path.Direction dir) 
//矩形路徑
public void addRect(float left, float top, float right, float bottom, Path.Direction dir) 
//橢圓路徑
public void addOval(RectF oval, Path.Direction dir) 
//橢圓路徑
public void addOval(float left, float top, float right, float bottom, Path.Direction dir) 
//圓路徑
public void addCircle(float x, float y, float radius, Path.Direction dir)
//弧路徑
public void addArc(RectF oval, float startAngle, float sweepAngle)
//弧路徑
public void addArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle) 
//橢圓路徑
public void addRoundRect(RectF rect, float rx, float ry, Path.Direction dir)
//橢圓路徑
public void addRoundRect(float left, float top, float right, float bottom, float rx, float ry, Path.Direction dir)
//橢圓路徑
public void addRoundRect(RectF rect, float[] radii, Path.Direction dir) 
//橢圓路徑
public void addRoundRect(float left, float top, float right, float bottom, float[] radii, Path.Direction dir) 
//橢圓路徑
public void addPath(Path src, float dx, float dy) 
//橢圓路徑
public void addPath(Path src)
//橢圓路徑
public void addPath(Path src, Matrix matrix) 

效果如下
(1)矩形路徑


圖片.png

(2)圓路徑


圖片.png

(3)弧路徑


圖片.png

(4)橢圓路徑


圖片.png

(5)圓角矩形路徑


圖片.png

畫文字

方法

public void drawText(char[] text, int index, int count, float x, float y, Paint paint) 

public void drawText(String text, float x, float y, Paint paint) 

public void drawText(String text, int start, int end, float x, float y, Paint paint) 

public void drawText(CharSequence text, int start, int end, float x, float y, Paint paint) 

public void drawTextOnPath(char[] text, int index, int count, Path path, float hOffset, float vOffset, Paint paint) 

public void drawTextOnPath(String text, Path path, float hOffset, float vOffset, Paint paint)

public void drawTextRun(char[] text, int index, int count, int contextIndex, int contextCount, float x, float y, boolean isRtl, Paint paint)

public void drawTextRun(CharSequence text, int start, int end, int contextStart, int contextEnd, float x, float y, boolean isRtl, Paint paint)

代碼

char[] chars = {'a','b','c','d'};
//從數(shù)組第二位開始畫兩個數(shù)據(jù)
canvas.drawText(chars, 1, 2,100, 100, mPaint);

效果如下


圖片.png

代碼

canvas.drawText("我是中國人",100, 100, mPaint);

效果如下


圖片.png

代碼

//從角標(biāo)2畫到角標(biāo)4
canvas.drawText("我是中國人",2, 4, 100, 100, mPaint);

效果如下


圖片.png

代碼

//這個方法用于處理特殊文字餐茵,阿拉伯文字就是特殊文字之一
canvas.drawTextRun("????????????????", 0, 16, 0, 16, 100, 100, true, mPaint);

參數(shù)描述:
text:要繪制的文字
start:從那個字開始繪制
end:繪制到哪個字結(jié)束
contextStart:上下文的起始位置。contextStart 需要小于等于 start
contextEnd:上下文的結(jié)束位置述吸。contextEnd 需要大于等于 end
x:文字左邊的坐標(biāo)
y:文字的基線坐標(biāo)
isRtl:是否是 RTL(Right-To-Left忿族,從右向左)
大小關(guān)系:
contextStart <= start <= end<= contextEnd

代碼

    Path path=new Path();
    path.addCircle(400,400,200, Path.Direction.CW);
    //根據(jù)路徑繪制文字
    canvas.drawTextOnPath("床前明月光,地上鞋兩雙蝌矛!",path, 0, 0, mPaint);

效果如下


圖片.png

畫點

方法

public void drawPoint(float x, float y, Paint paint) 

public void drawPoints(float[] pts, int offset, int count, Paint paint) 

public void drawPoints(float[] pts, Paint paint) 

代碼

    float[] pos = {100, 100,100,200,200,200,300,300,400,400,400,500,500,500};
    canvas.drawPoints(pos, mPaint);

效果如下


圖片.png

畫畫筆(將畫筆設(shè)置的顏色和透明度鋪滿畫布)

畫筆配置

/**
 * 初始化畫筆
 * @return
 */
private void initPaint(){
    if(mPaint == null){
        mPaint = new Paint();
    }
    //設(shè)置畫筆的顏色
    mPaint.setColor(Color.RED);
    //設(shè)置畫筆的大小
    mPaint.setStrokeWidth(50);
    //設(shè)置文字大小
    mPaint.setTextSize(50);
    //設(shè)置畫筆的樣式
    mPaint.setStyle(Paint.Style.STROKE);
}

代碼

canvas.drawPaint(mPaint);//將畫筆設(shè)置的顏色和透明度鋪滿畫布

效果如下


圖片.png
  • 方法drawVertices待定(研究中)

裁剪畫布

方法

public boolean clipRect(RectF rect, Op op) 

public boolean clipRect(Rect rect, Op op)

public boolean clipRect(RectF rect)

public boolean clipOutRect(RectF rect) 

public boolean clipRect(Rect rect) 

public boolean clipOutRect(Rect rect) 

public boolean clipRect(float left, float top, float right, float bottom, Op op)

public boolean clipRect(float left, float top, float right, float bottom) 

public boolean clipOutRect(float left, float top, float right, float bottom) 

public boolean clipRect(int left, int top, int right, int bottom) 

public boolean clipOutRect(int left, int top, int right, int bottom)

public boolean clipPath(Path path, Op op)

public boolean clipPath(Path path) 

public boolean clipOutPath(Path path) 

根據(jù)以上的方法可以看出道批,裁剪有兩種,一種的矩形入撒,另一種是自定義路徑隆豹,兩者用法是一樣的。

對op參數(shù)的理解:以剪裁兩次的區(qū)域分別為A衅金,B來區(qū)別

  • Region.Op.DIFFERENCE:剪裁出差異的部分噪伊,類似 A-B 部分
  • Region.Op.REPLACE:后剪裁B的覆蓋剪裁的A
  • Region.Op.REVERSE_DEFFERENCE:剪裁出差異的部分,類似 B-A 部分
  • Region.Op.INTERSECT:剪裁出相交的部分氮唯,類似 A交B 部分
  • Region.Op.UNION:剪裁出AB合并的部分鉴吹,類似** AUB**
  • Region.Op.XOR:是** (AUB)-(A交B)** 剛好與** A交B** 相對

繪制兩個路徑,代碼如下

    Path path1 = new Path();
    path1.addRect(300, 600, 600, 900, Path.Direction.CCW);
    canvas.clipPath(path1);

    Path path2 = new Path();
    path2.addCircle(300, 600, 300,Path.Direction.CCW);
    canvas.clipPath(path2);

    canvas.drawColor(Color.BLUE);

展示效果

圖片.png

顯然惩琉, 默認(rèn)情況下豆励, 兩個路徑取相交的部分,也就是說OP的默認(rèn)值是Region.Op.INTERSECT。

下面分別對OP進(jìn)行驗證:
(1)Region.Op.DIFFERENCE和Region.Op.DIFFERENCE
代碼

    Path path1 = new Path();
    path1.addRect(300, 600, 600, 900, Path.Direction.CCW);
    canvas.clipPath(path1, Region.Op.DIFFERENCE);

    Path path2 = new Path();
    path2.addCircle(300, 600, 300,Path.Direction.CCW);
    canvas.clipPath(path2, Region.Op.DIFFERENCE);

    canvas.drawColor(Color.BLUE);

效果如下


圖片.png
  • 在整個畫布上取非矩形路徑區(qū)域良蒸,假設(shè)為A
  • 在A上取非圓形路徑區(qū)域技扼。

(2)Region.Op.DIFFERENCE和Region.Op.REPLACE
代碼

    Path path1 = new Path();
    path1.addRect(300, 600, 600, 900, Path.Direction.CCW);
    canvas.clipPath(path1, Region.Op.REPLACE);

    Path path2 = new Path();
    path2.addCircle(300, 600, 300,Path.Direction.CCW);
    canvas.clipPath(path2, Region.Op.DIFFERENCE);

效果如下


圖片.png
  • 矩形裁剪路徑的模式是Region.Op.REPLACE,就是取其本身區(qū)域嫩痰,假設(shè)為A
  • 圓形裁剪路徑模式是Region.Op.DIFFERENCE拗踢,就在A上取非圓區(qū)域鲸沮。

現(xiàn)在,我們將模式反過來。
代碼

    Path path1 = new Path();
    path1.addRect(300, 600, 600, 900, Path.Direction.CCW);
    canvas.clipPath(path1, Region.Op.DIFFERENCE);

    Path path2 = new Path();
    path2.addCircle(300, 600, 300,Path.Direction.CCW);
    canvas.clipPath(path2, Region.Op.REPLACE);

    canvas.drawColor(Color.BLUE);

效果如下


圖片.png
  • 矩形路徑區(qū)域完全被圓形路徑區(qū)域覆蓋叉庐。

(3)Region.Op.DIFFERENCE和Region.Op.REVERSE_DIFFERENCE
代碼

    Path path1 = new Path();
    path1.addRect(300, 600, 600, 900, Path.Direction.CCW);
    canvas.clipPath(path1, Region.Op.REVERSE_DIFFERENCE);

    Path path2 = new Path();
    path2.addCircle(300, 600, 300,Path.Direction.CCW);
    canvas.clipPath(path2, Region.Op.DIFFERENCE);

    canvas.drawColor(Color.BLUE);

效果如下


圖片.png
  • 矩形區(qū)域使用的裁剪模式是Region.Op.REVERSE_DIFFERENCE波丰,也就是說叨恨,矩形區(qū)域減去整個畫布區(qū)域识藤, 結(jié)果就是空區(qū)域。假設(shè)為A祷蝌。
  • 圓形區(qū)域使用的裁剪模式是Region.Op.DIFFERENCE茅撞,因為當(dāng)前路徑的繪制是根據(jù)A畫布而言的,A畫布為空巨朦,所以當(dāng)前再怎么繪制也為空米丘。

我們將模式反過來
代碼

    Path path1 = new Path();
    path1.addRect(300, 600, 600, 900, Path.Direction.CCW);
    canvas.clipPath(path1, Region.Op.DIFFERENCE);

    Path path2 = new Path();
    path2.addCircle(300, 600, 300,Path.Direction.CCW);
    canvas.clipPath(path2, Region.Op.REVERSE_DIFFERENCE);

    canvas.drawColor(Color.BLUE);

效果如下


圖片.png
  • 矩形路徑的裁剪模式為Region.Op.DIFFERENCE,所以先取除矩形路徑區(qū)域的其他區(qū)域糊啡,假設(shè)為A蠕蚜。
  • 由于圓形路徑的裁剪模式為Region.Op.REVERSE_DIFFERENCE,所以用圓形路徑的區(qū)域減去A悔橄。

(4)Region.Op.DIFFERENCE和Region.Op.INTERSECT
代碼

    Path path1 = new Path();
    path1.addRect(300, 600, 600, 900, Path.Direction.CCW);
    canvas.clipPath(path1, Region.Op.DIFFERENCE);

    Path path2 = new Path();
    path2.addCircle(300, 600, 300,Path.Direction.CCW);
    canvas.clipPath(path2, Region.Op.INTERSECT);

    canvas.drawColor(Color.BLUE);

效果如下


圖片.png
  • 取矩形區(qū)域不同的地方,假設(shè)為A腺毫。
  • 取圓形區(qū)域與A相交的地方癣疟。

(5)Region.Op.INTERSECT和Region.Op.INTERSECT
代碼

    Path path1 = new Path();
    path1.addRect(300, 600, 600, 900, Path.Direction.CCW);
    canvas.clipPath(path1, Region.Op.INTERSECT);

    Path path2 = new Path();
    path2.addCircle(300, 600, 300,Path.Direction.CCW);
    canvas.clipPath(path2, Region.Op.INTERSECT);

    canvas.drawColor(Color.BLUE);

效果如下


圖片.png
  • 取畫布和矩形區(qū)域相交的區(qū)域, 假設(shè)為A
  • 取A和圓形區(qū)域相交的地方潮酒。

(6)Region.Op.INTERSECT和Region.Op.UNION
代碼

    Path path1 = new Path();
    path1.addRect(300, 600, 600, 900, Path.Direction.CCW);
    canvas.clipPath(path1, Region.Op.INTERSECT);

    Path path2 = new Path();
    path2.addCircle(300, 600, 300,Path.Direction.CCW);
    canvas.clipPath(path2, Region.Op.UNION);

    canvas.drawColor(Color.BLUE);

效果如下


圖片.png
  • 取畫布和矩形區(qū)域相交的區(qū)域睛挚, 假設(shè)為A
  • 取A和圓形區(qū)域合并的區(qū)域。

(7)Region.Op.INTERSECT和Region.Op.XOR
代碼

    Path path1 = new Path();
    path1.addRect(300, 600, 600, 900, Path.Direction.CCW);
    canvas.clipPath(path1, Region.Op.INTERSECT);

    Path path2 = new Path();
    path2.addCircle(300, 600, 300,Path.Direction.CCW);
    canvas.clipPath(path2, Region.Op.XOR);

    canvas.drawColor(Color.BLUE);

效果如下


圖片.png
  • 取畫布和矩形區(qū)域相交的區(qū)域急黎, 假設(shè)為A
  • A和圓形區(qū)域相并的區(qū)域減去A和圓形區(qū)域相交的區(qū)域扎狱。

先舉這么多例子。

Canvas的保存與恢復(fù)

保存:用于保存Canvas狀態(tài)勃教。

canvas.save();

恢復(fù):用于恢復(fù)Canvas狀態(tài)淤击。

canvas.restore();

Canvas的變幻操作

  • 平移操作

      canvas.drawRect(new Rect(0, 0, 200, 200), mPaint);
      canvas.translate(300, 300);
      canvas.drawRect(new Rect(0, 0, 400, 400), mPaint);
    
圖片.png
  • 縮放操作

      mPaint.setColor(Color.CYAN);
      canvas.drawRect(new Rect(0, 0, 200, 200), mPaint);
      mPaint.setColor(Color.RED);
      canvas.scale(0.5f, 0.5f);
      canvas.drawRect(new Rect(300, 300, 500, 500), mPaint);
    
圖片.png
  • 旋轉(zhuǎn)操作

無基準(zhǔn)點的情況:

    mPaint.setColor(Color.CYAN);
    canvas.drawRect(new Rect(0, 0, 200, 200), mPaint);
    mPaint.setColor(Color.RED);
    canvas.rotate(-30);
    canvas.drawRect(new Rect(300, 300, 500, 500), mPaint);
圖片.png

有基準(zhǔn)點的情況:

    mPaint.setColor(Color.CYAN);
    canvas.drawRect(new Rect(0, 0, 200, 200), mPaint);
    mPaint.setColor(Color.RED);
    canvas.rotate(30, 0, 300);
    canvas.drawRect(new Rect(0, 300, 400, 400), mPaint);
圖片.png
  • 錯切操作

    • sx:將畫布在 x 方向上傾斜相應(yīng)的角度,sx 為傾斜角度的 tan 值故源;
    • sy:將畫布在 y 軸方向上傾斜相應(yīng)的角度污抬,sy 為傾斜角度的 tan 值;
      比如在 X 軸方向上傾斜45度,tan45=1;
    mPaint.setColor(Color.CYAN);
    canvas.drawColor(Color.RED);
    canvas.skew(1,0);
    canvas.drawRect(new Rect(0, 0, 200, 200), mPaint);
圖片.png

其它方法

  • isHardwareAccelerated:判斷是否有硬件加速
  • saveLayerAlpha:本身和save方法差不多印机,但是它單獨分配了一個畫布用于繪制圖層矢腻。它定義了一個畫布區(qū)域(可設(shè)置透明度),此方法之后的所有繪制都在此區(qū)域中繪制射赛,直到調(diào)用canvas.restore()方法多柑。例如:在調(diào)用saveLayerAlpha方法之前繪制了一個“圓形”,在調(diào)用saveLayerAlpha方法之后繪制了一個“圓形”此時這兩個圓形并不在同一個圖層楣责。
  • isOpaque:檢測是否支持透明
  • quickReject:判斷是否沒和某個矩形相交竣灌,避免過度繪制
  • clipOutPath、clipOutRect:這兩個方法在API 28之后才出現(xiàn)
  • concat或setMatrix:使用矩陣來操作畫布

[本章完...]

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末腐魂,一起剝皮案震驚了整個濱河市帐偎,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蛔屹,老刑警劉巖削樊,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異兔毒,居然都是意外死亡漫贞,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進(jìn)店門育叁,熙熙樓的掌柜王于貴愁眉苦臉地迎上來迅脐,“玉大人,你說我怎么就攤上這事豪嗽∏疵铮” “怎么了?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵龟梦,是天一觀的道長隐锭。 經(jīng)常有香客問我,道長计贰,這世上最難降的妖魔是什么钦睡? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮躁倒,結(jié)果婚禮上荞怒,老公的妹妹穿的比我還像新娘。我一直安慰自己秧秉,他們只是感情好褐桌,可當(dāng)我...
    茶點故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著象迎,像睡著了一般撩嚼。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天完丽,我揣著相機(jī)與錄音恋技,去河邊找鬼。 笑死逻族,一個胖子當(dāng)著我的面吹牛蜻底,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播聘鳞,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼薄辅,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了抠璃?” 一聲冷哼從身側(cè)響起站楚,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎搏嗡,沒想到半個月后窿春,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡采盒,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年旧乞,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片磅氨。...
    茶點故事閱讀 38,117評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡尺栖,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出烦租,到底是詐尸還是另有隱情延赌,我是刑警寧澤,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布叉橱,位于F島的核電站皮胡,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏赏迟。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一蠢棱、第九天 我趴在偏房一處隱蔽的房頂上張望锌杀。 院中可真熱鬧,春花似錦泻仙、人聲如沸糕再。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽突想。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間猾担,已是汗流浹背袭灯。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留绑嘹,地道東北人稽荧。 一個月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像工腋,于是被迫代替她去往敵國和親姨丈。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,877評論 2 345