Bootstrap

优先显示两边内容,中间TextView内容尾部省略号

我也不知道这个情景用什么内容来描述,就是我们在做一种卡片布局时,底部的内容是作者头像->作者信息->标签或者来源,然后pm要求两边内容显示完全,中间内容如果空间不够,尾部用...代替,需求乍一听没什么问题,ellipsize属性加maxLine="1"不就搞定了,然后出去试了一波不到两分钟就回来了,因为ellipsiize属性并没有生效,反而把末尾的内容挤没了。那么今天,博主就说出两种自己的思路,大家仅供参考。话不多说,先上图:

第一种解决方式:使用ConstraintLayout(不熟悉此布局的可以去学习下,实在不想学习就复制粘贴吧)

这会用到此布局的chain属性,在xml里面需要有省略号的控件设置app:layout_constraintHorizontal_chainStyle="packed",这样会优先显示另外的控件文案,代码如下

<androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
  >
  <TextView
      android:id="@+id/tv"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="关注好的反倒是地方"
      android:background="#0F0"
      app:layout_constraintTop_toTopOf="parent"
      app:layout_constraintStart_toStartOf="parent"
      app:layout_constraintEnd_toStartOf="@+id/tv2"
      />
    >
  <TextView
      android:id="@+id/tv2"
      android:layout_width="0dp"
      android:layout_height="wrap_content"
      android:text="你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好"
      android:ellipsize="end"
      android:layout_marginLeft="5dp"
      android:layout_marginRight="5dp"
      android:singleLine="true"
      android:textColor="@color/black"
      app:layout_constraintHorizontal_chainStyle="packed"
      android:background="#f00"
      app:layout_constraintTop_toTopOf="parent"
      app:layout_constraintStart_toEndOf="@+id/tv"
      app:layout_constraintEnd_toStartOf="@+id/tv3"

      />

  <TextView
      android:id="@+id/tv3"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="关注"
      android:background="#0F0"
      app:layout_constraintTop_toTopOf="parent"
      app:layout_constraintStart_toEndOf="@+id/tv2"
      app:layout_constraintEnd_toEndOf="parent"
      />
</androidx.constraintlayout.widget.ConstraintLayout>

第二种解决方式:先把两边的View需要的宽度算出来,给两个的View设置上width,然后把剩余的空间给到中间的View,比较复杂。代码如下:

xml布局:

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
  >
  <TextView
      android:id="@+id/tv4"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:background="#0f0"
    />
  <TextView
      android:id="@+id/tv5"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:maxLines="1"
      android:ellipsize="end"
      android:background="#f00"
    />
  <TextView
      android:id="@+id/tv6"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:background="#0f0"
    />
</LinearLayout>

java代码:

fun start() {
    // 获取屏幕宽度
    val manager: WindowManager = this.windowManager
    val outMetrics: DisplayMetrics = DisplayMetrics()
    manager.defaultDisplay.getMetrics(outMetrics);
    // 首先拿到布局的总宽度
    var layoutWidth = outMetrics.widthPixels

    // 需要先给TextView的text赋值,才能测量出总宽度
    tv4?.text = "这是左边的控件"
    // 先减去左边控件的宽度,为了拿到tv4的宽度,先进行测量
    tv4?.measure(0,0)
    val leftViewWidth = tv4?.measuredWidth
    if (leftViewWidth != null) {
        layoutWidth -= leftViewWidth
    }

    //然后用同样的方式减去右边的宽度
    tv6?.text = "这是右边的控件"
    tv6?.measure(0,0)
    val rightViewWidth = tv6?.measuredWidth
    if (rightViewWidth != null) {
        layoutWidth -= rightViewWidth
    }

    // 剩余的就是空间就都是中间View的了
    val lp = tv5?.layoutParams
    lp?.let {
        it.width = layoutWidth
    }
    tv5?.text = "你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好"
}

这样,效果同样是上面的效果啦,OK,到此为止,基本就把问题解决了,但是,ellipsize这个属性比较恶心,在有的版本或机型上,最后的三个省略号距离尾部会有一定间距,没法控制,如果UI走查比较严格,还得需要解决,我们的思路是算出每个文字的宽度,然后用剩余宽度除以每个文字的宽度,看看能放下几个字,然后剩余的手动用...拼接,代码如下:

    val text = "你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好"
    // 需要的宽度
    val needWidth = tv5.paint.measureText(text)
    if (needWidth > layoutWidth) {
        // 当前位置能放下的文字数量
        var stringNum = tv5.paint.breakText(text, true, layoutWidth.toFloat(), null)
        if (stringNum > 1) {
            // 因为要拼接... ,所以要减去一个文字的距离
            var subAuthText = text.substring(0, stringNum)
            // 这个StringUtils是用来判断是否以英文字母结尾
            if (StringUtils.checkEndForEnglish(subAuthText)) {
                // 如果截断的文字是以字母结尾,只截断一个字母的距离是不足以放下...的,所以默认截取三个字母
                var loopCount = 3
                while (loopCount > 0 && stringNum > 1) {
                    stringNum --
                    subAuthText = subAuthText.substring(0, stringNum)
                    if (!StringUtils.checkEndForEnglish(subAuthText)) {
                        break
                    }
                    loopCount --
                }
                // 如果loopCount大于0,代表文字后小于3个字母,为了兼容,还得再减去一个字的距离
                if (loopCount > 0 && stringNum > 1) {
                    stringNum --
                    subAuthText = subAuthText.substring(0, stringNum)
                }
            } else {
                stringNum --
                subAuthText = subAuthText.substring(0, stringNum)
            }
            val desc = StringBuilder().append(subAuthText)
                .append("...")
                .toString()
            tv5.text = desc
        }
    }
}

最后附上StringUtils的代码

object StringUtils {
    /**
     * 判断是否是英文字母结尾
     */
    fun checkEndForEnglish(str: String): Boolean {
        val c = str[str.length - 1]
        return (c in 'a'..'z' || c in 'A'..'Z')
    }
}

;