使用 ConstraintSet 时测量的布局大小为零
measured layout size is zero when using ConstraintSet
这让我发疯!当我尝试使用约束布局和约束集以编程方式构建布局时,测量布局始终为 0。
我在所有 child 和 parents 上都尝试了 requestLayout
、forceLayout
、invalidate
。但宽度和高度为 0 使整个视图不可见。
这是我的布局生成器。我的问题是当我应用约束时视图变得不可见。
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" />
我不得不克隆 ConstraintSet
。不仅如此,必须在添加视图 之后进行克隆。添加视图和克隆后 ConstraintSet
,它知道视图的大小并可以相应地设置约束。
private FormViewBuilder(Context context) {
this.context = context;
formView = new FormView(context); // initialize
// ...
}
// ...
public FormViewBuilder addField(int id, @NonNull String key, @NonNull String hint, String requiredMessage) {
TextInputLayout field = newTextField(context, id, key, hint, requiredMessage);
getLastRow().add(field);
// add views before build <<================ important
ConstraintLayout.LayoutParams params = new ConstraintLayout.LayoutParams(
ConstraintLayout.LayoutParams.CHAIN_SPREAD,
ConstraintLayout.LayoutParams.WRAP_CONTENT
);
formView.addView(field, params);
return this;
}
public FormView build() {
ConstraintSet constraints = new ConstraintSet();
constraints.clone(formView); // clone after views are added <<================ important
// only set constraints. do not add or remove views
constraints.applyTo(formView);
return formView;
}
以防万一有人无意中发现了这一点,另一种可能是您使用了错误的 LayoutParams 类型。确保使用您布局的确切类型的 LayoutParams child class,在这种情况下:ConstraintLayout.LayoutParams.WRAP_CONTENT
.
这让我发疯!当我尝试使用约束布局和约束集以编程方式构建布局时,测量布局始终为 0。
我在所有 child 和 parents 上都尝试了 requestLayout
、forceLayout
、invalidate
。但宽度和高度为 0 使整个视图不可见。
这是我的布局生成器。我的问题是当我应用约束时视图变得不可见。
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" />
我不得不克隆 ConstraintSet
。不仅如此,必须在添加视图 之后进行克隆。添加视图和克隆后 ConstraintSet
,它知道视图的大小并可以相应地设置约束。
private FormViewBuilder(Context context) {
this.context = context;
formView = new FormView(context); // initialize
// ...
}
// ...
public FormViewBuilder addField(int id, @NonNull String key, @NonNull String hint, String requiredMessage) {
TextInputLayout field = newTextField(context, id, key, hint, requiredMessage);
getLastRow().add(field);
// add views before build <<================ important
ConstraintLayout.LayoutParams params = new ConstraintLayout.LayoutParams(
ConstraintLayout.LayoutParams.CHAIN_SPREAD,
ConstraintLayout.LayoutParams.WRAP_CONTENT
);
formView.addView(field, params);
return this;
}
public FormView build() {
ConstraintSet constraints = new ConstraintSet();
constraints.clone(formView); // clone after views are added <<================ important
// only set constraints. do not add or remove views
constraints.applyTo(formView);
return formView;
}
以防万一有人无意中发现了这一点,另一种可能是您使用了错误的 LayoutParams 类型。确保使用您布局的确切类型的 LayoutParams child class,在这种情况下:ConstraintLayout.LayoutParams.WRAP_CONTENT
.