This is driving me crazy! measured layout is always 0 when i try to build layout programmatically using constraint layout and constraint set.
I tried requestLayout, forceLayout, invalidate on all child and parents. but width and height is 0 rendering whole view invisible.
here is my layout builder. my problem is that views are rendering invisible when i apply constraints.
public final class FormBuilder {
private Context context;
private ArrayList<ArrayList<TextInputLayout>> fields;
private FormBuilder(Context context) {
this.context = context;
fields = new ArrayList<>();
fields.add(new ArrayList<TextInputLayout>());
}
private ArrayList<TextInputLayout> getLastRow() {
return fields.get(fields.size() - 1);
}
public static FormBuilder newForm(Context context) {
return new FormBuilder(context);
}
public FormBuilder addField(@NonNull String hint) {
TextInputLayout field = newTextField(context, hint);
getLastRow().add(field);
return this;
}
public FormBuilder nextRow() {
if (getLastRow().size() > 0) fields.add(new ArrayList<TextInputLayout>());
return this;
}
public FormView build() {
FormView formView = new FormView(context);
ConstraintSet constraints = new ConstraintSet();
int topId = ConstraintSet.PARENT_ID;
for (ArrayList<TextInputLayout> row : fields) {
if (row.size() == 0) continue;
int[] rowIds = new int[row.size()];
for (int i = 0; i < row.size(); i++) { // constraint top
TextInputLayout field = row.get(i);
rowIds[i] = field.getId();
ConstraintLayout.LayoutParams params = new ConstraintLayout.LayoutParams(
ConstraintLayout.LayoutParams.WRAP_CONTENT,
ConstraintLayout.LayoutParams.WRAP_CONTENT
);
formView.addView(field, params);
if (topId == ConstraintSet.PARENT_ID) {
constraints.connect(rowIds[i], ConstraintSet.TOP, ConstraintSet.PARENT_ID, ConstraintSet.TOP);
} else {
constraints.connect(rowIds[i], ConstraintSet.TOP, topId, ConstraintSet.BOTTOM);
}
}
if (row.size() >= 2) { // constraint start and end
constraints.createHorizontalChainRtl(
ConstraintSet.PARENT_ID, ConstraintSet.START,
ConstraintSet.PARENT_ID, ConstraintSet.END,
rowIds, null, ConstraintSet.CHAIN_SPREAD);
} else { // row.size = 1
constraints.connect(rowIds[0], ConstraintSet.START, ConstraintSet.PARENT_ID, ConstraintSet.START);
constraints.connect(rowIds[0], ConstraintSet.END, ConstraintSet.PARENT_ID, ConstraintSet.END);
}
topId = rowIds[0];
}
constraints.applyTo(formView); // this turns layout size to zero
return formView;
}
private static TextInputLayout newTextField(Context context, String hint) {
TextInputLayout.LayoutParams layoutParams = new TextInputLayout.LayoutParams(
TextInputLayout.LayoutParams.MATCH_PARENT,
TextInputLayout.LayoutParams.WRAP_CONTENT
);
TextInputLayout inputLayout = new TextInputLayout(context);
TextInputEditText editText = new TextInputEditText(context);
inputLayout.addView(editText, layoutParams);
inputLayout.setHint(hint);
inputLayout.setId(ViewCompat.generateViewId());
return inputLayout;
}
}
MainActivity.java
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
FormView form = FormBuilder.newForm(this)
.addField("First name").addField("Last name")
.nextRow()
.addField("Phone numnber")
.build();
FrameLayout layout = findViewById(R.id.main_content);
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT
);
layout.addView(form, params);
}
}
FormView.java
public class FormView extends ConstraintLayout {
public FormView(Context context) {
super(context);
}
}
main_activity.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/main_content"
android:layout_width="match_parent"
android:layout_height="match_parent" />
I had to clone
ConstraintSet. not only that, cloning must be done after views are added. after adding views and cloningConstraintSet, it knows size of the views and can set constraints accordingly.