'Edittext togglepassword dots changes to '*' for the first time and then reappears again as dots
I have used Textinputlayout and edittext inside it. And have used one method to change the default icon for dots to * , but once clicked on eye icon it again changes it to dots only. How to manage this?
Below is my code:-
.java file
oldpw=(EditText) findViewById(R.id.oldpw);
oldpw.setTransformationMethod(new AsteriskPasswordTransformationMethod());
public class AsteriskPasswordTransformationMethod extends PasswordTransformationMethod {
@Override
public CharSequence getTransformation(CharSequence source, View view) {
return new PasswordCharSequence(source);
}
private class PasswordCharSequence implements CharSequence {
private CharSequence mSource;
public PasswordCharSequence(CharSequence source) {
mSource = source; // Store char sequence
}
public char charAt(int index) {
return '*'; // This is the important part
}
public int length() {
return mSource.length(); // Return default
}
public CharSequence subSequence(int start, int end) {
return mSource.subSequence(start, end); // Return default
}
}
};
xml file
<android.support.design.widget.TextInputLayout
android:layout_marginTop="15dp"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:layout_marginBottom="15dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:passwordToggleEnabled="true"
app:passwordToggleDrawable="@drawable/show_password_selector"
app:passwordToggleTint="#989898"
android:id="@+id/et1"
android:padding="0dp">
<EditText
android:hint="Old Password"
android:inputType="textPassword"
android:id="@+id/oldpw"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</android.support.design.widget.TextInputLayout>
Solution 1:[1]
Below code worked for me.
class MainActivity : AppCompatActivity() {
var tiet_password:TextInputEditText? = null
var til_password: TextInputLayout? = null
var isPasswordVisible = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
tiet_password = findViewById(R.id.tiet_password)
til_password = findViewById(R.id.til_password)
tiet_password?.transformationMethod = AsteriskPassword()
with(til_password) { this?.addEndIconClickListener() }
}
}
private fun TextInputLayout.addEndIconClickListener() {
var isPasswordVisible = false
this.setEndIconOnClickListener {
if (isPasswordVisible) {
isPasswordVisible = false
this.editText!!.transformationMethod = AsteriskPasswordTransformationMethod()
} else {
isPasswordVisible = true
this.editText!!.transformationMethod = HideReturnsTransformationMethod()
}
}
}
class AsteriskPassword : PasswordTransformationMethod() {
override fun getTransformation(source: CharSequence, view: View): CharSequence {
return PasswordCharSequence(source)
}
private inner class PasswordCharSequence(private val mSource: CharSequence) :
CharSequence {
override val length: Int
get() = mSource.length
override fun get(index: Int): Char {
return '*'
}
override fun subSequence(start: Int, end: Int): CharSequence {
return mSource.subSequence(start, end)
}
}
}
private const val DOT = '*'
class AsteriskPasswordTransformationMethod : PasswordTransformationMethod() {
private val ACTIVE: Any = NoCopySpan.Concrete()
override fun getTransformation(source: CharSequence?, view: View?): CharSequence {
if (source is Spannable) {
val vr = source.getSpans(0, source.length, ViewReference::class.java)
vr.forEach { source.removeSpan(it) }
removeVisibleSpans(source)
source.setSpan(ViewReference(view!!), 0, 0, Spannable.SPAN_POINT_POINT)
}
return PasswordCharSequence(source!!)
}
private fun removeVisibleSpans(sp: Spannable) {
val old = sp.getSpans(0, sp.length, Visible::class.java)
old.forEach { sp.removeSpan(it) }
}
private inner class PasswordCharSequence(private val mSource: CharSequence) : CharSequence, GetChars {
override val length: Int
get() = mSource.length
override fun get(index: Int): Char {
if (mSource is Spanned) {
var st = mSource.getSpanStart(ACTIVE)
var en = mSource.getSpanEnd(ACTIVE)
if (index in st until en) return mSource[index]
val visible = mSource.getSpans(0, mSource.length, Visible::class.java)
visible.forEach {
if (mSource.getSpanStart(it.mTransformer) >= 0) {
st = mSource.getSpanStart(it)
en = mSource.getSpanEnd(it)
if (index in st until en) return mSource[index]
}
}
}
return DOT
}
override fun subSequence(startIndex: Int, endIndex: Int): CharSequence {
val buf = CharArray(endIndex - startIndex)
getChars(startIndex, endIndex, buf, 0)
return String(buf)
}
override fun toString(): String = subSequence(0, length).toString()
override fun getChars(start: Int, end: Int, dest: CharArray?, destoff: Int) {
TextUtils.getChars(mSource, start, end, dest, destoff)
var st = -1
var en = -1
var nvisible = 0
var starts: IntArray? = null
var ends: IntArray? = null
if (mSource is Spanned) {
st = mSource.getSpanStart(ACTIVE)
en = mSource.getSpanEnd(ACTIVE)
val visible = mSource.getSpans(0, mSource.length, Visible::class.java)
nvisible = visible.size
starts = IntArray(nvisible)
ends = IntArray(nvisible)
for (i in 0 until nvisible) {
if (mSource.getSpanStart(visible[i].mTransformer) >= 0) {
starts[i] = mSource.getSpanStart(visible[i])
ends[i] = mSource.getSpanEnd(visible[i])
}
}
}
for (i in start until end) {
if (i !in st until en) {
var visible = false
for (a in 0 until nvisible) {
if (i >= starts!![a] && i < ends!![a]) {
visible = true
break
}
}
if (!visible) dest?.set(i - start + destoff, DOT)
}
}
}
}
private inner class Visible(private val mText: Spannable, val mTransformer: AsteriskPasswordTransformationMethod) : Handler(
Looper.getMainLooper()), UpdateLayout, Runnable {
init {
postAtTime(this, SystemClock.uptimeMillis() + 1000)
}
override fun run() {
mText.removeSpan(this)
}
}
private inner class ViewReference(v: View) : WeakReference<View>(v), NoCopySpan
}
XML file is:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/til_email"
android:layout_width="match_parent"
android:layout_height="90dp"
android:layout_marginTop="32dp"
android:hint="Email">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/tiet_email"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textEmailAddress"
android:paddingBottom="20dp"
android:singleLine="true" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/til_password"
android:layout_width="match_parent"
android:layout_height="90dp"
android:hint="Password"
android:longClickable="false"
android:layout_gravity="center"
android:layout_marginBottom="5dp"
android:layout_marginTop="5dp"
android:gravity="center"
app:passwordToggleEnabled="true">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/tiet_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textWebPassword"
android:paddingBottom="20dp"
android:drawableEnd="@drawable/password_enabled_eye"/>
</com.google.android.material.textfield.TextInputLayout>
<Button
android:id="@+id/btn_login"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:paddingStart="50dp"
android:paddingEnd="50dp"
android:text="Login"/>
</LinearLayout>
Solution 2:[2]
Use this method:
In you XML layout
<EditText
android:id="@+id/etPassword"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Enter Password"
android:inputType="textPassword"
android:drawableEnd="@drawable/eye" <!--setting eye icon here-->
/>
Then in your Java class.
set Boolean globally.
Boolean isPasswordVisible = false;
Then add this in onCreate()
etPassword.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
final int DRAWABLE_RIGHT = 2;
if(event.getAction() == MotionEvent.ACTION_UP) {
if(event.getRawX() >= (etPassword.getRight() - etPassword.getCompoundDrawables()[DRAWABLE_RIGHT].getBounds().width())) {
if (isPasswordVisible) {
isPasswordVisible = false;
etPassword.setTransformationMethod(new PasswordTransformationMethod());
} else {
isPasswordVisible = true;
etPassword.setTransformationMethod(new HideReturnsTransformationMethod());
}
return true;
}
}
return false;
}
});
Hope this helps you !
Solution 3:[3]
I have made this custom class with my modification. It works even you click on Password Toogle Icon. It works every time.
Custom Password Transformation Class:
private const val DOT = '?'
class AsteriskPasswordTransformationMethod : PasswordTransformationMethod() {
private val ACTIVE: Any = NoCopySpan.Concrete()
override fun getTransformation(source: CharSequence?, view: View?): CharSequence {
if (source is Spannable) {
/*
* Remove any references to other views that may still be
* attached. This will happen when you flip the screen
* while a password field is showing; there will still
* be references to the old EditText in the text.
*/
val vr = source.getSpans(0, source.length, ViewReference::class.java)
vr.forEach { source.removeSpan(it) }
removeVisibleSpans(source)
source.setSpan(ViewReference(view!!), 0, 0, Spannable.SPAN_POINT_POINT)
}
return PasswordCharSequence(source!!)
}
private fun removeVisibleSpans(sp: Spannable) {
val old = sp.getSpans(0, sp.length, Visible::class.java)
old.forEach { sp.removeSpan(it) }
}
private inner class PasswordCharSequence(private val mSource: CharSequence) : CharSequence, GetChars {
override val length: Int
get() = mSource.length
override fun get(index: Int): Char {
if (mSource is Spanned) {
var st = mSource.getSpanStart(ACTIVE)
var en = mSource.getSpanEnd(ACTIVE)
if (index in st until en) return mSource[index]
val visible = mSource.getSpans(0, mSource.length, Visible::class.java)
visible.forEach {
if (mSource.getSpanStart(it.mTransformer) >= 0) {
st = mSource.getSpanStart(it)
en = mSource.getSpanEnd(it)
if (index in st until en) return mSource[index]
}
}
}
return DOT
}
override fun subSequence(startIndex: Int, endIndex: Int): CharSequence {
val buf = CharArray(endIndex - startIndex)
getChars(startIndex, endIndex, buf, 0)
return String(buf)
}
override fun toString(): String = subSequence(0, length).toString()
override fun getChars(start: Int, end: Int, dest: CharArray?, destoff: Int) {
TextUtils.getChars(mSource, start, end, dest, destoff)
var st = -1
var en = -1
var nvisible = 0
var starts: IntArray? = null
var ends: IntArray? = null
if (mSource is Spanned) {
st = mSource.getSpanStart(ACTIVE)
en = mSource.getSpanEnd(ACTIVE)
val visible = mSource.getSpans(0, mSource.length, Visible::class.java)
nvisible = visible.size
starts = IntArray(nvisible)
ends = IntArray(nvisible)
for (i in 0 until nvisible) {
if (mSource.getSpanStart(visible[i].mTransformer) >= 0) {
starts[i] = mSource.getSpanStart(visible[i])
ends[i] = mSource.getSpanEnd(visible[i])
}
}
}
for (i in start until end) {
if (i !in st until en) {
var visible = false
for (a in 0 until nvisible) {
if (i >= starts!![a] && i < ends!![a]) {
visible = true
break
}
}
if (!visible) dest?.set(i - start + destoff, DOT)
}
}
}
}
private inner class Visible(private val mText: Spannable, val mTransformer: AsteriskPasswordTransformationMethod) : Handler(Looper.getMainLooper()), UpdateLayout, Runnable {
init {
postAtTime(this, SystemClock.uptimeMillis() + 1000)
}
override fun run() {
mText.removeSpan(this)
}
}
/**
* Used to stash a reference back to the View in the Editable so we
* can use it to check the settings.
*/
private inner class ViewReference(v: View) : WeakReference<View>(v), NoCopySpan
}
Use it in TextInputor EditText like this. I created an Extension for this. Check:
fun TextInputLayout.addEndIconClickListener() {
var isPasswordVisible = false
this.setEndIconOnClickListener {
if (isPasswordVisible) {
isPasswordVisible = false
this.editText!!.transformationMethod = AsteriskPasswordTransformationMethod()
} else {
isPasswordVisible = true
this.editText!!.transformationMethod = HideReturnsTransformationMethod()
}
}
}
Then in Fragment or Activity, set Transformation Method like this.
binding.etPin.editText?.transformationMethod = AsteriskPasswordTransformationMethod()
binding.etPin.addEndIconClickListener()
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 | |
| Solution 3 | Nafis Kabbo |

