目錄
一、前言
二、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:管理 正弦函數 的垂直偏移量;
在我們這裏的場景中,主要控制兩個參數:
- A 的振幅變動,上面函數圖中在靠近 x=0 的地方函數的震動幅度變大,所以我們將 x的值 考慮進 A的計算中,並且以分母的形式(分母越大,數值越小;分母越小,數值越大)
- 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吧😄
歡迎加我微信,我們可以進行更多更有趣的交流