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

如何继承EditText实现同外观的带标题自定义输入组件?

我来给你梳理两种可行的方案,帮你解决这个问题:


方案一:继承EditText,通过绘制实现标题和底部横线

因为EditText本身是TextView的子类,无法直接添加子View,但我们可以通过重写onDraw()方法,在EditText的上方绘制标题文本、下方绘制底部横线,这样既继承了EditText的所有原生属性,又能保留你需要的外观样式。

步骤与代码示例

  1. 定义少量自定义属性(可选,用于XML配置标题)
    在res/values/attr.xml中添加:
<declare-styleable name="TitleEditText">
    <attr name="titleText" format="string"/>
    <attr name="titleTextColor" format="color"/>
    <attr name="titleTextSize" format="dimension"/>
    <attr name="bottomLineColor" format="color"/>
</declare-styleable>
  1. 创建自定义EditText子类
public class TitleEditText extends androidx.appcompat.widget.AppCompatEditText {
    private String mTitleText = "";
    private int mTitleTextColor = Color.parseColor("#666666");
    private float mTitleTextSize;
    private int mBottomLineColor = Color.parseColor("#eeeeee");
    private Paint mTitlePaint;
    private Paint mLinePaint;
    // 标题与EditText的间距
    private final int mTitleTopPadding = dpToPx(16);
    private final int mTitleBottomPadding = dpToPx(8);

    public TitleEditText(Context context) {
        super(context);
        init(null, 0);
    }

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

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

    private void init(AttributeSet attrs, int defStyleAttr) {
        // 初始化默认标题字号
        mTitleTextSize = spToPx(12);

        // 解析自定义属性
        if (attrs != null) {
            TypedArray ta = getContext().obtainStyledAttributes(attrs, R.styleable.TitleEditText, defStyleAttr, 0);
            mTitleText = ta.getString(R.styleable.TitleEditText_titleText);
            mTitleTextColor = ta.getColor(R.styleable.TitleEditText_titleTextColor, mTitleTextColor);
            mTitleTextSize = ta.getDimension(R.styleable.TitleEditText_titleTextSize, mTitleTextSize);
            mBottomLineColor = ta.getColor(R.styleable.TitleEditText_bottomLineColor, mBottomLineColor);
            ta.recycle();
        }

        // 初始化画笔
        mTitlePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mTitlePaint.setColor(mTitleTextColor);
        mTitlePaint.setTextSize(mTitleTextSize);

        mLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mLinePaint.setColor(mBottomLineColor);
        mLinePaint.setStrokeWidth(dpToPx(1));

        // 调整EditText内边距,给标题和横线留出空间
        int topPadding = getPaddingTop() + (int)mTitleTextSize + mTitleTopPadding + mTitleBottomPadding;
        int bottomPadding = getPaddingBottom() + dpToPx(1);
        setPadding(getPaddingLeft(), topPadding, getPaddingRight(), bottomPadding);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        // 绘制标题文本
        if (!TextUtils.isEmpty(mTitleText)) {
            float titleX = getPaddingLeft();
            float titleY = mTitleTopPadding + mTitlePaint.getFontSpacing();
            canvas.drawText(mTitleText, titleX, titleY, mTitlePaint);
        }

        // 绘制底部横线
        float lineStartX = getPaddingLeft();
        float lineEndX = getWidth() - getPaddingRight();
        float lineY = getHeight() - getPaddingBottom();
        canvas.drawLine(lineStartX, lineY, lineEndX, lineY, mLinePaint);

        // 绘制EditText原生内容
        super.onDraw(canvas);
    }

    // 辅助转换方法
    private float dpToPx(float dp) {
        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, getResources().getDisplayMetrics());
    }

    private float spToPx(float sp) {
        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, getResources().getDisplayMetrics());
    }

    // 代码中设置标题的方法
    public void setTitleText(String title) {
        mTitleText = title;
        invalidate();
    }
}
  1. XML中使用
<com.yourpackage.TitleEditText
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:hint="예) 힌트 텍스트"
    android:inputType="text"
    android:singleLine="true"
    android:textSize="18sp"
    android:textColor="@color/textLightDark"
    app:titleText="필드 이름"
    app:titleTextColor="@color/textLightDark"
    app:bottomLineColor="@color/colorLight"/>

这样你就能直接使用EditText的所有原生属性,同时拥有标题和底部横线的样式。


方案二:保持ConstraintLayout结构,透传EditText属性

如果你想保留原来的ConstraintLayout布局结构,又不想在attr.xml中定义大量EditText属性,可以通过属性透传的方式,让XML中设置的EditText属性自动传递给内部的contentEditText。

修改后的自定义View代码

public class DefaultEditTextContainerView extends ConstraintLayout {
    @BindView(R.id.titleText) public TextView titleText;
    @BindView(R.id.contentEditText) public EditText contentEditText;
    private String mTitleText;

    public DefaultEditTextContainerView(Context context) {
        super(context);
        init(null, 0);
    }

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

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

    private void init(AttributeSet attrs, int defStyle) {
        View view = inflate(getContext(), R.layout.view_default_edit_text_container_view, this);
        ButterKnife.bind(this, view);

        // 解析自定义标题属性
        if (attrs != null) {
            TypedArray ta = getContext().obtainStyledAttributes(attrs, R.styleable.DefaultEditTextContainerView, defStyle, 0);
            mTitleText = ta.getString(R.styleable.DefaultEditTextContainerView_titleText);
            ta.recycle();

            // 透传EditText核心属性
            TypedArray editTextTa = getContext().obtainStyledAttributes(attrs, new int[]{
                    android.R.attr.hint,
                    android.R.attr.inputType,
                    android.R.attr.textColor,
                    android.R.attr.textColorHint,
                    android.R.attr.textSize,
                    android.R.attr.singleLine
            });

            contentEditText.setHint(editTextTa.getString(0));
            contentEditText.setInputType(editTextTa.getInt(1, InputType.TYPE_CLASS_TEXT));
            contentEditText.setTextColor(editTextTa.getColor(2, Color.parseColor("#666666")));
            contentEditText.setHintTextColor(editTextTa.getColor(3, Color.parseColor("#eeeeee")));
            contentEditText.setTextSize(TypedValue.COMPLEX_UNIT_PX, editTextTa.getDimension(4, spToPx(18)));
            contentEditText.setSingleLine(editTextTa.getBoolean(5, true));

            editTextTa.recycle();
        }

        titleText.setText(mTitleText);
    }

    private float spToPx(float sp) {
        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, getResources().getDisplayMetrics());
    }

    // 代理EditText的常用方法,方便代码调用
    public String getInputText() {
        return contentEditText.getText().toString();
    }

    public void setInputText(String text) {
        contentEditText.setText(text);
    }
}

这种方式不需要额外自定义大量属性,XML中直接使用Android原生的EditText属性即可,会自动传递给内部的输入框。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 04:25:49