ConstraintLayout中addView()添加视图未得到预期结果问题求助
问题描述
代码中的else分支可正常运行,但if分支逻辑存在问题:向ConstraintLayout重新添加视图时,无法得到预期布局效果。
预期实现效果:
实际运行效果不符合需求:
问题代码
public class ChecklistChildViewHolder extends RecyclerView.ViewHolder { CheckBox checkBox; ImageView delete, edit, move; ConstraintLayout constraintLayout; ChecklistChildViewHolder(@NonNull View itemView) { super(itemView); checkBox = itemView.findViewById(R.id.checkbox_child); delete = itemView.findViewById(R.id.delete_child); edit = itemView.findViewById(R.id.edit_child); move = itemView.findViewById(R.id.move_child); constraintLayout = itemView.findViewById(R.id.constraint_child); } public void updateVisibility(boolean isEnableDelete){ ConstraintLayout.LayoutParams lpCheckBox = (ConstraintLayout.LayoutParams) checkBox.getLayoutParams(); ConstraintLayout.LayoutParams lpDelete = (ConstraintLayout.LayoutParams) delete.getLayoutParams(); ConstraintLayout.LayoutParams lpEdit = (ConstraintLayout.LayoutParams) edit.getLayoutParams(); if (isEnableDelete) { constraintLayout.removeView(move); constraintLayout.addView(delete); constraintLayout.addView(edit); lpDelete.endToEnd = R.id.constraint_child; lpDelete.startToEnd = R.id.edit_child; lpEdit.startToEnd = R.id.checkbox_child; lpEdit.endToStart = R.id.delete_child; delete.setLayoutParams(lpDelete); edit.setLayoutParams(lpEdit); } else { constraintLayout.removeView(delete); constraintLayout.removeView(edit); lpCheckBox.endToStart = R.id.move_child; lpCheckBox.startToStart = R.id.constraint_child; checkBox.setLayoutParams(lpCheckBox); } } }
问题根源&修复方案
代码存在3个直接导致布局异常的问题,对应修复即可:
- 状态切换不闭环,约束残留:RecyclerView的ViewHolder会被复用,两个分支的状态切换没有做全量重置。切到if分支时,既没有清除上一次else分支给checkBox设置的
endToStart = R.id.move_child约束,也没有处理move视图移除后的约束失效问题;切回else分支时,也没有把之前移除的move视图重新加回布局,状态不一致直接导致布局错位。 - 约束修改后未触发重绘:修改LayoutParams的约束属性后,没有主动调用
requestLayout()通知布局刷新,新设置的约束不会立即生效。 - 视图添加顺序和依赖顺序冲突:if分支里先add delete再add edit,但delete的约束依赖edit的id,添加delete时edit还没挂载到布局上,会导致约束匹配失败。
修复后的参考代码:
public void updateVisibility(boolean isEnableDelete){ ConstraintLayout.LayoutParams lpCheckBox = (ConstraintLayout.LayoutParams) checkBox.getLayoutParams(); ConstraintLayout.LayoutParams lpDelete = (ConstraintLayout.LayoutParams) delete.getLayoutParams(); ConstraintLayout.LayoutParams lpEdit = (ConstraintLayout.LayoutParams) edit.getLayoutParams(); if (isEnableDelete) { // 移除不需要的move视图 constraintLayout.removeView(move); // 按依赖顺序添加视图:先加被依赖的edit,再加依赖edit的delete constraintLayout.addView(edit); constraintLayout.addView(delete); // 重置checkBox约束,清除残留的move依赖 lpCheckBox.startToStart = R.id.constraint_child; lpCheckBox.endToStart = R.id.edit_child; // 设置edit、delete的约束 lpEdit.startToEnd = R.id.checkbox_child; lpEdit.endToStart = R.id.delete_child; lpDelete.endToEnd = R.id.constraint_child; lpDelete.startToEnd = R.id.edit_child; // 应用所有布局参数 checkBox.setLayoutParams(lpCheckBox); edit.setLayoutParams(lpEdit); delete.setLayoutParams(lpDelete); } else { // 移除不需要的edit、delete视图 constraintLayout.removeView(delete); constraintLayout.removeView(edit); // 加回move视图 constraintLayout.addView(move); // 重置checkBox约束 lpCheckBox.startToStart = R.id.constraint_child; lpCheckBox.endToStart = R.id.move_child; checkBox.setLayoutParams(lpCheckBox); } // 触发父布局重新计算布局 constraintLayout.requestLayout(); }
优化建议:不需要反复add/remove视图,直接调用setVisibility()控制delete/edit/move的显示隐藏,再对应切换约束即可,性能更好,也不会出现视图添加顺序导致的约束匹配问题。
内容的提问来源于stack exchange,提问作者nano tech
相关产品推荐
相关产品推荐

