Canvas中的書法家講解與實戰——Android高級UI

目錄

一、前言

二、Canvas中的書法家API

三、實戰

四、寫在最後

一、前言

canvas 的 API 方法相當之多,小盆友本篇文章之前已經分享了 “Canvas中的裁剪師”“Canvas中的繪圖師”,今天分享的是文字方面的API。

在分享前,小盆友囉嗦兩句,有些童鞋說 canvas 的這幾篇文章是初級文章和 “Android高級UI” 這幾個字顯得有些格格不入。小盆友藉此解釋下,canvas 的這幾篇文章是作爲 高級UI 文章的補充,是這系列文章中的 墊腳石,並非想做 “標題黨” 來吸引流量(貌似一直也沒什麼流量😂)。

囉嗦了這麼多,來看看今天的實戰效果圖

抖動的字符

二、Canvas中的書法家API

1、drawText(四個重載方法)

(1)第一個 drawText 函數

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

描述: 在座標爲 (x,y) 處繪製 text 字符串。

舉個例子:

private static final String CONTENT = "zinc 猛猛的小盆友";

canvas.drawText(CONTENT, -300, -500, mPaint);

效果圖


(2)第二個 drawText 函數

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

描述: 在座標爲 (x,y) 處繪製字符串text,從下標爲start的字符開始,到下標爲 (end-1) 的字符終止。

舉個例子:

private static final String CONTENT = "zinc 猛猛的小盆友";

// 繪製內容爲從CONTENT的第四個字符開始,到CONTENT最後一個字符
canvas.drawText(CONTENT, 3, CONTENT.length(), -300, -400, mPaint);

效果圖


(3)第三個 drawText 函數

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

描述: 在座標爲 (x,y) 處繪製 text,從下標爲start開始,繪製count個字符。

舉個例子

private static final char[] C = "https://github.com/zincPower/UI2018".toCharArray();

canvas.drawText(C, 0, C.length, -300, -100, mPaint);
canvas.drawText(C, 5, 10, -300, 0, mPaint);

效果圖


(4)第四個 drawText 函數

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

描述: 在座標爲 (x,y) 處繪製 text,從下標爲start的字符開始,到下標爲 (end-1) 的字符終止。

舉個例子

private static final CharSequence SEQ = "https://blog.csdn.net/weixin_37625173";

canvas.drawText(SEQ, 0, SEQ.length(), -300, 300, mPaint);
canvas.drawText(SEQ, 6, 20, -300, 400, mPaint);

效果圖

2、drawTextOnPath (兩個重載方法)

(1)第一個 drawTextOnPath 函數

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

描述:路徑path 上繪製 text。

特殊參數說明:
1)hOffset:水平偏移量
2)vOffset:垂直偏移量

舉個例子

private static final String CONTENT = "zinc 猛猛的小盆友";

// mPath 是一個貝塞爾曲線繪製的路徑
canvas.drawTextOnPath(CONTENT, mPath, 0, 0, mPaint);

效果圖


(2)第二個 drawTextOnPath 函數

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

描述:路徑path 上繪製 text。

特殊參數說明:
1)index:從下標爲index的字符開始繪製
2)count:繪製字符的個數
3)hOffset:水平偏移量
4)vOffset:垂直偏移量

例子

private static final char[] C = "https://blog.csdn.net/weixin_37625173".toCharArray();

// 從下標爲2的字符(即第三個字符)開始,繪製20個字符
canvas.drawTextOnPath(C, 2, 20, mPath, 0, 0, mPaint);

效果圖

3、drawTextRun

這個方法不做過多的解釋,因爲在實際開發中使用可以說較少。簡單概括這個方法的作用,他是爲了處理一些語言文字(例如:阿拉伯語),當一個字在一個詞語中,會受左右的字影響而進行變形的情況。

這方面的語言小盆友不懂,所以沒法舉出嚴謹的例子,請有需要的童鞋移步Demo中自行體會。

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

4、drawPosText(兩個重載方法)

(1)第一個 drawPosText 函數

public void drawPosText(@NonNull String text, @NonNull @Size(multiple = 2) float[] pos,
            @NonNull Paint paint)

描述: 在pos對應的座標上繪製text。

舉個例子

private static final String CONTENT = "猛猛的小盆友";
private static final float[] pos1 = new float[]{
        -300, -600,
        -250, -500,
        -200, -400,
        -150, -300,
        -100, -200,
        -50, -100,
};

// 每個字符 和 pos的座標要一一對應的上,否則crash
canvas.drawPosText(CONTENT, pos1, mPaint);

效果圖


(2)第二個 drawPosText 函數

public void drawPosText(@NonNull char[] text, int index, int count,
            @NonNull @Size(multiple = 2) float[] pos,
            @NonNull Paint paint)

描述: 在pos對應的座標上繪製text,從下標爲index的字符開始,繪製count個。

舉個例子

private static final String CONTENT = "猛猛的小盆友";
private static final float[] pos2 = new float[]{
        -300, 100,
        -250, 200,
        -200, 300,
        -150, 400,
        -100, 500,
};

canvas.drawPosText(CONTENT.toCharArray(), 1, 4, pos2, mPaint);

效果圖

三、實戰

抖動的字符

Github入口:傳送門

編碼思路
在這一實戰中,其實 drawTextOnPath 反倒不是主角,他只是負責在我們的 path 上將文字繪出即可,所以童鞋們知道了最主要的是 path 的確定。

獲取 path 上的點,是通過如下的函數獲取

private float calculateY(float x) {
    double a = Math.pow(4 / (4 + Math.pow(4 * x / mLength, 4)), 2.5f) * mA;
    return (float) (a * Math.sin(Math.PI * x / 200 - m));
}

具體的函數圖形如下


公式的最初原型來源於此博客,在此謝謝博主。

看完這函數,可能有些童鞋比較懵逼,小盆友稍微給一些簡單解釋(畢竟難的我也說不清😂),我們將此公式抽象一下,便是如下形狀:

A*sin(w*x+m)+k

這就是我們在初中學的三角函數:正弦函數sin。我們羅列下幾個參數的作用:

  • A:管理 正弦函數 的振幅,即上下襬動的幅度;
  • w:管理 正弦函數 的水平收縮幅度
  • m:管理 正弦函數 的水平偏移量
  • k:管理 正弦函數 的垂直偏移量

在我們這裏的場景中,主要控制兩個參數:

  1. A 的振幅變動,上面函數圖中在靠近 x=0 的地方函數的震動幅度變大,所以我們將 x的值 考慮進 A的計算中,並且以分母的形式(分母越大,數值越小;分母越小,數值越大)
  2. m 的水平偏移,正弦的一個週期是 2π,所以我們這裏的曲直只需要從 [0-2π] 的一個範圍即可。

讓字符串搖擺起來
經過上面的簡單分析,我們需要的各種零件也都準備好了,最後加入我們再熟悉不過的 屬性動畫,就可以讓這條 路徑path 動起來。路徑path 動起來,會導致繪製在上面文字也動起來。

mAnimator = ValueAnimator.ofFloat(0, (float) (2 * Math.PI));
mAnimator.setInterpolator(new LinearInterpolator());
mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        float progress = (float) animation.getAnimatedValue();
        m = progress;
        mA = (float) (1 - progress / (2 * Math.PI)) * A;
        invalidate();
    }
});
mAnimator.setDuration(1000);

四、寫在最後

這次的文章較爲簡單和基礎,只是小盆友有點強迫症,必須要把 canvas 的每個API都過一遍和記錄一下。

如果覺得文章對你有所啓發,請給我個贊吧,如果發現有那些欠妥的地方,請留言區與我討論,我們共同進步。

高級UI系列的Github地址:請進入傳送門,如果喜歡的話給我一個star吧😄

歡迎加我微信,我們可以進行更多更有趣的交流


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章