自定义View继承ConstraintLayout时构造函数用ConstraintSet设置约束不生效如何解决
问题原因
你在构造函数中直接调用layout()设置约束不生效,是因为构造函数执行阶段,自定义ConstraintLayout本身还未完成视图树的测量、布局流程,此时调用ConstraintSet.clone()拿到的是未初始化完成的布局状态,约束应用后不会触发正确的布局计算。而postDelayed延迟2秒后执行时,布局已经完成了初始化流程,所以能正常生效,但固定延迟的方式兼容性极差,不同设备渲染速度不同很容易出问题。
正确的实现方案
有几个更稳妥的时机可以选:
方法1:使用无延迟的post()方法
不需要指定延迟时间,将设置约束的任务post到主线程消息队列,任务会在当前视图树的布局遍历完成后自动执行,比固定延迟可靠得多:
public ConstraintAfterInit(@NonNull Context context) { super(context); view = new LinearLayout(context); view.setId(View.generateViewId()); view.setBackgroundColor(Color.GREEN); ConstraintLayout.LayoutParams viewLp = new ConstraintLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT); viewLp.matchConstraintMinHeight = 200; view.setLayoutParams(viewLp); this.addView(view); // 替换掉原来的postDelayed即可 this.post(() -> layout()); }
方法2:使用OnGlobalLayoutListener监听布局完成事件
这个回调会在视图树第一次完成测量、布局后触发,适合需要精准控制第一次布局时机的场景,记得用完移除监听避免重复触发:
public ConstraintAfterInit(@NonNull Context context) { super(context); view = new LinearLayout(context); view.setId(View.generateViewId()); view.setBackgroundColor(Color.GREEN); ConstraintLayout.LayoutParams viewLp = new ConstraintLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT); viewLp.matchConstraintMinHeight = 200; view.setLayoutParams(viewLp); this.addView(view); getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { layout(); // 移除监听,避免后续布局变化重复触发 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { getViewTreeObserver().removeOnGlobalLayoutListener(this); } else { getViewTreeObserver().removeGlobalOnLayoutListener(this); } } }); }
额外优化点
如果你的自定义View同时支持XML布局引入,记得补全另外两个构造函数:
ConstraintAfterInit(@NonNull Context context, @Nullable AttributeSet attrs)ConstraintAfterInit(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr)
避免XML引入时崩溃。
内容的提问来源于stack exchange,提问作者yangzhou
相关产品推荐
相关产品推荐

