You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于ConstraintLayout的CustomSwitchCompat无法作为约束锚点求助

问题描述

基于ConstraintLayout实现了两个自定义View:CustomEditTextCompat与CustomSwitchCompat,二者实现逻辑基本一致,仅交互组件分别为EditTextCompat和SwitchCompat。当使用CustomEditTextCompat作为layout_constraintTop_toBottomOf的约束锚点时,布局表现正常;但将CustomSwitchCompat作为锚点时,被约束的View会直接跳至顶部。

相关代码如下:

custom_switch_compat.xml

<?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="wrap_content"
tools:background="@color/black">

<androidx.constraintlayout.widget.Guideline
    android:id="@+id/centerLeft_line"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    app:layout_constraintGuide_percent="0.49" />

<androidx.constraintlayout.widget.Guideline
    android:id="@+id/centerRight_line"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    app:layout_constraintGuide_percent="0.51" />

<androidx.appcompat.widget.AppCompatTextView
    android:id="@+id/switchTextLabel"
    style="@style/ColorAndFontTheme"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginTop="13dp"
    android:layout_marginEnd="8dp"
    app:layout_constraintEnd_toStartOf="@+id/centerLeft_line"
    app:layout_constraintTop_toTopOf="parent"
    tools:text="Label" />

<androidx.appcompat.widget.SwitchCompat
    android:id="@+id/switchValue"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:checked="false"
    android:theme="@style/menuSwitchTheme"
    app:layout_constraintBottom_toBottomOf="@+id/switchTextLabel"
    app:layout_constraintStart_toEndOf="@+id/centerRight_line"
    app:layout_constraintTop_toTopOf="@+id/switchTextLabel" />
 </androidx.constraintlayout.widget.ConstraintLayout>

CustomSwitchCompat.java

public class CustomSwitchCompat extends ConstraintLayout {
private TextView label;
private SwitchCompat switchCompat;

public CustomSwitchCompat(Context context, AttributeSet attrs) {
    super(context);
    init(context, attrs);
}

private void init(Context context, AttributeSet attrs) {
    LayoutInflater.from(context).inflate(R.layout.custom_switch_compat, this, true);
    label = findViewById(R.id.switchTextLabel);
    switchCompat = findViewById(R.id.switchValue);
    TypedArray attributes = context.obtainStyledAttributes(attrs, R.styleable.CustomSwitchCompat);


    String labelText = attributes.getString(R.styleable.CustomSwitchCompat_switchLabelText);
    label.setText(labelText);
    // Additional attributes such as onText and offText can be set here
    attributes.recycle();
}
}

custom_edit_text_compat.xml

<?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="wrap_content"
    tools:background="@color/black">

    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/centerLeft_line"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        app:layout_constraintGuide_percent="0.49" />

    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/centerRight_line"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        app:layout_constraintGuide_percent="0.51" />

    <androidx.appcompat.widget.AppCompatTextView
        android:id="@+id/textLabel"
        style="@style/ColorAndFontTheme"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="18dp"
        android:layout_marginEnd="8dp"
        app:layout_constraintEnd_toStartOf="@+id/centerLeft_line"
        app:layout_constraintTop_toTopOf="parent"
        tools:text="Label" />

    <androidx.appcompat.widget.AppCompatEditText
        android:id="@+id/editInput"
        style="@style/ColorAndFontTheme"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginTop="3dp"
        android:textColorHint="@color/gray"
        app:layout_constraintBottom_toBottomOf="@+id/textLabel"
        app:layout_constraintStart_toStartOf="@+id/centerRight_line"
        app:layout_constraintTop_toTopOf="@id/textLabel"
        tools:text="Editbox" />

</androidx.constraintlayout.widget.ConstraintLayout>

CustomEditTextCompat.java

public class CustomEditTextCompat extends ConstraintLayout {
    protected AppCompatEditText editInput;
    private AppCompatTextView textLabel;

    public CustomEditTextCompat(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs);
    }

    private void init(Context context, AttributeSet attrs) {
        LayoutInflater.from(context).inflate(R.layout.custom_edit_text_compat, this, true);

        textLabel = findViewById(R.id.textLabel);
        editInput = findViewById(R.id.editInput);

        TypedArray attributes = context.obtainStyledAttributes(attrs, R.styleable.CustomEditTextCompat);
        String labelText = attributes.getString(R.styleable.CustomEditTextCompat_labelText);
        String hintText = attributes.getString(R.styleable.CustomEditTextCompat_hintText);

        textLabel.setText(labelText);
        editInput.setHint(hintText);

        attributes.recycle();
    }

    public String getEditTextValue() {
        return editInput.getText().toString();
    }

    public void setLabelText(String text) {
        textLabel.setText(text);
    }

    public void setHintText(String hint) {
        editInput.setHint(hint);
    }
}

使用示例

<com..menuFragments.components.CustomEditTextCompat
        android:id="@+id/custom_attacks_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:hintText="Cool fire wave"
        app:labelText="Attack Name"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <com..menuFragments.components.CustomSwitchCompat
        android:id="@+id/custom_attacks_switch"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/custom_attacks_name"
        app:switchLabelText="Finish when done" />

    <com..menuFragments.components.CustomEditTextCompat
        android:id="@+id/custom_attacks_box_width"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:hintText="Default"
        app:labelText="Box width"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/custom_attacks_switch" />
解决方案

问题出在CustomSwitchCompat的构造函数调用上:

  • 对比CustomEditTextCompat的构造函数,它调用了super(context, attrs),而CustomSwitchCompat只调用了super(context),没有传入attrs参数。这会导致自定义View无法正确解析布局中设置的约束属性,进而使得它作为锚点时,后续View的约束计算出错。

修改CustomSwitchCompat的构造函数:

public CustomSwitchCompat(Context context, AttributeSet attrs) {
    super(context, attrs); // 传入attrs参数
    init(context, attrs);
}

另外,建议补充实现带三个参数的构造函数,确保在所有场景下布局属性都能正确处理:

public CustomSwitchCompat(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    init(context, attrs);
}

修改后,CustomSwitchCompat就能正确参与ConstraintLayout的约束计算,被约束的View也会正常显示在它的下方。


内容的提问来源于stack exchange,提问作者Andreas Andersson

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 12:24:59