多行文本视图与其基线对齐(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_width
和 layout_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) Horizontal 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>
我有三个 TextView "hi"
、"x"
和 "Hello World"
,我想将它们对齐到 Hello World 文本的底部(即 hi_x_World)。 Hello World 只有一行,但 layout_width
和 layout_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) Horizontal 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>