letterSpacing导致TextView文本被截断

一.背景介绍

(Android10 11目前有这个问题 Android15似乎有新的属性 但是没有可用的环境 没有验证)

简介

android:maxLines="1"

android:textAlignment="viewStart"

android:letterSpacing="0.04"

多个属性同时作用情况下 在系统为阿拉伯语情况下,显示英文文本字符串出现截断问题

UX需求:

1.有两个控件 address displayName

address自适应宽度 displayName在address之后占满剩余空间

2.系统为阿拉伯语情况下控件位置需要调换(父控件 android:layoutDirection="locale" 实现)

3.系统为英语情况下 如果控件有多余的宽度,文本要在Textview的内部靠左显示。

系统为阿拉伯语情况下 如果控件有多余的宽度,文本要在Textview的内部靠右显示。(android:textAlignment="viewStart"实现)

4.控件都只显示一行文本,当文本过长时,尾部显示...(android:maxLines="1" android:ellipsize="end"实现)

5.UX提供了字体文件 但是字体本身字符间距太小 需要通过代码设置letterSpacing(android:letterSpacing="0.04"实现)

二.xml实现

XML 复制代码
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="528dp"
    android:layout_height="140dp"
    android:background="#55DDDDDD"
    android:layoutDirection="locale"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/address"
        android:layout_width="50dp"
        android:layout_height="wrap_content"
        android:text="adress"
        android:textColor="#F00"
        app:layout_constraintEnd_toStartOf="@+id/displayName"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/displayName"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:ellipsize="end"
        android:letterSpacing="0.04"

        android:maxLines="1"
        android:text="This is long text This is long text This is long text This is long text This is long text This is long textThis is long text"
        android:textAlignment="viewStart"
        android:textDirection="locale"

        android:textSize="36sp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@+id/address"
        app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

三. 现象分析

在系统语言切换到阿拉伯的情况下 英文出现截断现象

当我们修改如下属性时 截断问题可以被解决

例如删除 android:maxLines="1"

例如 把android:textAlignment="viewStart" 改成textStart

例如删除android:letterSpacing="0.04"

但是为了满足UX的需求 这些属性我们都不能修改

另外 如果把displayName的文本改成阿拉伯语 也不会出现问题

还有一个现象 displayName的文本越长 被截断的文本就越多

还有我们将letterSpacing改的越小 被截断的现象越不明显

从以上的各种现象 可以推测,是Android系统在系统语言是阿拉伯语情况下 对于英文文本的letterSpacing计算有问题 导致其判断显示省略号的位置出错

后面调查发现 Android系统在系统语言是英文情况下 对于阿拉伯语文本的letterSpacing计算也有问题

四.解决思路

如果条件变化 我们可以改动如下情况任意一个

* 1.view textAlignment is TEXT_ALIGNMENT_VIEW_START

* 2.text is too long to display in textview

* 3.letterSpacing is not 0

* 4.ellipsize="end"

截断问题都不会发生

但如果无法修改上面的这几个状态 那么我们可以在这种情况下重新绘制text

或者动态修改TEXT_ALIGNMENT的属性值

下面介绍重新绘制text的方案

五.代码实现

Kotlin 复制代码
import android.content.Context
import android.graphics.Canvas
import android.text.Layout
import android.text.StaticLayout
import android.text.TextUtils
import android.util.AttributeSet
import android.view.View
import androidx.appcompat.widget.AppCompatTextView
import com.telenav.transformerhmi.uiframework.R

/**
 *
 * it will re draw text
 * when all of following conditions happen together
 * 1.view textAlignment is TEXT_ALIGNMENT_VIEW_START
 * 2.text is too long to display in textview
 * 3.letterSpacing is not 0
 * 4.ellipsize="end"
 * otherwise this is just a normal textview
 */


class AutoAlignmentViewStartTextView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = R.style.TextAppearance
) : AppCompatTextView(context, attrs, defStyleAttr) {

    override fun onDraw(canvas: Canvas) {
        if (textAlignment == View.TEXT_ALIGNMENT_VIEW_START && needEllipsis() && letterSpacing != 0f && ellipsize == TextUtils.TruncateAt.END) {

            // get Paint
            val paint = paint.apply {
                color = currentTextColor
                textSize = textSize
            }

            val text = text.toString()

            // get text content width
            val width = width - paddingLeft - paddingRight

            // create StaticLayout to deal with text which has ellipsis
            val layout = StaticLayout.Builder.obtain(text, 0, text.length, paint, width)
                .setMaxLines(maxLines)
                .setEllipsize(TextUtils.TruncateAt.END)  // Display ellipsis when the view size is exceeded
                .setAlignment(Layout.Alignment.ALIGN_NORMAL)  // Left-align text, but right-align by canvas movement
                .build()

            // text center vertical
            val y = (height - layout.height) / 2

            canvas.save()

            if (layoutDirection == View.LAYOUT_DIRECTION_RTL) {
                // Move the canvas so the text starts drawing from the right
                canvas.translate(paddingLeft.toFloat(), y.toFloat())
            }

            layout.draw(canvas)

            canvas.restore()
        } else {
            super.onDraw(canvas)
        }
    }


    private fun needEllipsis(): Boolean {
        val availableWidth = width - paddingStart - paddingEnd
        var currentWidth = 0f
        var lineCount = 0

        text.forEach { char ->
            currentWidth += paint.measureText(char.toString())
            if (currentWidth > availableWidth) {// Text occupies the entire line.
                lineCount++
                if (lineCount > maxLines) {
                    return true
                }
                currentWidth = paint.measureText(char.toString())
            }
        }

        if (currentWidth > 0) {
            lineCount++
        }
        // measured line count > maxLines
        return lineCount > maxLines
    }
}
相关推荐
jonyleek3 天前
JVS开源框架:工作流引擎代理中心的设计挑战与实现方案
java·gitee·开源·github·软件需求
Sheljoee.4 天前
【GitHub】基础入门步骤
开发语言·人工智能·python·单片机·gitee·github·嵌入式实时数据库
影阴5 天前
如何将本地项目上传至Gitee仓库(详细教程)
gitee
loveLifeLoveCoding6 天前
gitee 使用 webhoot 触发 Jenkins 自动构建
运维·gitee·jenkins
子蛟7 天前
Android Studio 安装过程
android·gitee·android studio
南鸢1.07 天前
IntelliJ IDEA 中上传项目到 Gitee 的完整指南
java·git·gitee·idea
一个很帅的帅哥8 天前
Mac在Typora配置PicGo图床,以github为例
macos·gitee·github·typora·mac·图床
謬熙8 天前
GitHub、Gitee、GitLab介绍
gitee·gitlab·github
..过云雨14 天前
gitee建立/取消关联仓库
gitee
寰梦14 天前
上传Gitee仓库流程图
gitee·流程图