多行文本视图与其基线对齐(ConstraintLayout Android Studio)

Multi-line text-views alignment to their baseline (ConstraintLayout Android Studio)

我有三个 TextView "hi""x""Hello World",我想将它们对齐到 Hello World 文本的底部(即 hi_x_World)。 Hello World 只有一行,但 layout_widthlayout_height 都设置为 wrap_content.

它们有不同的字体大小,所以即使我可以轻松对齐文本视图框的底部,文本本身也不会对齐。

我发现了一个不同的 XML 参数 app:layout_constraintBaseline_toBaselineOf="@+id/text,当我在 TextView 中只有一行时它可以工作。但是,当我有 2 行或更多行时(如在 Hello World TextView 中),所考虑的基线位于 'Hello' 而不是 'World'.

有什么方法可以更改设置以考虑单词 "World" 下方的基线而不是 "Hello" 吗?

您可以使用:

app:layout_constraintBottom_toBottomOf="@+id/helloworldtextid"

此外,不要忘记将高度设置为 wrap_content,以便 TextViews 的底部与 "Hello World" 的底部对齐,即使它换行到两行或更多行。

多行时如果希望居中到"Hello World",考虑添加

app:layout_constraintTop_toTopOf="@+id/helloworldtextid"

连同最下面的那个。它将文本居中到多行 "Hello World" 垂直。

据我所知,截至目前,无法使用 ConstraintLayout 完成此任务。

如果你事先知道 "helloWorldTextView" 的内容,你可能想将这些行拆分成几个 textView,然后使用 app:layout_constraintBaselineToBaselineOf

我知道这是一个棘手的解决方法,但这是我想到的唯一方法。

第二次更新:这是从 Stack Overflow answer 中获取的第一个解决方案的另一种方式,它也适用于 ConstraintLayout。此解决方案使用自定义 TextView。自定义 TextView returns 来自 getBaseline() 函数的 TextView 最后一行文本的基线而不是第一行的基线,这是默认操作。这是一个很好的、干净的解决方案 (IMO),它考虑了多行 TextViews 以及重力等

Kotlin 版本的 BaselineLastLineTextView

class BaselineLastLineTextView @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null
) : AppCompatTextView(context, attrs) {

    override fun getBaseline(): Int {
        val layout = layout ?: return super.getBaseline()
        val baselineOffset = super.getBaseline() - layout.getLineBaseline(0)
        return baselineOffset + layout.getLineBaseline(layout.lineCount - 1)
    }
}

第一次更新:这是对我下面的答案的更新,它仍然是一个有效的解决方案 (IMO)。这是一种不涉及任何 Java/Kotlin 代码的替代方法,只需使用 XML.

即可完成

创建一个不可见的 wrap_content TextView,其字体大小与“Hello World!”相同TextView。 (您可能还需要根据实际布局考虑填充和边距。)将这个新视图限制在“Hello World!”的底部,使其成为 invisible 并将内容设置为保证占用的短内容只有一行。这将为您提供一个与“Hello World!”的最后一行具有相同基线的目标视图。查看。

将“hi”和“x”的基线约束到新的 invisible 视图。所有视图现在将共享相同的基线且无需编码。

<androidx.constraintlayout.widget.ConstraintLayout 
    android:id="@+id/layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/hiddenView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="A"
        android:textSize="50sp"
        android:visibility="invisible"
        app:layout_constraintBottom_toBottomOf="@id/helloView"
        app:layout_constraintEnd_toEndOf="parent" />

    <TextView
        android:id="@+id/hiView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="hi"
        android:textSize="46sp"
        app:layout_constraintBaseline_toBaselineOf="@id/hiddenView"
        app:layout_constraintEnd_toStartOf="@+id/xView"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="@id/helloView" />

    <TextView
        android:id="@+id/xView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="x"
        android:textSize="36sp"
        app:layout_constraintBaseline_toBaselineOf="@id/hiddenView"
        app:layout_constraintEnd_toStartOf="@+id/helloView"
        app:layout_constraintStart_toEndOf="@+id/hiView"
        app:layout_constraintTop_toTopOf="@id/helloView" />

    <TextView
        android:id="@+id/helloView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello\nWorld!"
        android:textSize="50sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@+id/xView"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="24dp"
        android:onClick="onClick"
        android:text="Adjust Base Lines"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/helloView" />


</androidx.constraintlayout.widget.ConstraintLayout>


第一个答案: 如另一个答案所述,仅使用 ConstraintLayout 约束无法做到这一点。 您将需要求助于程序化解决方案。

每个 TextView 是一个 StaticLayout that can reveal a bit about the typography 文本。参考静态布局,可以在适当的视图中添加padding,使基线对齐。

在此演示中,三个 TextView 只是让它们的顶部对齐。最初,视图如下所示:

单击按钮时,会计算基线位置,并在“hi”和“x”的顶部添加填充 TextViews

细节会因实施而异,但这是通用技术。

MainActivity.kt

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }

    fun onClick(view: View) {
        button.isEnabled = false
        // Get the StaticLayout from the TextView
        val layout = helloView.layout

        // Get the base line location for last line of Hello World! TextView, "hi" and "x"
        val helloBaseLIne = layout.getLineBaseline(layout.lineCount - 1)
        val hiBaseLine = hiView.layout.getLineBaseline(0)
        val xBaseLine = xView.layout.getLineBaseline(0)
        
        // Shift "hi" and "x" down so base lines match that of hello world!
        hiView.updatePadding(top = helloBaseLIne - hiBaseLine)
        xView.updatePadding(top = helloBaseLIne - xBaseLine)
    }
}

activity_main.xml

<androidx.constraintlayout.widget.ConstraintLayout 
    android:layout_width="match_parent"
    android:id="@+id/layout"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/hiView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="hi"
        android:textSize="46sp"
        app:layout_constraintTop_toTopOf="@id/helloView"
        app:layout_constraintEnd_toStartOf="@+id/xView"
        app:layout_constraintStart_toStartOf="parent"/>

    <TextView
        android:id="@+id/xView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="x"
        android:textSize="36sp"
        app:layout_constraintTop_toTopOf="@id/helloView"
        app:layout_constraintEnd_toStartOf="@+id/helloView"
        app:layout_constraintStart_toEndOf="@+id/hiView" />

    <TextView
        android:id="@+id/helloView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello\nWorld!"
        android:textSize="50sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@+id/xView"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="24dp"
        android:text="Adjust Base Lines"
        android:onClick="onClick"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/helloView" />


</androidx.constraintlayout.widget.ConstraintLayout>

使用指南

Utility class 表示 ConstraintLayout 的 Guideline 辅助对象。助手对象不会显示在设备上(它们被标记为 View.GONE)并且仅用于布局目的。它们仅在 ConstraintLayout 中工作。

辅助线可以是水平的也可以是垂直的:

a) 垂直指南的宽度为零,其 ConstraintLayout 父项的高度为

b) Horizo​​ntal Guidelines 的高度为零,其宽度为 ConstraintLayout parent

Widgets can then be constrained to a Guideline, allowing multiple widgets to be positioned easily from one Guideline, or allowing reactive layout behavior by using percent positioning.

例子

代码:-

<?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="match_parent"
    android:layout_height="match_parent">

    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/guideline"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintGuide_percent=".4"
        />

    <TextView
        android:id="@+id/textViewHi"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toLeftOf="parent"
        android:layout_marginLeft="50dp"
        android:text="hi"
        android:textSize="30sp"
        app:layout_constraintBottom_toTopOf="@+id/guideline" />
    <TextView
        android:id="@+id/textViewX"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toLeftOf="@+id/textViewHi"
        android:layout_marginLeft="50dp"
        android:text="x"
        android:textSize="30sp"
        app:layout_constraintBottom_toTopOf="@+id/guideline" />

    <TextView
        android:id="@+id/textViewHelloWorld"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toLeftOf="@+id/textViewX"
        android:layout_marginLeft="50dp"
        android:text="Hello World"
        android:textSize="45sp"
        app:layout_constraintBottom_toTopOf="@+id/guideline" />

</androidx.constraintlayout.widget.ConstraintLayout>