美文网首页Android自定义View安卓高级UI半栈工程师
Canvas中的书法家讲解与实战——Android高级UI

Canvas中的书法家讲解与实战——Android高级UI

作者: 9dfaf364d57f | 来源:发表于2019-05-25 13:29 被阅读12次

    目录

    一、前言

    二、Canvas中的书法家API

    三、实战

    四、写在最后

    一、前言

    canvas 的 API 方法相当之多,小盆友本篇文章之前已经分享了 “Canvas中的裁剪师”“Canvas中的绘图师”,今天分享的是文字方面的API。

    在分享前,小盆友啰嗦两句,有些童鞋说 canvas 的这几篇文章是初级文章和 “Android高级UI” 这几个字显得有些格格不入。小盆友借此解释下,canvas 的这几篇文章是作为 高级UI 文章的补充,是这系列文章中的 垫脚石,并非想做 “标题党” 来吸引流量(貌似一直也没什么流量😂)。

    啰嗦了这么多,来看看今天的实战效果图

    抖动的字符

    image

    二、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);
    

    效果图

    image
    (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);
    

    效果图

    image
    (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);
    

    效果图

    image
    (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);
    

    效果图

    image

    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);
    

    效果图

    image
    (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);
    

    效果图

    image

    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);
    

    效果图

    image
    (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);
    

    效果图

    image

    三、实战

    抖动的字符

    image

    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));
    }
    

    具体的函数图形如下


    image

    公式的最初原型来源于此博客,在此谢谢博主。

    看完这函数,可能有些童鞋比较懵逼,小盆友稍微给一些简单解释(毕竟难的我也说不清😂),我们将此公式抽象一下,便是如下形状:

    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吧😄

    欢迎加我微信,我们可以进行更多更有趣的交流


    image

    相关文章

      网友评论

        本文标题:Canvas中的书法家讲解与实战——Android高级UI

        本文链接:https://www.haomeiwen.com/subject/bmwazqtx.html