0

I have two text views, at one instance only one text view should be present and at another instance both the text should be present. When only one one text view is present it should be aligned center.

<?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:id="@+id/disp"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="?attr/background_color">

    <ImageView
        android:id="@+id/dev_ic"
        style="@style/dev_list"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/dev_name"
        style="@style/dev_title"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        app:layout_constraintStart_toEndOf="@+id/dev_ic"
        app:layout_constraintTop_toTopOf="@id/dev_ic"
        tools:text="TEXT" />

    <TextView
        android:id="@+id/dev_connect_state"
        android:visibility="gone"
        android:text="@string/connecting"
        style="@style/connection_state"
        app:layout_constraintStart_toStartOf="@id/dev_name"
        app:layout_constraintTop_toBottomOf="@id/dev_name" />

      <View
        style="@style/separator"
        android:id="@+id/separator"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/dev_ic" />
</androidx.constraintlayout.widget.ConstraintLayout>

Styles.xml :

<style name="dev_list">
    <item name="android:layout_width">@dimen/icon_size</item>
    <item name="android:layout_height">@dimen/icon_size</item>
    <item name="android:layout_marginStart">@dimen/element_margin</item>
</style>


<style name="dev_title">
    <item name="android:layout_width">wrap_content</item>
    <item name="android:layout_height">wrap_content</item>
    <item name="android:ellipsize">end</item>
    <item name="android:textColor">?attr/text_color</item>
    <item name="android:textSize">@dimen/small_text_size</item>
</style>

<style name="connection_state">
    <item name="android:layout_width">wrap_content</item>
    <item name="android:layout_height">wrap_content</item>
    <item name="android:ellipsize">end</item>
    <item name="android:textColor">@color/primary</item>
    <item name="android:textSize">@dimen/small_text_size</item>
</style>

I have to set app:layout_constraintBottom_toBottomOf="@id/dev_ic" (sets to the middle, adjacent to the image view) for text view with id : dev_name programmatically..

@BindView(R2.id.disp)
ConstraintLayout mDevItem;

void alignDeviceNameToCenter(boolean isAlignCenter) {
            ConstraintSet constraintSet = new ConstraintSet();
            constraintSet.clone(mDevItem);
            if (isAlignCenter) {
                Log.d("TAG","Going to align center");
                constraintSet.connect(R2.id.dev_name, ConstraintSet.BOTTOM, R2.id.dev_ic, ConstraintSet.BOTTOM, 0);
            } else {
                Log.d("TAG","Going to clear the center alignment");
                constraintSet.clear(R2.id.dev_name, ConstraintSet.BOTTOM);
            }
            constraintSet.applyTo(mDevItem);
        }

Though I could, see the prints, the Text view is not aligned center to the imageview. Whereas when I set the app:layout_constraintBottom_toBottomOf="@id/dev_ic in xml, it gets aligned. But I couldn't do the same programmatically.

Update :

Connecting state

Normal state

When there is only one text view, then it should be centered w.r.t image icon. otherwise, the other text view appears to the bottom.

Angus
  • 12,133
  • 29
  • 96
  • 151
  • 1
    Removed tag `android-studio`. That tag is used for problems/questions regarding the Android Studio product. Your question has nothing to do with Android Studio, it is a generic Android question. – David Wasser Jan 29 '21 at 23:02
  • I built a sample app to test the issue and the code in the alignDeviceNameToCenter() method seems to work correctly. So the problem must be somewhere else. Did you check whether the view binding for the root ConstraintLayout works correctly? Otherwise it would probably help if you post more of the surrounding code. – Steven Meliopoulos Jan 30 '21 at 19:18

3 Answers3

1

From your description it's not completely clear how you want the TextViews to be aligned when both are visible. Assuming you want them to line up with the top of the ImageView, you could try this:

<?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"
    android:id="@+id/disp"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <ImageView
        android:id="@+id/dev_ic"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/dev_name"
        app:layout_constraintBottom_toTopOf="@id/dev_connect_state"
        app:layout_constraintStart_toEndOf="@+id/dev_ic"
        app:layout_constraintTop_toTopOf="@id/dev_ic" />

    <TextView
        android:id="@+id/dev_connect_state"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="@id/dev_name"
        app:layout_constraintTop_toBottomOf="@id/dev_name" />

</androidx.constraintlayout.widget.ConstraintLayout>

The 2 key points are:

  1. The bottom of dev_name is constrained to the top of dev_connect_state.
  2. The height of dev_connect_state is set to 0dp. This will allow it to stretch and take up all the space below dev_name, pushing them to the top.

This way, when dev_connect_state is set to GONE, dev_name will automatically become vertically centered without the need to add any constraints programmatically.

Also note that the attribute layout_alignParentBottom you were using on dev_name belongs to RelativeLayout so it won't do anything here.

Steven Meliopoulos
  • 4,450
  • 4
  • 34
  • 36
  • Thanks. I have updated the question with the pictures and the styles, the image and the two text views alignment to the image icon. Also, both the text views have styles with layout height and layout width as wrap content. – Angus Jan 30 '21 at 00:44
  • Are the width and height on those styles needed? Since they don't have a background it doesn't seem to matter what the exact dimensions of the TextViews are. – Steven Meliopoulos Jan 30 '21 at 00:59
1

You don't need additional code or a third widget. Constrains should work like so

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/image"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        android:src="@mipmap/ic_launcher" />

    <com.google.android.material.textview.MaterialTextView
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:id="@+id/text1"
        app:layout_constraintStart_toEndOf="@id/image"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="@id/image"
        android:text="I am text one"
        app:layout_constraintBottom_toTopOf="@id/text2" />

    <com.google.android.material.textview.MaterialTextView
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:id="@+id/text2"
        app:layout_constraintStart_toEndOf="@id/image"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toBottomOf="@id/text1"
        android:text="I am text two"
        android:visibility="visible"
        app:layout_constraintBottom_toBottomOf="@id/image" />

</androidx.constraintlayout.widget.ConstraintLayout>

This looks as expected:

image of android emulator showing layout with an image and two texts on the side

Now if you hide (GONE) the lower TextView (text2) by setting it to android:visibility="gone", the result is:

image of android emulator showing layout with an image and one text on the side

How does it work?

As requested by the OP, here's what these constrains mean to each other.

The root/parent is a CL (constraint layout) that spans the entire space (match_parent).

Inside there are three widgets: an Image View and two TextViews.

The ImageView is independent of the textViews so it really positions itself where it wants. I've used constraintHorizontal_bias to move it to the beginning of the constrain (since it's horizontal, it moves to the "start/left"); otherwise the image would be equally pulled from both sides (left/right) and would sit in the middle. NOTE: A "horizontal chain" would probably be more flexible depending on the content of the text views to prevent a potential overlap, but this works most of the time fine.

Vertically speaking the image is now pinned to top/bottom of parent, assuming this is a "Cell", but you could either give it a specific size, or use 0dp and let the constrains size it, etc. There's really nothing special here.

The two textviews are similarly constrained, albeit one sits "Above" the other.

Horizontally speaking, the first one, the one that is always visible, pins it's "start" (left in LTR) to the end of the image. This mean it will sit on the "right" (end) of it. the "End/Right" is pinned to the parent, since we want the text view to have as much space as possible. Its width is set to 0dp so it will respect the constrains, you could also use wrap_content but be aware of some of the issues with "widgets that contain text that may need to re-measure themselves at some point" (you'd need a special CL attribute to those, see this).

Vertically speaking, the top is pinned to the top of the image and the bottom is constrained to the top of the text2 (which may not be there but this widget is wrap content so it has another way to position it).

As for the 2nd textView, the one that can disappear, the positioning follows a similar idea, except this time it's pinned vertically to the bottom of the TextView above. This has enough information already to position this second text view, start/end to the image/parent and top to the bottom of the text view above. When this textView is gone, the one above doesn't really care.

And that's pretty much it. If this was my app and this worked for me, i'd leave it as such; but if you start having issues with texts that overlap, or don't behave, then you ought to start thinking of introducing Chains/Barriers or similar mechanisms in CL to better guide the algorithm to put your widgets where you want them, how you want them.

Good luck :)

Martin Marconcini
  • 26,875
  • 19
  • 106
  • 144
  • Thanks. The solution you gave worked. Could you please elaborate on your change, so that I can understand the same. – Angus Jan 31 '21 at 06:29
0

Just add 3 textviews,

2 of them should be visible when you want both the name and content to be visible and the third should be invisible at that time

only the third should be visible when only the name is needed

align them as you need and you dont have to worry about ensuring that it is in the centre or not

ADITYA RANADE
  • 293
  • 3
  • 10