Tima
Tima

Reputation: 12905

Android. TextInputLayout. Toggle password visibility event listener?

There is a password visibility toggle button within TextInputLayout for InputType textPassword.

Is it somehow possible to catch toggle events?

I couldn't find any public methods for this

Upvotes: 3

Views: 2855

Answers (5)

Paul Spiesberger
Paul Spiesberger

Reputation: 5770

You can do the following since Material Components v1.1.0 (Kotlin):

textInputLayout.setEndIconOnClickListener {
    // do something here
}

Upvotes: 1

Rohan
Rohan

Reputation: 859

I'm using binding here.

This is the id of the layout - regPasswordlayout default id of the password toggle - text_input_password_toggle

binding.regPasswordlayout.findViewById(R.id.text_input_password_toggle)
.setOnClickListener(new View.OnClickListener() {
         @Override
         public void onClick(View v) {
              // your code goes here
         }
     });

Upvotes: 0

Rodrigo Queiroz
Rodrigo Queiroz

Reputation: 2844

After analyzing the View hierarchy with Layout Inspector I can see the following:

View Hierarchy screenshot

So you can either do:

val toggle = password.findViewById<CheckableImageButton?>(R.id.text_input_password_toggle)
toggle?.setOnClickListener {
    println("clicked on toggle")
}

or manually:

// Assuming you have AndroidKTX
val views = password.children
for (v in views) {
    if (v is FrameLayout) {
        val innerViews = v.children
        for (iv in innerViews) {
            if (iv is CheckableImageButton) {
                iv.setOnClickListener { 
                    println("clicked on toggle")
                }
            }
        }
    }
}

Upvotes: 0

GaRRaPeTa
GaRRaPeTa

Reputation: 5598

You can use this:

import android.annotation.SuppressLint;
import android.content.Context;
import android.support.design.widget.CheckableImageButton;
import android.support.design.widget.TextInputLayout;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

import com.ctm.core.ui.R;

import org.jetbrains.annotations.NotNull;

/**
 * Extended {@link TextInputLayout} allowing setting a listener when the user toggles the visibility
 * of the password.
 */
public class TextInputLayoutEx extends TextInputLayout {

    public TextInputLayoutEx(Context context) {
        super(context);
    }

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

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

    public void setOnToggleShowPasswordListener(OnToggleShowPasswordListener listener) {
        if (listener != null) {
            initTogglePasswordShownListener(listener);
        }
    }

    @SuppressLint({"RestrictedApi"})
    private void initTogglePasswordShownListener(@NotNull OnToggleShowPasswordListener listener) {
        final View revealPasswordView = this.findViewById(R.id.text_input_password_toggle);
        if (revealPasswordView == null) {
            return;
        }
        if (!(revealPasswordView instanceof CheckableImageButton)) {
            return;
        }

        final CheckableImageButton revealPasswordCheckableImageButton = (CheckableImageButton) revealPasswordView;
        revealPasswordView.setOnTouchListener((view, motionEvent) -> {
            if (MotionEvent.ACTION_UP == motionEvent.getAction()) {
                listener.onToggleShowPasswordClicked(!revealPasswordCheckableImageButton.isChecked());
                return view.performClick();
            }
            return false;
        });
    }

    public interface OnToggleShowPasswordListener {
        void onToggleShowPasswordClicked(boolean isShown);
    }
}

In client code:

TextInputLayoutEx layoutPassword = (TextInputLayoutEx) findByById(...)
layoutPassword.setPasswordVisibilityToggleEnabled(true);
        layoutPassword.setOnToggleShowPasswordListener(isShown -> Toast.makeText(SignInActivity.this, "Password shown: " + isShown, Toast.LENGTH_SHORT).show());

Upvotes: 0

Tima
Tima

Reputation: 12905

I looked at the source code of the TextInputLayout to find the type of the view of the toggle button. Its CheckableImageButton. Everything else is easy. You need to find the view iterating recursively over children of the TextInputLayout View. And then setOnTouchListener as @MikeM suggested in the comments.

View togglePasswordButton = findTogglePasswordButton(mTextInputLayoutView);
if (togglePasswordButton != null) {
    togglePasswordButton.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View view, MotionEvent motionEvent) {
            // implementation
            return false;
        }
    });
}

private View findTogglePasswordButton(ViewGroup viewGroup) {
    int childCount = viewGroup.getChildCount();
    for (int ind = 0; ind < childCount; ind++) {
        View child = viewGroup.getChildAt(ind);
        if (child instanceof ViewGroup) {
            View togglePasswordButton = findTogglePasswordButton((ViewGroup) child);
            if (togglePasswordButton != null) {
                return togglePasswordButton;
            }
        } else if (child instanceof CheckableImageButton) {
            return child;
        }
    }
    return null;
}

An alternative implmentation of findTogglePasswordButton

private View findTogglePasswordButton() {
    return findViewById(R.id.text_input_password_toggle);
}

@MikeM. thank you for id

Upvotes: 6

Related Questions