user3265561
user3265561

Reputation: 832

Align baseline of TextView to Guideline in Constraint Layout

Hopefully I'm not being completely thick here, but I'm unable to align the baseline of a TextView to a Guideline in a ConstraintLayout. It appears the guideline does not have a baseline, which is pretty annoying. Does anyone know how I might achieve this? Here's a bit of layout xml that doesn't work (this is within a ConstraintLayout):

                 <TextView
                    android:id="@+id/textToAlignBaseline"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginLeft="20dp"
                    android:maxLines="1"
                    android:paddingRight="6dp"
                    android:textAppearance="@style/TextStyles.Body"
                    app:layout_constraintBaseline_toBaselineOf="@+id/guidelineBottomMargin"
                    app:layout_constraintLeft_toLeftOf="parent" />

                <Button
                    android:id="@+id/buttonToAlignBottom"
                    android:layout_width="wrap_content"
                    android:layout_height="39dp"
                    android:layout_marginRight="20dp"
                    android:background="@drawable/selector_button_bg"
                    android:paddingLeft="16dp"
                    android:paddingRight="16dp"
                    android:text="@string/clickme"
                    android:textAppearance="@style/TextStyles.Body"
                    app:layout_constraintBottom_toTopOf="@+id/guidelineBottomMargin"
                    app:layout_constraintRight_toRightOf="parent" />

                <android.support.constraint.Guideline
                    android:id="@+id/guidelineBottomMargin"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:orientation="horizontal"
                    app:layout_constraintGuide_end="20dp" />

Upvotes: 5

Views: 2929

Answers (3)

Perqin
Perqin

Reputation: 855

I've found a workaround to align the text following exactly the Material Design guideline as mentioned by @robinst .

Firstly add an ImageView, with 0 as layout_width, your desired baseline as layout_height, no src and baselineAlignBottom set to true:

<ImageView
    android:id="@+id/typeTextBaseline"
    android:layout_width="0dp"
    android:layout_height="24dp"
    android:baselineAlignBottom="true"
    android:visibility="invisible"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent" />

Then you can align your TextView's baseline to this ImageView's baseline:

<TextView
    android:id="@+id/typeTextView"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginStart="16dp"
    android:textAppearance="@style/TextAppearance.MaterialComponents.Overline"
    app:layout_constraintBaseline_toBaselineOf="@id/typeTextBaseline"
    app:layout_constraintStart_toStartOf="parent" />

I found this workaround when searching all implementations of View's getBaseline method, and the ImageView's implementation is as follow:

public int getBaseline() {
    if (mBaselineAlignBottom) {
        return getMeasuredHeight();
    } else {
        return mBaseline;
    }
}

So, once you set baselineAlignBottom to true, the ImageView's baseline becomes its bottom edge and it is magically served as a guideline now:)

Upvotes: 0

robinst
robinst

Reputation: 31417

I had this question when I tried to implement a two-line list according to Material Design, where the "Two-line item" baseline should be 32dp from the top:

Material Design spec

It looks like a plain View or Guideline doesn't have a baseline because it returns -1 from getBaseline. So what I ended up with is using an invisible TextView like this:

<TextView
    android:id="@+id/guideline"
    android:layout_width="match_parent"
    android:layout_height="1dp"
    android:layout_marginTop="32dp"
    android:textSize="0sp"
    android:visibility="invisible"
    app:layout_constraintTop_toTopOf="parent" />

And then use app:layout_constraintBaseline_toBaselineOf="@id/guideline" for aligning the other view to it.

Upvotes: 5

user3265561
user3265561

Reputation: 832

I think I might have come up with a solution, I tried extending Baseline and returning 0 from 'getBaseline' but that was never called, so I tried extending AppCompatButton instead and returning 'getMeasuredHeight' from 'getBaseline' instead (just like ImageView does when 'baselineAlignBottom' is used) and this seems to work correctly now. The TextView just needs changing to align its baseline to the button instead of the guideline. I need to clean it up with attributes similar to ImageView but this is what I've got for now:

public class ButtonBottomBaseline extends android.support.v7.widget.AppCompatButton {
    public ButtonBottomBaseline(Context context) {
        super(context);
    }

    public ButtonBottomBaseline(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public ButtonBottomBaseline(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    public int getBaseline() {
        return getMeasuredHeight();
    }
}

Upvotes: 2

Related Questions