0

I'm experimenting with the ConstraintLayout Java API and I've hit a strange apparent incongruity between its behavior and that of inflated XML re: View dimensions. Here's my Java:

// called from Activity.onCreate(), member method of the Activity
public void setupView() {
    PopupWindow popupWindow = new PopupWindow();
    View genView = genButton(this);
    popupWindow.setBackgroundDrawable(getDrawable(android.R.drawable.alert_light_frame));
    DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
    popupWindow.setWidth((int)(displayMetrics.widthPixels * 0.75f));
    popupWindow.setHeight((int)(displayMetrics.heightPixels * 0.5f));
    popupWindow.setContentView(genView);
    popupWindow.showAtLocation(findViewById(android.R.id.content).getRootView(),
            Gravity.CENTER, 0, 0);
}

// lives in a factory class, so has Context passed in
public View genButton(Context context) {    
    ConstraintLayout layout = new ConstraintLayout(context);
    layout.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
    ConstraintSet generativeConstraints = new ConstraintSet();
    Button button = new Button(context);
    button.setId(1);
    button.setWidth(150);
    button.setHeight(50);
    button.setText("Testing Testing Lorem Ipsum etc.");
    layout.addView(button);
    generativeConstraints.connect(1, START, PARENT_ID, START);
    generativeConstraints.connect(1, END, PARENT_ID, END);
    generativeConstraints.connect(1, TOP, PARENT_ID, TOP);
    generativeConstraints.applyTo(layout);
    return layout;
}

which presents a situation where the generated Button can't be seen. If I change the ConstraintSet such that the bottom of the Button is constrained to the bottom of the parent, the Button is visible and fills the entire space between the top and the bottom of the parent; this seems to indicate that the ConstraintLayout decided the button had no intrinsic dimensions and so was free to stretch it to fill the constraints. If I comment the generativeConstraints.applyTo(layout) line, I get my Button with expected dimens but at screen coords 0,0 since no constraints were ever applied to it -- this suggests the Button dimens are being wiped out somewhere in the ConstraintSet, but I can't see where.

To my understanding, the following XML (called exp_constraints_template.xml in layouts directory) should be equivalent to the Java above:

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

    <Button
        android:id="@+id/button1"
        android:layout_width="150dp"
        android:layout_height="50dp"
        android:text="Testing Testing Lorem Ipsum etc."
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

Replacing the PopupWindow.setContentView() call in setupView() above with popupWindow.setContentView(getLayoutInflater().inflate(R.layout.exp_constraints_template, null)) works as expected. Only obvious difference is in the id of the Button, being generated by AAPT in the XML and hardcoded to 1 in the Java. If that were a problem, however, I'd expect to see crashes and/or no Button regardless of the config.

What could be causing the difference in behavior I'm seeing between XML and Java?

CCJ
  • 1,619
  • 26
  • 41

1 Answers1

0

Turns out you need to addView() all Views to your ConstraintLayout BEFORE calling ConstraintSet.clone(layout), and you have to call ConstraintSet.clone(layout) before calling ConstraintSet.applyTo(layout) per https://stackoverflow.com/a/53237056/870135

So with the ConstraintLayout Java API the order of operations needs to be:

  1. add views to layout/viewgroup and perform any mods that might change their dimens or other constraint related data

  2. init/update ConstraintSet with clone(layout)

    • Note: calling clone() multiple times seems to break things, so if you have many views to add it may be best to have two loops -- first one to generate and add the views, then call clone() to update the ConstraintSet, and then perform as second loop to apply constraint rules to each generated view.
  3. call any ConstraintSet configuration methods such as connect()

  4. apply ConstraintSet with applyTo(layout)

in order to have view dimensions known by the ConstraintSet and preserved on application of constraints.

Tamir Abutbul
  • 7,301
  • 7
  • 25
  • 53
CCJ
  • 1,619
  • 26
  • 41