多行文本视图如何对齐到它们的基线(ConstraintLayout Android Studio)

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

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

我发现了一个不同的XML参数app:layout_constraintBaseline_toBaselineOf="@+id/text,当TextView中只有一行时有效。然而,当我有两行或更多行时(例如在Hello World TextView中),被考虑的基线在“Hello”而不是“World”。

是否有任何方法可以更改设置以考虑单词“World”下面的基线而不是“Hello”?

enter image description here


你能展示一下你的XML和代码,并尝试用像上面那样的图片展示你想要实现的内容吗? - androidXP
这是一个支持本地化的功能请求,请点击此处查看。 - gmk57
4个回答

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

BaselineLastLineTextView的Kotlin版本

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)
    }
}

第一次更新:这是对我下面答案的更新,该答案仍然是有效的解决方案(在我看来)。这是一种不涉及任何Java / Kotlin代码的替代方法,只需使用XML即可完成。
创建一个不可见的wrap_content TextView,其字体大小与“Hello World!”TextView相同。(根据实际布局,您可能还需要考虑填充和边距。)将此新视图约束到“Hello World!”底部,使其不可见,并将内容设置为某些短语,保证仅占用一行。这将为您提供具有与“Hello World!”视图的最后一行相同基线的目标视图。
将“hi”和“x”的基线约束到新的不可见视图。所有视图现在都共享相同的基线,而无需编码。

enter image description here

<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>


< p >< em >第一个答案:正如另一个答案中所提到的,仅使用ConstraintLayout约束无法实现此目标。您需要采用编程解决方案。

在每个TextView中,都有一个StaticLayout,它可以揭示文本的排版信息。通过参考静态布局,可以向适当的视图添加填充以使基线对齐。

在此演示中,三个TextViews只需将其顶部对齐即可。最初,视图如下所示:

enter image description here

当按钮被点击时,将计算基线位置并向“hi”和“x”TextViews的顶部添加填充。

enter image description here

具体实现可能会有所不同,但这是一般的技术。

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>

hiddenView 的技巧很好用,谢谢!似乎没有必要将其设置为 invisible,只需省略 android:text 属性即可。 - gmk57

3
据我所知,目前无法使用ConstraintLayout完成此任务。
如果您事先知道“helloWorldTextView”的内容,您可能想将行拆分为几个textViews,然后使用app:layout_constraintBaselineToBaselineOf
我知道这是一个棘手的解决方法,但这是我能想到的唯一方法。

0

你可以使用:

app:layout_constraintBottom_toBottomOf="@+id/helloworldtextid"

此外,不要忘记将高度设置为wrap_content,这样即使TextView换行到两行或更多行,它们的底部也会与“Hello World”的底部对齐。
如果您希望在多行时将其居中于“Hello World”,请考虑添加。
app:layout_constraintTop_toTopOf="@+id/helloworldtextid"

连同底部一起。它将垂直地将多行的“Hello World”文本居中。


你可以使用verticalBias属性来改变居中方式(默认为完美居中=0.5)。 - Kushan
2
这不是OP所要求的。你只是建议将第一个TextView的底部与所需TextView的底部对齐,而不是它的基线。 - reavcn

-1

使用Guideline

实用程序类表示ConstraintLayout的Guideline帮助程序对象。帮助程序对象不会在设备上显示(它们被标记为View.GONE),仅用于布局目的,它们只能在ConstraintLayout中起作用。

Guideline可以是水平或垂直的

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

b) 水平指南线的高度为零,宽度为其ConstraintLayout父容器的宽度

然后,Widget可以约束到Guideline,允许从一个Guideline轻松地定位多个Widget,或者通过使用百分比定位来实现反应式布局行为。

示例 enter image description here

代码:-

<?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>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接