Android查缺補漏(View篇)--自定義View利器Canvas和Paint詳解


上篇文章介紹了自定義View的創建流程,從宏觀上給出了一個自定義View的創建步驟,本篇是上一篇文章的延續,介紹了自定義View中兩個必不可少的工具Canvas和Paint,從細節上更進一步的講解自定義View的詳細繪制方法。如果把自定義View比作蓋一座房子,那么上篇文章就相當於教會了我們怎么一步步的搭建房子的骨架,而本篇文章將要教會我們的是為房子的骨架添磚加瓦直至成型,甚至是怎么裝修。

Canvas

為了后文更為方便的講解Canvas的常用方法的使用,我們先來做一些准備工作,創建一個自定義View框架,先初始化一下Paint畫筆,並設置相關方法:

public class StudyView extends View {

    private Paint mPaint;
    private Context mContext;

    public StudyView(Context context) {
        super(context);
        init(context);
    }

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

    private void init(Context context) {
        mContext = context;
        mPaint = new Paint();
        mPaint.setAntiAlias(true); // 消除鋸齒
        mPaint.setStrokeWidth(5); // 設置筆尖寬度
        mPaint.setStyle(Paint.Style.STROKE); // 不填充
    }

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

繪制圓弧和扇形

Canvas提供drawArc()方法,通過傳遞不同的參數可用來繪制圓弧和扇形,此方法有兩個重載方法,詳細參數如下:

  • drawArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle, boolean useCenter, Paint paint)
  1. left:扇形或圓弧所占區域的左邊界線x坐標
  2. top:扇形或圓弧所占區域的上邊界線y坐標
  3. right:右邊界線x坐標
  4. bottom:下邊界線y坐標
  5. startAngle:扇形或圓弧的起始角度
  6. sweepAngle:掃過的角度
  7. userCenter:此參數可以理解為true就是畫扇形,false就是畫圓弧
  8. paint:畫筆
  • drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint)

此方法第一個參數是一個RectF類,也是邊界,就是把一個方法的left,top,right,bottom封裝到了RectF類中,剩余參數與上一個方法一致。

接下來用着兩個重載方法分別繪制兩個90°的扇形和兩個90°的圓弧:

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    // 繪制扇形
    canvas.drawArc(0, 0, 200, 200, 0, 90, true, mPaint);
    RectF rectF = new RectF(0, 0, 200, 200);
    canvas.drawArc(rectF, 180, 90, true, mPaint);

    // 繪制圓弧
    canvas.drawArc(300, 0, 500, 200, 0, 90, false, mPaint);
    RectF rectF1 = new RectF(300, 0, 500, 200);
    canvas.drawArc(rectF1, 180, 90, false, mPaint);
}

繪制效果如下圖所示,另外需要說明的一點是,drawArc的第五個參數startAngle中的角度,0°是指坐標系中第四象限中與x重合的角度,順時針方向代表角度增大的方向,如下圖中紅色線條所示。

繪制Bitmap

在Canvas中提供了drawBitmap方法,此方法可以讓我們直接獲取一張圖片繪制到畫布上,有了它可以讓我們的自定義View錦上添花,同時也讓我們實現一些復雜效果有了一個更加方便的途徑。下面是drawBitmap的幾個比較常用的重載方法:

  • drawBitmap(Bitmap bitmap, float left, float top, Paint paint)
  1. bitmap:Bitmap資源文件
  2. left和top:代表了圖片左上角落入的位置坐標。
  3. top:看2
  4. paint:畫筆
  • drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint)
  1. src:在Bitmap圖片上截取一部分作為繪制源,可null
  2. det:將繪制目標拉伸平鋪到det指定的矩形中
  • drawBitmap(Bitmap bitmap, Rect src, RectF dst, Paint paint)
    同第二個重載方法,幾乎一毛一樣。

  • drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint)

  1. matrix:Matrix的參數傳入是的drawBitmap功能變得異常強大,讓此方法有意思了許多,通過matrix可以實現圖片的平移(postTranslate())、縮放(postScale())、旋轉(postRotate())、錯切(postSkew())等等花式炫酷效果,由於Matrix的用法稍微多一些,篇幅限制,這里就先一帶而過了,感興趣的朋友可以自行探索。

在onDraw方法中drawBitmap的以上重載方法,注意在使用完Bitmap之后記得用Bitmap.recycle()來回收掉資源,以防止oom。

/** drawBitmap */
Bitmap bitmap = BitmapFactory.decodeResource(mContext.getResources(), android.R.mipmap.sym_def_app_icon);
// 繪制圖片
canvas.drawBitmap(bitmap, 0, 300, null);
// 將圖片拉伸平鋪在RectF矩形內
canvas.drawBitmap(bitmap, null, new RectF(200, 300, 500, 500), null);
// 截取圖片的四分之一拉伸平鋪在RectF矩形內
canvas.drawBitmap(bitmap, new Rect(0, 0, bitmap.getWidth()/2, bitmap.getHeight()/2), new RectF(500, 300, 800, 500), null);

Matrix matrix = new Matrix();
matrix.postTranslate(800, 300); // 將bitmap平移到此位置
canvas.drawBitmap(bitmap, matrix, mPaint);

// 為防止oom,及時回收bitmap
bitmap.recycle();

效果如下圖(紅框內)。

繪制圓形

  • drawCircle(float cx, float cy, float radius, Paint paint)
  1. cx:圓心x坐標
  2. cy:圓心y坐標
  3. radius:半徑
canvas.drawCircle(100, 700, 100, mPaint);

效果如下圖:

繪制點

  • drawPoint(float x, float y, Paint paint)
  1. x:點的x坐標
  2. y:點的y坐標
  • drawPoints(float[] pts, Paint paint) 繪制一組點
  1. pts:float數組,兩位為一組,兩兩結合代表x、y坐標,例如:pts[0]、pts[1]代表第一個點的x、y坐標,pts[2]、pts[3]代表第二個點的x、y坐標,依次類推。
  • drawPoints(float[] pts, int offset, int count, Paint paint) 繪制一組點
  1. pts:float數組,兩位為一組,兩兩結合代表x、y坐標,例如:pts[0]、pts[1]代表第一個點的x、y坐標,pts[2]、pts[3]代表第二個點的x、y坐標,依次類推。
  2. offset:代表數組開始跳過幾個只開始繪制點,注意這里不是指數組的下標,而是代表跳過幾個值。
  3. count:在跳過offset個值后,處理幾個值,注意這里的count不是代表點的個數,而是代表數組中值的個數。
canvas.drawPoint(100, 700, mPaint); // 繪制一個點

float[] points = new float[] {
    130, 700,
    160, 700,
    190, 700,
    210, 700,
    240, 700
};

canvas.drawPoints(points, 2, 4, mPaint); // 繪制一組點(代表跳過前兩個值,處理4個值,也就是實際繪制2個點)

效果如下圖:

繪制橢圓

  • drawOval(float left, float top, float right, float bottom, Paint paint)
  1. left
  2. top
  3. right
  4. bottom

在left、top、right、bottom圍成的區域內繪制一個橢圓。

  • drawOval(RectF oval, Paint paint)
  1. 將第一個重載方法的left、top、right、bottom封裝到RectF類中,與扇形的重載方法異曲同工。
RectF rectF2 = new RectF(300, 600, 700, 800); // 創建一個RectF
canvas.drawOval(rectF2, mPaint);

效果如下圖:

繪制矩形

  • drawRect(float left, float top, float right, float bottom, Paint paint)
  • drawRect(Rect r, Paint paint)
  • drawRect(RectF rect, Paint paint)

drawRect的參數非常好理解,這里就不啰嗦了,直接上代碼看效果:

canvas.drawRect(rectF2, mPaint);

注:這里的rectF2即上文繪制橢圓時創建的RectF對象。

繪制圓角矩形

  • drawRoundRect(float left, float top, float right, float bottom, float rx, float ry, Paint paint)
  • drawRoundRect(RectF rect, float rx, float ry, Paint paint)

drawRoundRect是繪制圓角矩形,用法和drawRect類似,唯一不同的是多了兩個參數:
2. rx:x軸方向的圓角弧度
3. ry:y軸方向的圓角弧度

上代碼,看效果:

canvas.drawRoundRect(rectF2, 60, 30, mPaint);

這里為了突出兩個方向的圓角弧度,特地將rx和ry設置差距比較大,效果如下圖:

繪制直線

  • drawLine(float startX, float startY, float stopX, float stopY, Paint paint)
  • drawLines(float[] pts, int offset, int count, Paint paint)
  • drawLines(float[] pts, Paint paint)

drawLine和drawLines一個是繪制一個點,一個是繪制一組點,其中drawLines中的float數組中四個值為一組點,其用法可以參照drawPoints。

canvas.drawLine(100, 820, 800, 820, mPaint);

float[] lines = new float[]{
        100f, 850f, 800f, 850f,
        100f, 900f, 800f, 900f,
        100f, 950f, 800f, 950f
};
canvas.drawLines(lines, mPaint); // 按floats數組中,四個數為1組,繪制多條線

效果如下圖:

drawPath() 繪制不規則圖形

上面的這些Canvas方法固然已經很強大了,但是我們如果想要繪制一些不規則的圖形怎么辦,這時候就要用到強大的drawPath()方法了,通過對Path進行設置不同的坐標、添加不同圖形,最后傳入drawPath方法中可以繪制出復雜的且不規則的形狀。以下是drawPath的方法及參數:

  • drawPath(Path path, Paint paint)

這里的關鍵參數就是Path,Path類的方法較多,大部分用法類似,這里挑幾個說一下:

  • Path類
  1. addArc(RectF oval, float startAngle, float sweepAngle) 往path里面添加一個圓弧
  2. addCircle(float x, float y, float radius, Path.Direction dir) 添加一個圓形
  3. addOval(RectF oval, Path.Direction dir) 添加一個橢圓
  4. addRect(RectF rect, Path.Direction dir) 添加一個矩形
  5. lineTo(float x, float y) 連線到坐標(x,y)
  6. moveTo(float x, float y) 將path繪制點移動到坐標(x,y)
  7. close() 用直線閉合圖形,調用此方法后,path會將最后一處點與起始用直線連接起來,path起始點為moveTo()方法的坐標上,如果沒有調用moveTo()起始點將默認為(0,0)坐標。
  8. ...

接下來使用drawPath繪制一個樓梯:

// 使用 Path 繪制一個樓梯
Path path = new Path();
path.lineTo(0, 1000);
path.lineTo(100, 1000);
path.lineTo(100, 1100);
path.lineTo(200, 1100);
path.lineTo(200, 1200);
path.lineTo(300, 1200);
path.lineTo(300, 1300);
path.lineTo(400, 1300);
path.lineTo(400, 1400);
path.lineTo(0, 1400);
path.lineTo(0, 1000);
path.close();
canvas.drawPath(path, mPaint);

效果如下圖:

再用drawPath方法繪制一個Android小機器人:

/ 使用 Path 繪制一個Android機器人
// 繪制兩個觸角
path.reset();
path.moveTo(625, 1050);
path.lineTo(650, 1120);
path.moveTo(775, 1050);
path.lineTo(750, 1120);

path.addArc(new RectF(600, 1100, 800, 1300), 180, 180); // 繪制頭部
path.addCircle(666.66f, 1150, 10, Path.Direction.CW); // 繪制眼睛,CW:順時針繪制, CCW:逆時針繪制
path.addCircle(733.33f, 1150, 10, Path.Direction.CW);
path.addRect(new RectF(600, 1200, 800, 1300), Path.Direction.CW);  // 身體
canvas.drawPath(path, mPaint);

效果圖如下:

最后,上文中Canvas示例的全部代碼如下:

public class StudyView extends View {

    private Paint mPaint;
    private Context mContext;

    public StudyView(Context context) {
        super(context);
        init(context);
    }

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

    private void init(Context context) {
        mContext = context;
        mPaint = new Paint();
        mPaint.setAntiAlias(true); // 消除鋸齒
        mPaint.setStrokeWidth(5); // 設置筆尖寬度
        mPaint.setStyle(Paint.Style.STROKE); // 不填充
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        /** 1、drawArc */

        // 繪制扇形
        canvas.drawArc(0, 0, 200, 200, 0, 90, true, mPaint);
        RectF rectF = new RectF(0, 0, 200, 200);
        canvas.drawArc(rectF, 180, 90, true, mPaint);

        // 繪制圓弧
        canvas.drawArc(300, 0, 500, 200, 0, 90, false, mPaint);
        RectF rectF1 = new RectF(300, 0, 500, 200);
        canvas.drawArc(rectF1, 180, 90, false, mPaint);

        /** 2、drawBitmap */
        Bitmap bitmap = BitmapFactory.decodeResource(mContext.getResources(), android.R.mipmap.sym_def_app_icon);
        // 繪制圖片
        canvas.drawBitmap(bitmap, 0, 300, null);
        // 將圖片拉伸平鋪在RectF矩形內
        canvas.drawBitmap(bitmap, null, new RectF(200, 300, 500, 500), null);
        // 截取圖片的四分之一拉伸平鋪在RectF矩形內
        canvas.drawBitmap(bitmap, new Rect(0, 0, bitmap.getWidth()/2, bitmap.getHeight()/2), new RectF(500, 300, 800, 500), null);

        Matrix matrix = new Matrix();
        matrix.postTranslate(800, 300); // 將bitmap平移到此位置
        canvas.drawBitmap(bitmap, matrix, mPaint);

        // 為防止oom,及時回收bitmap
        bitmap.recycle();

        /** 3、drawCircle */
        canvas.drawCircle(100, 700, 100, mPaint);

        /** 4、繪制一個點 */
        canvas.drawPoint(100, 700, mPaint); // 繪制一個點

        float[] points = new float[] {
            130, 700,
            160, 700,
            190, 700,
            210, 700,
            240, 700
        };

        canvas.drawPoints(points, 2, 4, mPaint); // 繪制一組點(代表跳過前兩個值,處理4個值,也就是實際繪制2個點)


        RectF rectF2 = new RectF(300, 600, 700, 800); // 創建一個RectF

        /** 5、drawOval 繪制橢圓 */
        canvas.drawOval(rectF2, mPaint);

        /** 6、drawRect 繪制矩形*/
        canvas.drawRect(rectF2, mPaint);
        canvas.drawRoundRect(rectF2, 60, 30, mPaint);

        /** 7、drawLine */
        canvas.drawLine(100, 820, 800, 820, mPaint);

        float[] lines = new float[]{
                100f, 850f, 800f, 850f,
                100f, 900f, 800f, 900f,
                100f, 950f, 800f, 950f
        };
        canvas.drawLines(lines, mPaint); // 按floats數組中,四個數為1組,繪制多條線


        /** 8、drawPath */

        // 使用 Path 繪制一個樓梯
        Path path = new Path();
        path.moveTo(0, 1000);
        path.lineTo(100, 1000);
        path.lineTo(100, 1100);
        path.lineTo(200, 1100);
        path.lineTo(200, 1200);
        path.lineTo(300, 1200);
        path.lineTo(300, 1300);
        path.lineTo(400, 1300);
        path.lineTo(400, 1400);
        path.close();
        canvas.drawPath(path, mPaint);

        // 使用 Path 繪制一個Android機器人

        // 繪制兩個觸角
        path.reset();
        path.moveTo(625, 1050);
        path.lineTo(650, 1120);
        path.moveTo(775, 1050);
        path.lineTo(750, 1120);

        path.addArc(new RectF(600, 1100, 800, 1300), 180, 180); // 繪制頭部
        path.addCircle(666.66f, 1150, 10, Path.Direction.CW); // 繪制眼睛,CW:順時針繪制, CCW:逆時針繪制
        path.addCircle(733.33f, 1150, 10, Path.Direction.CW);
        path.addRect(new RectF(600, 1200, 800, 1300), Path.Direction.CW);  // 身體
        canvas.drawPath(path, mPaint);
    }
}

完整效果圖如下:

其實Canvas除了可以繪制圖形之外,還可以繪制文字,Canvas的繪制文字的方法有drawText()、drawTextOnPath()、drawTextRun()等方法,在繪制文字是和Paint的結合更為緊密,所以講繪制文字的方法放在下文和Paint一起講可能效果會更好一些,好了,廢話不多說了,接下來咱們就開始Paint的篇章。

Paint

為了更為清晰的講解Paint的用法,先來新建一個自定義類,暫叫PaintStudyView,接下來創建一個它的大體骨架,在此類中定義了一些變量,變量的意義請見注釋:

public class PaintStudyView extends View {

    private Paint mTextPaint;  // 繪制文字的Paint
    private Paint mPointPaint; // 繪制參考點的Paint
    private Context mContext;

    private final static float Y_SPACE = 100; // y軸方向的間距

    public PaintStudyView(Context context) {
        super(context);
        init(context);
    }

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

    private void init(Context context) {
        mContext = context;
        mTextPaint = new Paint();
        mTextPaint.setAntiAlias(true); // 消除鋸齒
        mTextPaint.setStrokeWidth(1); // 設置筆尖寬度
        mTextPaint.setStyle(Paint.Style.FILL); // 填充
        mTextPaint.setTextSize(30);

        mPointPaint = new Paint();
        mPointPaint.setAntiAlias(true);
        mPointPaint.setStrokeWidth(5);
        mPointPaint.setColor(Color.RED); // 將參考點的Paint設置為紅色
        mPointPaint.setStyle(Paint.Style.STROKE);// 不填充
    }

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

Canvas的繪制文字的相關方法:

drawText()的重載方法

drawText() 是Canvas的繪制文字中的最長用的方法,它只能按照從左至右的普通方式來繪制文字。

  • drawText(String text, float x, float y, Paint paint)
  1. text:待繪制的文字內容
  2. x:文字繪制位置的x坐標
  3. y:文字繪制位置的y坐標
  4. paint:Paint畫筆,可以通過Paint.setTextAlign()來決定文字的方位,有:Paint.Align.LEFT(居左),Paint.Align.RIGHT(居右),Paint.Align.CENTER(居中)三個位置。
  • drawText(String text, int start, int end, float x, float y, Paint paint)
  1. start:代表從text中的第幾個字符開始截取繪制,包含第start個字符。
  2. end:代表截取到text的第幾個字符,不包含第end個字符。

例如:我是一個自定義View的控件,start=1,end=6,截取后為:是一個自定

下面兩個重載方法可以參考第二個很容易就能理解:

  • drawText(CharSequence text, int start, int end, float x, float y, Paint paint)
  • drawText(char[] text, int index, int count, float x, float y, Paint paint)

以下示例說明了文字的不同位置,同時也說明了第二個和第四個重載方法對字符串截取時的用法:

String str = "我是一個自定義View的控件";// 待繪制文字

float x = getWidth() / 2;
float y = 100;
canvas.drawPoint(x, y, mPointPaint); // 繪制參考點,便於觀察文字處於x,y坐標的位置,從而來學習setTextAlign()方法

mTextPaint.setTextAlign(Paint.Align.LEFT);
canvas.drawText(str, x, y, mTextPaint);

y += Y_SPACE;
canvas.drawPoint(x, y, mPointPaint);
mTextPaint.setTextAlign(Paint.Align.RIGHT);
canvas.drawText(str, 0, 6, x, y, mTextPaint);

y += Y_SPACE;
canvas.drawPoint(x, y, mPointPaint);
mTextPaint.setTextAlign(Paint.Align.CENTER);
canvas.drawText(str.toCharArray(), 1, 6, x, y, mTextPaint);

效果圖如下:

其中的紅點為額外添加的參考坐標,目的是為了突出setTextAlign中參數的位置。

drawTextOnPath()的重載方法

drawTextOnPath() 由方法名字我們就可以看出來他可以按照Path的走向來繪制文字,例如我們在path中傳入一個圓弧,那么繪制出來的文字走向就是圓弧狀的,是不是很酷,來看一下它的重載方法:

  • drawTextOnPath(String text, Path path, float hOffset, float vOffset, Paint paint)
  1. text:同drawText的第一個參數。
  2. path:Path參數,用法在前文已經說過了。
  3. hOffset:水平方向的偏移量。
  4. vOffset:垂直方向的偏移量。

關鍵點:有一點一定要提的就是,這里的hOffset是相對於path路徑的水平偏移量,而vOffset也是相對於path路徑的垂直偏移量,這么說可能還有點不清楚,結合下面的示例來說明,請仔細體會這里的意思:

// 1、下開口圓弧方向繪制文字
mTextPaint.setTextAlign(Paint.Align.LEFT);
y += Y_SPACE;
Path path = new Path();
path.addArc(new RectF(x - 150, y, x + 150, y + 300), 180,180);
canvas.drawPath(path, mPointPaint); // 參考弧度線
canvas.drawTextOnPath(str, path, 0, 0, mTextPaint); // 按照path路徑繪制文字,不偏移
canvas.drawTextOnPath(str, path, 30, 30, mTextPaint);// 向水平、垂直方向各偏移30
canvas.drawTextOnPath(str, path, 60, 60, mTextPaint);// 向水平、垂直方向各偏移60

// 2、上開口圓弧方向繪制文字
path.reset();
y += Y_SPACE;
path.addArc(new RectF(x - 150, y, x + 150, y + 300), 0, 180);
canvas.drawPath(path, mPointPaint); // 參考弧度線
canvas.drawTextOnPath(str, path, 0, 0, mTextPaint);
canvas.drawTextOnPath(str, path, 30, 30, mTextPaint);
canvas.drawTextOnPath(str, path, 60, 60, mTextPaint);
path.close();

// 3、豎直方向繪制文字
path.reset();
path.moveTo(200, y);
path.lineTo(200, y + 4 * Y_SPACE);
canvas.drawPath(path, mPointPaint); // 參考弧度線
canvas.drawTextOnPath(str, path, 0, 0, mTextPaint);
canvas.drawTextOnPath(str, path, 30, 60, mTextPaint);


y += Y_SPACE;
y += Y_SPACE;
y += Y_SPACE;
y += Y_SPACE;

// 4、水平方向繪制文字
path.reset();
path.moveTo(x, y);
path.lineTo(x + 4 * Y_SPACE, y);
canvas.drawPath(path, mPointPaint); // 參考弧度線
canvas.drawTextOnPath(str, path, 0, 0, mTextPaint);
canvas.drawTextOnPath(str, path, 30, 60, mTextPaint);

如下是效果圖,注意看圖片中的紅色部分,紅色的線是用代碼繪制出來的path參考線,紅色的箭頭是path的水平和垂直方向的走向,結合下圖可以更好的理解drawTextOnPath的hOffset和vOffset參數。

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

這個方法的套路想必不用解釋了。

drawTextRun()的重載方法

  • drawTextRun(char[] text, int index, int count, int contextIndex, int contextCount, float x, float y, boolean isRtl, Paint paint)
  • drawTextRun(CharSequence text, int start, int end, int contextStart, int contextEnd, float x, float y, boolean isRtl, Paint paint)

drawTextRun()可以文字的是從左到右還是從右到左的順序來繪制,其中倒數第二個參數isRtl就是用來控制方向的,true就是倒序繪制,false就是正序繪制,其他的參數就沒啥好說的了,這個方法用法比較簡單,這里就不貼代碼了。另外這個方法是在API 23才開始添加的,使用時要注意。

到目前為止,Canvas的常用用法基本介紹完了,接下來就可以着重來看Paint的使用了,Paint和Canvas兩者是不可分離的,兩者協作,相輔相成。所以在下面的用法示例中不免要用到Canvas的相關方法。

使用Paint測量文字的尺寸,定位文字

我們在開發自定義控件時,免不了要精確定位文字的文字,例如必須把文字放在某個區域的正中間,或者必須讓一行文字的幾何中心精確的處於某個點上,這時我們如果不懂這里的竅門可能就要盲目的試位置了,這樣一點一點試出來的位置很不可靠,可能換個屏幕尺寸位置就不對了,接下來怎么來看看怎么樣用最優雅的姿勢來精確的定位文字。

其實在水平方向的定位還比較好說,直接使用Paint.setTextAlign()就能搞定大多需求,主要是在水平方向上稍稍復雜一點,想要定位位置,首先需要先獲取文字的高度,要用到Paint的以下兩個方法:

  • float ascent():根據文字大小獲取文字頂端到文字基線的距離(返回的是負值)
  • float descent():根據文字大小獲取文字底部到文字基線的距離(返回的事正值)

有了這兩個方法那就非常好辦了,首先用代碼結合效果圖說明一下基線、ascent、descent和文字的關系:

y += Y_SPACE;
canvas.drawPoint(x, y, mPointPaint);
canvas.drawLine(x - 300, y, x+300, y, mPointPaint);
mTextPaint.setTextAlign(Paint.Align.CENTER);// 水平方向上讓文字居中
float ascent = mTextPaint.ascent(); // 根據文字大小獲取文字頂端到文字基線的距離(返回的是負值)
float descent = mTextPaint.descent(); // 根據文字大小獲取文字底部到文字基線的距離(返回的事正值)
canvas.drawLine(x - 300, y + ascent, x+300, y + ascent, mPointPaint);
canvas.drawLine(x - 300, y + descent, x+300, y + descent, mPointPaint);
canvas.drawText(str, x, y, mTextPaint);

效果圖如下,它們之間的關系注意看圖片里面的說明。(注:在這里感謝園友在截圖中指出的一處錯誤,現已修正)

接下來就讓文字的中心落在參考點上:

// 將文字的中心定位在參考點上
y += Y_SPACE;
canvas.drawPoint(x, y, mPointPaint);
canvas.drawText(str, x, y - ascent / 2 - descent / 2, mTextPaint);

效果圖如下,仔細看參考點(紅點)和文字的位置:

利用Paint.setShader()(着色器)繪制漸變色

使用 setShader() 方法可以添加漸變顏色也可以使用圖片作為背景,其參數是一個Shader類,傳入不同的Shader子類可以實現不同的漸變效果或者添加背景圖片,其子類有一下幾種:

  • LinearGradient:線性漸變
  • RadialGradient:放射狀漸變
  • SweepGradient:掃描漸變
  • BitmapShader:添加背景圖片
  • ComposeShader:多種Shader組合

上面接個Shader的子類在使用方式上都差不多,這里只用LinearGradient為例說明一下,並注意對LinearGradient構造器的最后一個參數傳入不同的參數對應的效果圖:

/* Shader 漸變 */
y = 100;
Shader shader = new LinearGradient(x - 50, y - 80, x + 50, y + 80,
        Color.parseColor("#FFCCBB"), Color.parseColor("#FF0000"), Shader.TileMode.CLAMP);
mTextPaint.setShader(shader);
canvas.drawRect(x - 500, y - 80, x + 500, y + 80, mTextPaint);

y += 3 * Y_SPACE;
Shader shader1 = new LinearGradient(x - 50, y - 80, x + 50, y + 80,
        Color.parseColor("#FFCCBB"), Color.parseColor("#FF0000"), Shader.TileMode.REPEAT);
mTextPaint.setShader(shader1);
canvas.drawRect(x - 500, y - 80, x + 500, y + 80, mTextPaint);

y += 3 * Y_SPACE;
Shader shader2 = new LinearGradient(x - 50, y - 80, x + 50, y + 80,
        Color.parseColor("#FFCCBB"), Color.parseColor("#FF0000"), Shader.TileMode.MIRROR);
mTextPaint.setShader(shader2);
canvas.drawRect(x - 500, y - 80, x + 500, y + 80, mTextPaint);

效果圖如下:

除了以上這些,Paint的用法還有很多很多,一時半會也列不完,博主在這也只能拋磚引玉,感興趣的朋友可查看官方API自行探索。


最后想說的是,本系列文章為博主對Android知識進行再次梳理,查缺補漏的學習過程,一方面是對自己遺忘的東西加以復習重新掌握,另一方面相信在重新學習的過程中定會有巨大的新收獲,如果你也有跟我同樣的想法,不妨關注我一起學習,互相探討,共同進步!

參考文獻:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM