自定义TextInputLayout:如何通过attrs.xml配置endIconMode?图标不显示排查
问题:自定义TextInputLayout通过attrs配置endIcon后图标不显示
需求是在自定义TextInputLayout中通过attrs.xml配置endIconMode而非编程方式,已定义相关属性并在布局中设置,但图标完全不显示,其他属性正常生效。相关代码如下:
attrs.xml
<declare-styleable name="CustomTextInputLayout"> <attr name="custom_label" format="string|reference"/> <attr name="custom_hint" format="string|reference"/> <attr name="custom_helper_text" format="string|reference"/> <attr name="custom_password_field" format="boolean"/> <attr name="custom_show_helper_text" format="boolean"/> <attr name="custom_end_icon_drawable" format="reference"/> <attr name="custom_end_icon_mode" format="enum"> <enum name="custom" value="-1"/> <enum name="none" value="0"/> <enum name="password_toggle" value="1"/> <enum name="clear_text" value="2"/> <enum name="dropdown_menu" value="3"/> </attr> <attr name="custom_end_icon_description" format="reference"/> <attr name="custom_show_label" format="boolean"/> <attr name="custom_input_mode" format="reference"/> </declare-styleable>
布局XML
<com.example.ui_components.CustomTextInputLayout android:id="@+id/bk_birthday" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginTop="20dp" app:custom_label="@string/profile_field_birthday" app:custom_hint="@string/profile_field_birthday" app:custom_helper_text="@string/sign_up_birthday_field_helper" app:custom_show_label="true" app:custom_show_helper_text="true" app:custom_end_icon_drawable="@drawable/ic_calendar" app:custom_end_icon_description="@string/set_birthday" app:custom_end_icon_mode="custom" app:layout_constraintTop_toBottomOf="@id/description" app:layout_constraintStart_toStartOf="@id/g_left" app:layout_constraintEnd_toEndOf="@id/g_right" />
自定义组件代码
class CustomTextInputLayout @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0, defStyleRes: Int = 0, ) : ConstraintLayout(context, attrs, defStyleAttr, defStyleRes) { private val binding = CustomTextInputLayoutBinding.inflate(LayoutInflater.from(context), this) init { context.obtainStyledAttributes(attrs, R.styleable.CustomTextInputLayout, defStyleAttr, defStyleRes) .run { setHint(getString(R.styleable.CustomTextInputLayout_textInput_hint) ?: "") if (getBoolean(R.styleable.CustomTextInputLayout_textInput_show_label, false)) { setLabel(getString(R.styleable.CustomTextInputLayout_textInput_label) ?: "") } if (getBoolean(R.styleable.CustomTextInputLayout_bktil_show_helper_text, false)) setHelperText(getString(R.styleable.CustomTextInputLayout_textInput_helper_text) ?: "") recycle() } } fun setLabel(label: String) { binding.customTextInputLayout.text = label header.labelFor } fun setHint(hint: String) { binding.customTextEditText.hint = hint } fun setHelperText(helper: String) { binding.customTextInputLayout.helperText = helper } }
组件布局XML
<merge xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" tools:parentTag="androidx.constraintlayout.widget.ConstraintLayout" > <TextView android:id="@+id/header_label" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="20dp" android:textAppearance="?textAppearanceHeadline6" android:textStyle="bold" android:labelFor="@id/custom_text_input_wrapper" app:layout_constraintTop_toTopOf="parent" app:layout_constraintStart_toStartOf="parent" tools:text="Email Address" android:contentDescription="@string/textHeaderLabel" /> <com.google.android.material.textfield.TextInputLayout android:id="@+id/custom_text_input_wrapper" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="20dp" app:boxCornerRadiusTopStart="8dp" app:boxCornerRadiusBottomEnd="8dp" app:boxCornerRadiusBottomStart="8dp" app:boxCornerRadiusTopEnd="8dp" app:layout_constraintTop_toBottomOf="@id/header_label" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:hintEnabled="false" > <com.google.android.material.textfield.TextInputEditText android:id="@+id/custom_text_input_edit_text" android:layout_width="match_parent" android:layout_height="48dp" tools:hint="Email Address" android:paddingTop="0dp" android:paddingBottom="0dp" android:imeOptions="actionNext" android:inputType="textEmailAddress" android:singleLine="true" /> </com.google.android.material.textfield.TextInputLayout> </merge>
问题原因及解决方案
遗漏的核心点
- 未处理endIcon相关属性:自定义组件的
init块中完全没有读取custom_end_icon_mode、custom_end_icon_drawable、custom_end_icon_description这些属性,也没有将它们传递给内部的MaterialTextInputLayout(custom_text_input_wrapper)。内部布局默认endIconMode为none,所以即使外部配置了属性也不会生效。 - 属性名不匹配:自定义组件代码中读取的属性名和
attrs.xml定义的不一致,比如R.styleable.CustomTextInputLayout_textInput_hint应该是R.styleable.CustomTextInputLayout_custom_hint,这类错误会导致属性读取异常。
修改后的自定义组件代码(避免额外getter/setter)
class CustomTextInputLayout @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0, defStyleRes: Int = 0, ) : ConstraintLayout(context, attrs, defStyleAttr, defStyleRes) { private val binding = CustomTextInputLayoutBinding.inflate(LayoutInflater.from(context), this) init { context.obtainStyledAttributes(attrs, R.styleable.CustomTextInputLayout, defStyleAttr, defStyleRes) .run { // 处理hint binding.customTextInputEditText.hint = getString(R.styleable.CustomTextInputLayout_custom_hint) ?: "" // 处理label if (getBoolean(R.styleable.CustomTextInputLayout_custom_show_label, false)) { binding.headerLabel.text = getString(R.styleable.CustomTextInputLayout_custom_label) ?: "" } else { binding.headerLabel.visibility = View.GONE } // 处理helper text if (getBoolean(R.styleable.CustomTextInputLayout_custom_show_helper_text, false)) { binding.customTextInputWrapper.helperText = getString(R.styleable.CustomTextInputLayout_custom_helper_text) ?: "" } // 处理endIcon相关属性 val endIconMode = getInt(R.styleable.CustomTextInputLayout_custom_end_icon_mode, 0) // 默认none binding.customTextInputWrapper.endIconMode = endIconMode val endIconDrawable = getDrawable(R.styleable.CustomTextInputLayout_custom_end_icon_drawable) endIconDrawable?.let { binding.customTextInputWrapper.endIconDrawable = it } val endIconDesc = getResourceId(R.styleable.CustomTextInputLayout_custom_end_icon_description, 0) if (endIconDesc != 0) { binding.customTextInputWrapper.setEndIconContentDescription(endIconDesc) } recycle() } } }
额外说明
- 枚举值的
value设置正确,和Material组件的TextInputLayout.EndIconMode对应,无需修改。 - 直接在
init块中操作内部布局的属性,避免了额外的getter/setter方法,符合需求。 - 确保读取的
styleable属性和attrs.xml中定义的名称完全一致,避免属性读取失败。
内容的提问来源于stack exchange,提问作者aj mitchell
相关产品推荐
相关产品推荐

