3

So I have a layout file for a RecycleView which has expandable/collapsible viewholders.

Clicking on the header would expand/collapse the extra data. Everything looks fine in the editor. However, it would have a ghost space similar to layout_marginBottom.

First loaded appearance

Figure 1 (First loaded appearance)

Expanded

Figure 2 (Expanded)

Collapsed correctly

Figure 3 (Collapsed correctly)

So the editor would only display the ViewHolder as Figure 2 and 3. However when running on a device, it would first display Figure 1, then after clicking on it to expand it, it would display Figure 2. Collapsing it once again would display Figure 3 instead of Figure 1. It would continue to display the correct figures (2 and 3).

The extra contents in Figure 2 have common parents with the header which is a ConstraintLayout.

This would happen regardless of the GONE specifiers: Programmatically or XML Using activity.runOnUIThread would not help. Using a new Handler().onPostDelayed would only be worse as would display Figure 1 but with more unwanted space, as much as Figure 2, only without the extra info.

Here's the XML file:

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
app:cardCornerRadius="10dp">

<android.support.constraint.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content" >

    <LinearLayout
        android:id="@+id/header"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center_vertical"
        android:orientation="horizontal"
        android:padding="10dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toStartOf="@+id/validity"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.0">

        <ImageButton
            android:id="@+id/expand"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@android:color/transparent"
            android:layout_marginRight="8dp"
            android:layout_marginEnd="8dp"
            app:srcCompat="@drawable/ic_right" />

        <TextView
            android:id="@+id/title"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:ellipsize="end"
            android:maxLines="1"
            android:textColor="@color/textDark" />
    </LinearLayout>

    <View
        android:id="@+id/divider"
        android:layout_width="0dp"
        android:layout_height="2dp"
        android:layout_marginEnd="16dp"
        android:layout_marginLeft="16dp"
        android:layout_marginRight="16dp"
        android:layout_marginStart="16dp"
        android:background="@color/colorSecondaryDark"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/header" />

    <TextView
        android:id="@+id/date"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginEnd="8dp"
        android:layout_marginLeft="10dp"
        android:layout_marginRight="8dp"
        android:layout_marginStart="10dp"
        android:layout_marginTop="8dp"
        app:layout_constraintEnd_toStartOf="@+id/size"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/divider" />

    <TextView
        android:id="@+id/size"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginEnd="16dp"
        android:layout_marginRight="16dp"
        android:layout_marginTop="8dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/divider" />

    <TextView
        android:id="@+id/amount"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginEnd="8dp"
        android:layout_marginLeft="10dp"
        android:layout_marginRight="8dp"
        android:layout_marginStart="10dp"
        android:layout_marginTop="8dp"
        app:layout_constraintEnd_toStartOf="@+id/validity"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/date" />

    <LinearLayout
        android:id="@+id/validity"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginEnd="16dp"
        android:layout_marginRight="16dp"
        android:layout_marginTop="8dp"
        android:gravity="center_vertical"
        android:orientation="horizontal"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/size">

        <ImageView
            android:id="@+id/help"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginEnd="8dp"
            android:layout_marginRight="8dp"
            android:layout_weight="1"
            android:contentDescription="@string/backup_layout_help_desc"
            app:srcCompat="@drawable/ic_help" />

        <TextView
            android:id="@+id/invalid"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="@string/backup_layout_invalid"
            android:textAllCaps="true"
            android:textColor="@color/red" />
    </LinearLayout>

    <TextView
        android:id="@+id/passwords"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginEnd="10dp"
        android:layout_marginStart="10dp"
        android:layout_marginTop="8dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/amount" />

    <Button
        android:id="@+id/delete"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginEnd="8dp"
        android:layout_marginLeft="10dp"
        android:layout_marginRight="8dp"
        android:layout_marginStart="10dp"
        android:layout_marginTop="10dp"
        android:background="@android:color/transparent"
        android:text="@string/backup_layout_delete"
        android:textColor="@color/red"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toStartOf="@+id/importshare"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/passwords"
        app:layout_constraintVertical_bias="1.0" />

    <LinearLayout
        android:id="@+id/importshare"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginEnd="10dp"
        android:layout_marginRight="10dp"
        android:layout_marginTop="10dp"
        android:orientation="horizontal"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/validity"
        app:layout_constraintVertical_bias="1.0">

        <Button
            android:id="@+id/commit"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:background="@android:color/transparent"
            android:text="Import"
            android:textColor="@color/yellow" />

        <ImageButton
            android:id="@+id/share"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:background="@android:color/transparent"
            android:contentDescription="Share"
            android:padding="10dp"
            app:srcCompat="@drawable/ic_share" />
    </LinearLayout>

</android.support.constraint.ConstraintLayout>

And the ViewHolder (cut to show relevant parts):

public ViewHolder(View itemView, Context context) {
        super(itemView);

        Amount = itemView.findViewById(R.id.amount);
        Date = itemView.findViewById(R.id.date);
        Delete = itemView.findViewById(R.id.delete);
        Divider = itemView.findViewById(R.id.divider);
        ExpandCollapse = itemView.findViewById(R.id.expand);
        Import = itemView.findViewById(R.id.commit);
        ImportShare = itemView.findViewById(R.id.importshare);
        Header = itemView.findViewById(R.id.header);
        Help = itemView.findViewById(R.id.help);
        Passwords = itemView.findViewById(R.id.passwords);
        Share = itemView.findViewById(R.id.share);
        Size = itemView.findViewById(R.id.size);
        Title = itemView.findViewById(R.id.title);
        Validity = itemView.findViewById(R.id.validity);

        Title.setTypeface(Typeface.createFromAsset(context.getAssets(), "cera.otf"));

        Header.setOnClickListener(this);

        onClick(Header);

    }

    @Override
    public void onClick(View v) {
        if (expanded) collapse();
        else expand();
    }

    public void expand() {
        ExpandCollapse.animate().rotation(90).start();

        Date.setVisibility(View.VISIBLE);
        Size.setVisibility(View.VISIBLE);
        Amount.setVisibility(View.VISIBLE);
        Passwords.setVisibility(View.VISIBLE);
        Divider.setVisibility(View.VISIBLE);
        Validity.setVisibility(valid ? View.INVISIBLE : View.VISIBLE);

        Delete.setVisibility(View.VISIBLE);
        ImportShare.setVisibility(View.VISIBLE);

        expanded = true;
    }

    public void collapse() {
        ExpandCollapse.animate().rotation(0).start();

        Date.setVisibility(View.GONE);
        Size.setVisibility(View.GONE);
        Amount.setVisibility(View.GONE);
        Passwords.setVisibility(View.GONE);
        Divider.setVisibility(View.GONE);
        Validity.setVisibility(View.GONE);

        Delete.setVisibility(View.GONE);
        ImportShare.setVisibility(View.GONE);

        expanded = false;
    }
Santhosh Joseph
  • 744
  • 7
  • 18
iBlueDust
  • 51
  • 7
  • why do you have nested LinearLayouts if the purpose of ConstraintLayout is to eliminate nesting? – Angelina Sep 23 '18 at 11:53
  • The header needed LinearLayouts but the other ones, now that you say it were unnecessary. I just didn't know how to center things horizontally in ConstraintLayouts. – iBlueDust Sep 23 '18 at 15:25
  • using constraints and chains. – Angelina Sep 24 '18 at 08:05
  • I can't tell from reading the code, but I bet if you temporarily set a different background color (not white) for ALL views, you would see which one is taking that space. – Nerdy Bunz Sep 24 '18 at 11:17
  • Turns out two views were displaying as invisible although it was specified to be `GONE`. Not sure why but after modifying the adapter to never set them to `INVISIBLE` and to `GONE` instead, everything became fine. Btw the two views were a `TextView` and a `ImageView`. – iBlueDust Sep 24 '18 at 13:54

1 Answers1

0

Kotlin

I had a very similar issue when attempting to hide elements when the keyboard is open

What would happen is when you open the keyboard you would get two resizes, One adjustment for the opening and the keyboard and another for setting the views to gone. The problem is the second adjustment would only occur when another element requests a layout so your screen would jump around very noticeably twice

  1. When keyboard open
  2. When you tap or move another UI element

I have found a solution for this, but only when Window soft input mode = adjust resize

See below

class MainActivity : AppCompatActivity() {
    private var activityHeight = 0
    private var keyboardOpen = false
    private var mainScreen : ConstraintLayout? = null
    private var infoText : TextView? = null
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.main_screen)

        mainScreen = findViewById(R.id.mainConstraintLayout)
        infoText = findViewById(R.id.mainInfoText)

        //...

        /* Capture initial screen size */
        this@MainActivity.window.decorView.doOnNextLayout {
            val displayFrame : Rect = Rect()
            this@MainActivity.window.decorView.getWindowVisibleDisplayFrame(displayFrame)
            activityHeight = displayFrame.height()
        }

        /* Check for keyboard open/close */
        this@MainActivity.window.decorView.addOnLayoutChangeListener { v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom ->
            val drawFrame : Rect = Rect()
            this@MainActivity.window.decorView.getWindowVisibleDisplayFrame(drawFrame)
            val currentSize = drawFrame.height()
            if(keyboardOpen){
                keyboardOpen = currentSize < activityHeight
                if (!keyboardOpen){
                    infoText?.visibility = View.VISIBLE
                }
            }else {
                keyboardOpen = currentSize < activityHeight
                if(keyboardOpen) {
                    infoText?.visibility = View.GONE
                    /* Request a new layout on your base layout so screen adjusts correctly */
                    mainScreen?.requestLayout()
                }
            }
        }
    }
}
Rowan Berry
  • 171
  • 7