0

I'm trying to figure out how to set the endIconMode in a custom text input layout. I am trying to set this in the attrs.xml, not programmatically (this is how I'm asked to do it at work)

here's what it looks like:

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>

And here is the 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"
  />

however the icon is not showing up at all. What am I missing? All of the other values are showing up as expected except the end icon.

This is why I chose to do the endIconMode the way I did -

I'd also like to avoid getters and setters if possible, but not sure how to go about doing that. Here's my custom text input component:

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
  }

here is the .xml layout

<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>

Thanks!!

  • You would need to pull `custom_end_icon_mode`'s value from the `AttributeSet` in the relevant constructor and call `setEndIconMode()` with it, but that's probably not really what you want to do 'cause it's kinda pointless. The `app:endIconMode` attribute still works in your subclass just like it does in plain old `TextInputLayout`; you can just use that. If you need access to that value inside your custom class, use `getEndIconMode()`. You don't need to redefine all of `TextInputLayout`'s attributes. You just need to define attributes for whatever custom functionality or behavior you're adding – Mike M. Jan 25 '23 at 04:21
  • thanks for your response @MikeM! using the endIconMode is not working. Another question is how would I access the textInputEditText inside the textInputLayout without using a getter/setter? – aj mitchell Jan 25 '23 at 18:20
  • @MikeM. I've added the class as well. – aj mitchell Jan 25 '23 at 18:51
  • I've added the xml. I'm trying to keep private the names of classes and things like that since it is work related, so apologies if I've got a name wrong here or there. but my overall issues are getting the icon to show, and finding a way to access the textInputEditText inside the textInputLayout without using getters and setters. example, how do I get access to the textInputEditText text without saying something like fun getEditText() { return binding.textInputEditText } and in the class using the component textInputLayout.getEditText().text – aj mitchell Jan 25 '23 at 19:21
  • Oh, I should've looked closer. Your class isn't a custom `TextInputLayout`. It's a custom `ConstraintLayout`; it just has a `TextInputLayout` inside of it. Your problem is that those attributes on the `` tag only go to your `CustomTextInputLayout`. They don't get passed down to any of its children. That's why `app:endIconMode` didn't work automatically. I'm not sure if this counts as doing it programmatically, but to get either attribute working will require code to be added to `CustomTextInputLayout`. – Mike M. Jan 25 '23 at 19:28
  • As for avoiding the setters and getters, you can't, really. As I mention above, your class isn't actually a `TextInputLayout`, it's a `ConstraintLayout`, so you'll have to have some sort of relay function or property to call `getEditText()` and `text` on the nested `TextInputLayout`. – Mike M. Jan 25 '23 at 19:31
  • ah I see. damn. that makes sense, not sure how to proceed though. I might leave that to my lead to tell me how to do that w/o using getEditText(). any idea what to do about the icon? I could add it into my attrs.xml, but I'd essentially just be rewriting the endIconMode into the attrs.xml and it didn't work when I tried it earlier – aj mitchell Jan 25 '23 at 19:41
  • Well, you'd have to retrieve it in the constructor like the other attributes there, and then manually set it on `binding.customTextInputLayout`. Unfortunately, you'd have to do that with all of `TextInputLayout`'s inherent ones, 'cause it doesn't know anything about `custom_end_icon_mode` or your other attributes, and it won't get those attributes anyway, as mentioned. There is a way to do this that'll let you use the regular attributes and pass those directly to the nested `TextInputLayout`, but it would require instantiating the `TextInputLayout` directly, instead of inflating it from XML – Mike M. Jan 25 '23 at 19:50
  • If it might help, I reworked this a bit to show what I meant by using the regular attributes and passing them straight through: https://drive.google.com/file/d/1ERN7DQ1wDL1EEvZDjiDiXDTdmkZC6V_1/view?usp=share_link. With placeholder strings, it looks like: https://i.stack.imgur.com/Ijf7Q.png. I used a `LinearLayout` instead 'cause it's easier to code a simple stack of `View`s with it. You'd still have to write relay functions for some things you'd want to modify at runtime after inflation, but you wouldn't have to mess with redundant attributes or the extra code necessary to pass them manually. – Mike M. Jan 26 '23 at 02:50

0 Answers0