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
    }
}
相关推荐
超级小的大杯柠檬水2 小时前
更新gitignore后如何使其生效
gitee
随笔写12 小时前
SourceTree保姆级教程1:(克隆,提交,推送)
gitee
编程乐学3 天前
基于Android Studio 蜜雪冰城(奶茶饮品点餐)—原创
android·gitee·android studio·大作业·安卓课设·奶茶点餐
茜茜西西CeCe4 天前
移动技术开发:简单计算器界面
java·gitee·安卓·android-studio·移动技术开发·原生安卓开发
黎相思4 天前
操作系统迁移(CentOs -> Ubuntu)
linux·ubuntu·gitee·centos
茜茜西西CeCe4 天前
移动技术开发:登录注册界面
java·gitee·gradle·android studio·安卓·移动技术开发·原生安卓开发
冬田里的一把火35 天前
[Android][Reboot/Shutdown] 重启/关机 分析
android·gitee
毅凉5 天前
git笔记
gitee·github·gitcode
爱吃香蕉的阿豪6 天前
828华为云征文|Flexus X实例Docker+Jenkins+gitee实现CI/CD自动化部署-解放你的双手~
ci/cd·docker·gitee·华为云·jenkins