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

*爱你&永不变心* 提交于 2021-01-21 06:33:31

问题


I have three TextViews "hi", "x" and "Hello World" which I would like to align in the bottom of the Hello World text (ie hi_x_World). Hello World is just one line but both layout_width and layout_height are set to wrap_content.

They have different font sizes so even though I can easily align the bottom of the boxes of the textviews, the text itself does not become aligned.

I found a different XML parameter app:layout_constraintBaseline_toBaselineOf="@+id/text that works when I only have one line in the TextView. However, when I have 2 or more lines (like in the Hello World TextView) the baseline that's considered is in the 'Hello' instead of 'World'.

Is there any way to change the setting to consider the baseline below the word "World" instead of "Hello" ?


回答1:


Second update: This is another way of looking at the first solution taken from this Stack Overflow answer that will also work with ConstraintLayout. This solution uses a custom TextView. The custom TextView returns the baseline of the last line of text in the TextView from the getBaseline() function instead of the baseline of the first line which is the default action. This is a nice, clean solution (IMO) that takes into account multi-line TextViews but also gravity, etc.

Kotlin version of 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)
    }
}

First update: This is an update to my answer below which is still a valid solution (IMO). This is an alternate approach that does not involve any Java/Kotlin code and can be accomplished just using XML.

Create an invisible wrap_content TextView that has the same font size as the "Hello World!" TextView. (You may also need to consider padding and margins depending upon the actual layout.) Constrain this new view to the bottom of "Hello World!", make it invisible and set the contents to something short that is guaranteed to occupy just one line. This will give you a target view that has the same base line as the last line of the "Hello World!" view.

Constrain the base lines of "hi" and "x" to the new invisible view. All views will now share the same base line and without coding.

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


First answer: As noted in another answer, there is no way to do this just using ConstraintLayout constraints. You will need to resort to a programmatic solution.

Within each TextView is a StaticLayout that can reveal a bit about the typography of the text. By referring to the static layout, padding can be added to the appropriate views to make the base lines align.

In this demo, the three TextViews simply have their tops aligned. Initially, the views look like this:

When the button is clicked, the base line locations are calculated and padding is added to the tops of the "hi" and "x" TextViews.

Details will vary with the implementation, but this is the general technique.

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>



回答2:


AFAIK, there is no way to accomplish this task using ConstraintLayout as of today.

If you know the content of the "helloWorldTextView" in advance, you might wanna split the lines into several textViews and then use app:layout_constraintBaselineToBaselineOf.

I know this is a tricky workaround, but it's the only way that comes to mind.




回答3:


You can use:

app:layout_constraintBottom_toBottomOf="@+id/helloworldtextid"

Also, don't forget to set the height to wrap_content so that the bottom of the TextViews line up to the bottom of the "Hello World" even when it wraps to two lines or more.

If you want it centered to "Hello World" when it is multi-line, consider adding

app:layout_constraintTop_toTopOf="@+id/helloworldtextid"

along with the bottom one. It will center the text to the multi-lined "Hello World" vertically.




回答4:


Use Guideline

Utility class representing a Guideline helper object for ConstraintLayout. Helper objects are not displayed on device (they are marked as View.GONE) and are only used for layout purposes. They only work within a ConstraintLayout.

A Guideline can be either horizontal or vertical:

a) Vertical Guidelines have a width of zero and the height of their ConstraintLayout parent

b) Horizontal Guidelines have a height of zero and the width of their 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.

Example

Code:-

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


来源:https://stackoverflow.com/questions/57610578/multi-line-text-views-alignment-to-their-baseline-constraintlayout-android-stud

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!