文字绘制 | Android 单行,多行文字绘制

绘制文本

  • 文字纵向居中的算法(Y轴居中),横向居中用 Paint.Align.CENTER 即可

    • 获取文字的上下左右位置,计算中心点,然后偏移即可:

      //文字位置
      aint.textAlign = Paint.Align.CENTER
      //获取文字的位置信息,相对于 baseLine 的左上右下位置
      paint.getTextBounds("哈哈哈", 0, "哈哈哈".length, rect)
      //计算文字中点
      val offset = (rect.top + rect.bottom) / 2
      
      canvas.drawText("哈哈哈", (width / 2).toFloat(), (height / 2).toFloat() - offset, paint)
      

优势是:让文字非常居中,不管在任何地方。

缺点:如果文字会变动,就会导致重新计算中心点,然后文字的位置可能会发生改变,这样看起来很不舒服。

  • 使用上线和下线,

    val fontMetrics = Paint.FontMetrics()
    
    
    paint.getFontMetrics(fontMetrics)
    val offset = (fontMetrics.ascent + fontMetrics.descent) / 2
    
    canvas.drawText("aaaa", (width / 2).toFloat(), (height / 2).toFloat() - offset, paint)
    

    使用这种方式,即时文字会变,它对应的位置也不会变

  • 让文字贴顶部

    paint.getFontMetrics(fontMetrics)
    val offset = (fontMetrics.ascent + fontMetrics.descent) / 2
    
    paint.textAlign = Paint.Align.LEFT
    canvas.drawText("aaaa", 0f, 0f - offset, paint)
    
  • 如果文字是居左的,并且文字稍微有点大,就会发现文字无法紧贴左边,解决:

    使用 getTextBounds 获取的 left 值作为偏移。这个 left 不会计算左边空白的地方。

    paint.getTextBounds("aaaa", 0, "aaaa".length, rect)
    paint.textSize = 150f
    paint.textAlign = Paint.Align.LEFT
    canvas.drawText("aaaa", -(rect.left.toFloat()), 0f, paint)
    

    让文字绘制的时候从 负的地方开始绘制即可。

  • 绘制多行文字

    class LinsTextView : View {
          
          
    
        val text = "Hilt 是 Android 的依赖注入库,是基于 Dagger 。可以说 Hilt 是专门为 Andorid 打造的。" +
                "\n" +
                "\u200B Hilt 创建了一组标准的 组件和作用域。这些组件会自动集成到 Android 程序中的生命周期中。在使用的时候可以指定使用的范围,事情作用在对应的生命周期当中。" +
                "————————————————\n" +
                "版权声明:本文为CSDN博主「345丶」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。" +
                "原文链接:https://blog.csdn.net/baidu_40389775/article/details/107095700"
    
    
        val textPaint = TextPaint()
    
    
        lateinit var staticLayout: StaticLayout
    
        constructor(context: Context?) : super(context)
    
        constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
    
        constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(
            context,
            attrs,
            defStyleAttr
        )
    
        init {
          
          
            textPaint.textSize = dp2px(15f)
        }
    
        override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
          
          
            super.onSizeChanged(w, h, oldw, oldh)
        /**
         * 4,左对齐
         * 5,文字空隙,默认为 1
         * 6,添加空隙
         * 7,纵向是否要加额外的高度
         */
            staticLayout = StaticLayout(
                text, textPaint, width, Layout.Alignment.ALIGN_NORMAL, 1f, 0f, false
            )
        }
    
        override fun onDraw(canvas: Canvas) {
          
          
            super.onDraw(canvas)
            staticLayout.draw(canvas)
        }
    
    }
    
  • 绘制多行文字,根据计算换行

    class LinsTextView : View {
          
          
    
    
        val text = "Hilt 是 Android 的依赖注入库,是基于 Dagger 。可以说 Hilt 是专门为 Andorid 打造的。" +
                "Hilt 创建了一组标准的 组件和作用域。这些组件会自动集成到 Android 程序中的生命周期中。在使用的时候可以指定使用的范围,事情作用在对应的生命周期当中。" +
                "版权声明:本文为CSDN博主「345丶」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。" +
                "原文链接:https://blog.csdn.net/baidu_40389775/article/details/107095700"
    
        val paint = Paint(Paint.ANTI_ALIAS_FLAG)
    
        var bitmap: Bitmap
    
        val cutWith = floatArrayOf()
    
        constructor(context: Context?) : super(context)
    
        constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
    
    
        constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(
            context,
            attrs,
            defStyleAttr
        )
    
        init {
          
          
            paint.textSize = dp2px(15f)
            bitmap = getAvatar(dp2px(100f))
        }
    
        override fun onDraw(canvas: Canvas) {
          
          
            super.onDraw(canvas)
            canvas.drawBitmap(bitmap, width - dp2px(100f), 100f, paint)
    
            /**
             * 2,是否正向绘制
             * 3,View 的宽度
             * 4,拿到截取的宽度
             * return 第一行的位置
             */
            var index = paint.breakText(text, true, width.toFloat(), cutWith)
            //绘制第一行
            canvas.drawText(text, 0, index, 0f, 50f, paint)
            //绘制第二行
            var oldIndex = index
            index = paint.breakText(
                text, index, text.length, true,
                (width - bitmap.width).toFloat(), cutWith
            )
            canvas.drawText(text, oldIndex, oldIndex + index, 0f, (50 + paint.fontSpacing), paint)
    
            //绘制第三行
            oldIndex = index
            index = paint.breakText(
                text, oldIndex, text.length, true,
                (width - bitmap.width).toFloat(), cutWith
            )
            canvas.drawText(text, oldIndex, oldIndex + index, 0f, (50 + (paint.fontSpacing * 2)), paint)
    
        }
    
    
        fun getAvatar(width: Float): Bitmap {
          
          
            val options = BitmapFactory.Options()
            //设置 true,就只会取到宽高
            options.inJustDecodeBounds = true
            //拿到宽高
            BitmapFactory.decodeResource(resources, R.drawable.avatar, options)
            //使用宽高,重新获取图片,对性能有一定好处
            options.inJustDecodeBounds = false
            options.inDensity = options.outWidth
            options.inTargetDensity = width.toInt()
            return BitmapFactory.decodeResource(resources, R.drawable.avatar, options)
        }
    }
    

在这里插入图片描述

​ 效果如上,如果旁边有别的东西就需要手动的换行,这种效果是使用 StaticLayout 做不到的。但是可以通过 breakText 做到。

​ 就是获取当前行能够显示多少文字,然后在显示多少文字,接着获取下一个可以显示多少。。以此类推。

​ 上面只是一种比较 low 的写法,能看清楚原理后,就可以通过 for 循环完成。获取到 文字的高度和底部位置,然后和图片的位置进行判断,以此确定要显示文字位置即可。

Guess you like

Origin blog.csdn.net/baidu_40389775/article/details/107182848