Search code examples
androidandroid-edittextandroid-inputtype

Where to read InputType in an extended EditText class


I've extended EditText and want to add some functionality depending on the given InputType. Fx. if the InputType is a password type then I want to check for that and change the right compound drawable.

Problem is that if I do that inside the init{} fun, then I get the default InputType 131073, which is not the right one.

Question is: Where in the EditText "lifecycle" can I get the right InputType indication?

open class EditText @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) :
        AppCompatEditText(context, attrs, defStyleAttr), View.OnTouchListener, View.OnFocusChangeListener, TextWatcherAdapter.TextWatcherListener {

    private var icon: Drawable? = null
    private var listener: Listener? = null

    private var touchListener: OnTouchListener? = null
    private var focusChangeListener: OnFocusChangeListener? = null

    init {
        icon = compoundDrawables[2]

        if (icon == null) {

            //////// NOT getting the right InputType !!!
            if (isPasswordInputType()) {
                icon = resources.getDrawable(R.drawable.ic_password_hide, null)
            } else {
                icon = resources.getDrawable(R.drawable.ic_delete_ring, null)
            }
        }

        icon?.setBounds(0, 0, icon!!.intrinsicWidth, icon!!.intrinsicHeight)
        setClearIconVisible(false)

        super.setOnTouchListener(this)
        super.setOnFocusChangeListener(this)
        addTextChangedListener(TextWatcherAdapter(this, this))
    }

    override fun onTouch(v: View, event: MotionEvent): Boolean {
        if (compoundDrawables[2] != null) {
            val left = width - icon!!.intrinsicWidth - paddingRight * 2
            val right = width
            val top = 0
            val bottom = height

            // If tapped on icon
            if (Rect(left, top, right, bottom).contains(event.x.toInt(), event.y.toInt())) {
                if (event.action == MotionEvent.ACTION_UP) {

                    //////// IS showing the right InputType !!!
                    if (isPasswordInputType()) {
                        if (transformationMethod == HideReturnsTransformationMethod.getInstance()) {
                            transformationMethod = PasswordTransformationMethod.getInstance()
                            icon = resources.getDrawable(R.drawable.ic_password_show, null)
                        } else if (transformationMethod == PasswordTransformationMethod.getInstance()) {
                            transformationMethod = HideReturnsTransformationMethod.getInstance()
                            icon = resources.getDrawable(R.drawable.ic_password_hide, null)
                        }
                        icon?.setBounds(0, 0, icon!!.intrinsicWidth, icon!!.intrinsicHeight)
                        setPasswordIconVisible(true)
                    } else {
                        setText("")
                    }

                    if (listener != null) {
                        listener!!.didClearText()
                    }
                }
                return true
            }
        }
        return if (touchListener != null) {
            touchListener!!.onTouch(v, event)
        } else false
    }

    override fun onFocusChange(v: View, hasFocus: Boolean) {
        if (isPasswordInputType()) {
            setClearIconVisible(true)
        } else {
            if (hasFocus) {
                setClearIconVisible(isNotEmpty(text!!))
            } else {
                setClearIconVisible(false)
            }
            if (focusChangeListener != null) {
                focusChangeListener!!.onFocusChange(v, hasFocus)
            }
        }
    }

    override fun onTextChanged(view: EditText, text: String) {
        if (isFocused) {
            setClearIconVisible(isNotEmpty(text))
        }
    }

    interface Listener {
        fun didClearText()
    }

    fun setListener(listener: Listener) {
        this.listener = listener
    }

    override fun setOnTouchListener(l: View.OnTouchListener) {
        this.touchListener = l
    }

    override fun setOnFocusChangeListener(f: View.OnFocusChangeListener) {
        this.focusChangeListener = f
    }

    fun setDefaultFocusChangeListener() {
        this.onFocusChangeListener = OnFocusChangeListener { v, hasFocus ->
            if (hasFocus) {
                elevation = resources.getDimensionPixelSize(R.dimen.edit_box_elevation_focused).toFloat()
            } else {
                elevation = resources.getDimensionPixelSize(R.dimen.edit_box_elevation).toFloat()
            }
        }
    }

    private fun isNotEmpty(text: Editable): Boolean {
        return isNotEmpty(text.toString())
    }

    private fun isNotEmpty(text: String?): Boolean {
        return text != null && text.isNotEmpty()
    }

    private fun setClearIconVisible(visible: Boolean) {
        val wasVisible = compoundDrawables[2] != null
        if (visible != wasVisible) {
            val d = if (visible) icon else null
            setCompoundDrawables(compoundDrawables[0], compoundDrawables[1], d, compoundDrawables[3])
        }
    }

    private fun setPasswordIconVisible(visible: Boolean) {
        val d = if (visible) icon else null
        setCompoundDrawables(compoundDrawables[0], compoundDrawables[1], d, compoundDrawables[3])
    }

    private fun isPasswordInputType(): Boolean {
        return (inputType == InputType.TYPE_NUMBER_VARIATION_PASSWORD ||
                inputType == InputType.TYPE_TEXT_VARIATION_PASSWORD ||
                inputType == InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD ||
                inputType == InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD ||
                inputType == InputType.TYPE_CLASS_TEXT + InputType.TYPE_NUMBER_VARIATION_PASSWORD ||
                inputType == InputType.TYPE_CLASS_TEXT + InputType.TYPE_TEXT_VARIATION_PASSWORD ||
                inputType == InputType.TYPE_CLASS_TEXT + InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD ||
                inputType == InputType.TYPE_CLASS_TEXT + InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD)
    }
}

Solution

  • OK, found the answer, so instead of deleting the question, I'll post the answer here:

    I moved the code that differentiates on InputType to the onAttachedToWindow event. In this lifecycle event the actual InputType is already present to work with:

    ...
    
    override fun onAttachedToWindow() {
        super.onAttachedToWindow()
    
        icon = compoundDrawables[2]
    
        if (icon == null) {
            icon = if (isPasswordInputType()) {
                resources.getDrawable(R.drawable.ic_password_show, null)
            } else {
                resources.getDrawable(R.drawable.ic_delete_ring, null)
            }
        }
    
        icon?.setBounds(0, 0, icon!!.intrinsicWidth, icon!!.intrinsicHeight)
        setClearIconVisible(false)
    }
    
    ...