Java自学者论坛

 找回密码
 立即注册

手机号码,快捷登录

恭喜Java自学者论坛(https://www.javazxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,会员资料板块,购买链接:点击进入购买VIP会员

JAVA高级面试进阶训练营视频教程

Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程Go语言视频零基础入门到精通Java架构师3期(课件+源码)
Java开发全终端实战租房项目视频教程SpringBoot2.X入门到高级使用教程大数据培训第六期全套视频教程深度学习(CNN RNN GAN)算法原理Java亿级流量电商系统视频教程
互联网架构师视频教程年薪50万Spark2.0从入门到精通年薪50万!人工智能学习路线教程年薪50万大数据入门到精通学习路线年薪50万机器学习入门到精通教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程MySQL入门到精通教程
查看: 602|回复: 0

Android ImageSpan与TextView中的text居中对齐问题解决(无论TextView设置行距与否)

[复制链接]
  • TA的每日心情
    奋斗
    2024-11-24 15:47
  • 签到天数: 804 天

    [LV.10]以坛为家III

    2053

    主题

    2111

    帖子

    72万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    726782
    发表于 2021-4-24 14:13:02 | 显示全部楼层 |阅读模式

     

     

    先解释一个类:Paint.FontMetrics,它表示绘制字体时的度量标准。google的官方api文档对它的字段说明如下:

     

    ascent: 字体最上端到基线的距离,为负值。
    descent:字体最下端到基线的距离,为正值。
    看下图:
     
    中间那条线就是基线,基线到上面那条线的距离就是ascent,基线到下面那条线的距离就是descent。
     
    回到主题,我们要让imagespan与text对齐,只需把imagespan放到descent线和ascent线之间的中间位置就可以了。实现方式为重写ImageSpan类的draw方法。最终实现方法如下:
    @Override
    public void draw(@NonNull Canvas canvas, CharSequence text,
                     int start, int end, float x,
                     int top, int y, int bottom, @NonNull Paint paint) {
         // image to draw
        Drawable b = getDrawable();
        // font metrics of text to be replaced
        Paint.FontMetricsInt fm = paint.getFontMetricsInt();
        int transY = (y + fm.descent + y + fm.ascent) / 2 
                - b.getBounds().bottom / 2;
        
        canvas.save();
        canvas.translate(x, transY);
        b.draw(canvas);
        canvas.restore();
    }
     
    解释下形参:
    x,要绘制的image的左边框到textview左边框的距离。
    y,要替换的文字的基线坐标,即基线到textview上边框的距离。
    top,替换行的最顶部位置。
    bottom,替换行的最底部位置。注意,textview中两行之间的行间距是属于上一行的,所以这里bottom是指行间隔的底部位置。
    paint,画笔,包含了要绘制字体的度量信息。
    这几个参数含义在代码中找不到说明,写了个demo测出来的。top和bottom参数只是解释下,函数里面用不上。
    然后解释下代码逻辑:
    getDrawable获取要绘制的image,getBounds是获取包裹image的矩形框尺寸;
    y + fm.descent得到字体的descent线坐标;
    y + fm.ascent得到字体的ascent线坐标;
    两者相加除以2就是两条线中线的坐标;
    b.getBounds().bottom是image的高度(试想把image放到原点),除以2即高度一半;
    前面得到的中线坐标减image高度的一半就是image顶部要绘制的目标位置;
    最后把目标坐标传递给canvas.translate函数就可以了,至于这个函数的理解先不管了。
     
      
    原理上大致就这样了,最后提供本文提出问题的最终解决方案,使用自定义的ImageSpan类,只需重写它的draw函数,代码如下:
    public class CenteredImageSpan extends ImageSpan {
        
        public CenteredImageSpan(Context context, final int drawableRes) {
            super(context, drawableRes);
        }
    
        @Override
        public void draw(@NonNull Canvas canvas, CharSequence text,
                         int start, int end, float x,
                         int top, int y, int bottom, @NonNull Paint paint) {
            // image to draw
            Drawable b = getDrawable();
            // font metrics of text to be replaced
            Paint.FontMetricsInt fm = paint.getFontMetricsInt();
            int transY = (y + fm.descent + y + fm.ascent) / 2
                    - b.getBounds().bottom / 2;
    
            canvas.save();
            canvas.translate(x, transY);
            b.draw(canvas);
            canvas.restore();
        }
    }

     

    最后看一下效果图:
     
     
    哎...今天够累的,签到来了1...
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|小黑屋|Java自学者论坛 ( 声明:本站文章及资料整理自互联网,用于Java自学者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2025-1-12 13:23 , Processed in 0.059092 second(s), 27 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表