android:password="true"
This hides the letters (****) but not immediately! When I type the letters, it take a while to hide.
For example, at the moment I type "a"; it shows ***a then it becomes ****.
How can I transform it immediately?
I believe this behaviour is intentional, as Android is used on smartphones with tiny little keyboards (physical and on-screen) where it's easy to make a typo. Displaying the letter briefly is so that the user can see if they typed something wrong, rather than hiding it and having no idea until they get an "incorrect password, your account has now been locked" type error!
I believe that the android:password="true" assigns a TransformationMethod to the text field which is responsible for converting the text into dots. I'm not an Android developer, but from reading the documentation I would imagine that this TransformationMethod has the delay built into the afterTextChanged callback. You could try writing your own TransformationMethod and play around with this and see if you can create your own version of the password masking rather than using the built-in one.
Just keep in mind the warnings in the doc, though, about avoiding infinite loops, because updating the text can re-trigger the events that you were notified about initially.
Implementation of TransformationMethod to hide letters when writing password:
public class LoginActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// example of usage
((TextView) findViewById(R.id.password)).setTransformationMethod(new HiddenPassTransformationMethod());
}
private class HiddenPassTransformationMethod implements TransformationMethod {
private char DOT = '\u2022';
#Override
public CharSequence getTransformation(final CharSequence charSequence, final View view) {
return new PassCharSequence(charSequence);
}
#Override
public void onFocusChanged(final View view, final CharSequence charSequence, final boolean b, final int i,
final Rect rect) {
//nothing to do here
}
private class PassCharSequence implements CharSequence {
private final CharSequence charSequence;
public PassCharSequence(final CharSequence charSequence) {
this.charSequence = charSequence;
}
#Override
public char charAt(final int index) {
return DOT;
}
#Override
public int length() {
return charSequence.length();
}
#Override
public CharSequence subSequence(final int start, final int end) {
return new PassCharSequence(charSequence.subSequence(start, end));
}
}
}
}
Related
I have a dialog with EditText in it. The EditText is already populated when it is created. When the user places the cursor on or near certain parts of the text a Toast will pop up.
My problem is listening for changes in cursor position. Another post asks the same question and the accepted solution was
You can override onSelectionChanged (int selStart, int selEnd) to get notified about selection changes. If the cursor is moved, this is called as well (in this case selStart == selEnd)
onSelectionChanged (int selStart, int selEnd) is a protected method of the TextView class. How do override it?
Just subclass or extend the class EditText and add the following code to the newly create class:
#Override
protected void onSelectionChanged(int selStart, int selEnd) {
// Do ur task here.
}
Don't forget to add constructors to the subclass. :)
You can actually listen to selection changes without subclassing an EditText. It's a little more complicated but still manageable. To do it you need to add a SpanWatcher to a text and handle changes of selection spans.
final SpanWatcher watcher = new SpanWatcher() {
#Override
public void onSpanAdded(final Spannable text, final Object what,
final int start, final int end) {
// Nothing here.
}
#Override
public void onSpanRemoved(final Spannable text, final Object what,
final int start, final int end) {
// Nothing here.
}
#Override
public void onSpanChanged(final Spannable text, final Object what,
final int ostart, final int oend, final int nstart, final int nend) {
if (what == Selection.SELECTION_START) {
// Selection start changed from ostart to nstart.
} else if (what == Selection.SELECTION_END) {
// Selection end changed from ostart to nstart.
}
}
};
editText.getText().setSpan(watcher, 0, 0, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
if anyone is still looking for a solution that does not Subclass the EditText:
(Code is in kotlin)
editText.setAccessibilityDelegate(object : View.AccessibilityDelegate() {
override fun sendAccessibilityEvent(host: View?, eventType: Int) {
super.sendAccessibilityEvent(host, eventType)
if (eventType == AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED){
// here you can access selection of the editText
// with `editText.selectionStart`
// and `editText.selectionEnd``
}
}
})
I debugged a related problem across different versions of Android (feel free to comment on your findings and I'll add them to the list).
Summary of findings:
4.1.6 (Samsung device)
onSelectionChanged() gets called on TEXT EDITS only.
4.0.6 (HTC Device)
4.0.4 (reported by user Arch1tect on Samsung Note 1 device)
onSelectionChanged() gets called on cursor changes (clicks, moves etc) but NOT on Text Edits.
In the cases above where you are not informed of the section changes (text edits in some versions of Android), you will have to do that using a TextWatcher, for example in the afterTextChanged() method.
Oh goodness, thanks so much for this idea. There's absolutely no reason why this feature shouldn't be in the SDK. I have an quick subclass that implements this idea but adding on the additional feature of listeners for when the selection changes. Hope it's useful.
public class EditTextSelectable extends EditText {
public interface OnSelectionChangedListener {
public void onSelectionChanged(int selStart, int selEnd);
}
private List<onSelectionChangedListener> listeners
= new ArrayList<onSelectionChangedListener>();
public void addOnSelectionChangedListener(OnSelectionChangedListener l) {
listeners.add(l);
}
public void removeOnSelectionChangedListener(OnSelectionChangedListener l) {
listeners.remove(l);
}
#Override
protected void onSelectionChanged(int selStart, int selEnd) {
for (onSelectionChangedListener l : listeners)
l.onSelectionChanged(selStart, selEnd);
}
}
}
The java version of the above answer,
mEtEditor.setAccessibilityDelegate(new View.AccessibilityDelegate(){
/**
* Sends an accessibility event of the given type. If accessibility is not
* enabled this method has no effect.
* <p>
* The default implementation behaves as {#link View#sendAccessibilityEvent(int)
* View#sendAccessibilityEvent(int)} for the case of no accessibility delegate
* been set.
* </p>
*
* #param host The View hosting the delegate.
* #param eventType The type of the event to send.
* #see View#sendAccessibilityEvent(int) View#sendAccessibilityEvent(int)
*/
#Override
public void sendAccessibilityEvent(View host, int eventType) {
super.sendAccessibilityEvent(host, eventType);
if (eventType == AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED){
// here you can access selection of the editText
// with `editText.selectionStart`
// and `editText.selectionEnd``
}
}
});
Here is an Extension version of what #Saeed Entezari posted:
fun EditText.setSelectionChangedListener(onSelectionChangedListener: (editText: EditText, selectionStart: Int, selectionEnd: Int) -> Unit) {
setAccessibilityDelegate(object : View.AccessibilityDelegate() {
override fun sendAccessibilityEvent(host: View?, eventType: Int) {
super.sendAccessibilityEvent(host, eventType)
if (eventType == AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED){
val editText = this#setSelectionChangedListener
onSelectionChangedListener.invoke(editText, editText.selectionStart, editText.selectionEnd)
}
}
})
}
The Question asker originally posted their answer into the question. I'm moving it into an answer to keep the question and answer separated.
Step One: Create the sub class
package com.example;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.EditText;
import android.widget.Toast;
public class EditTextCursorWatcher extends EditText {
public EditTextCursorWatcher(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
}
public EditTextCursorWatcher(Context context, AttributeSet attrs) {
super(context, attrs);
}
public EditTextCursorWatcher(Context context) {
super(context);
}
#Override
protected void onSelectionChanged(int selStart, int selEnd) {
Toast.makeText(getContext(), "selStart is " + selStart + "selEnd is " + selEnd, Toast.LENGTH_LONG).show();
}
}
Step Two: refer to the class in the layout file (eg main.xml (though mine was a custom dialog layout)). Don't forget to use full package name (in this case com.example.EditTextCursorWatcher, eg
<com.example.EditTextCursorWatcher
android:id="#+id/etEdit"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="top"
android:minLines="5"
android:inputType="textMultiLine"/>
editText.doAfterTextChanged {
doSomething(editText.selectionStart , editText.selectionEnd)
}
editText.setOnClickListener {
doSomething(editText.selectionStart , editText.selectionEnd)
}
I am writing a RichTextEditor class, which has an inner RichTextEditorTextWatcher class. I am seeing a discrepancy from what I see on the screen, versus what I get when I call e.getSpans inside beforeTextChanged.
On the screen, I see text on the screen (in my case, a single character) that does not have any style applied to it, but the e.getSpans() call actually says that I have a bold style applied.
Is this a known Android bug?
public class RichTextEditor extends AppCompatEditText
{
// other code not shown
public class RichTextEditorTextWatcher implements TextWatcher
{
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after)
{
if (after == 0) //deletion occurred
{
isDeletion = true;
Editable e = RichTextEditor.this.getText();
/** The next line is the problematic line!
* this.prevStyles returns a StyleSpan (bold) even when I don't see it on the screen for that character.
*/
this.prevStyles = e.getSpans(start, start+count, CharacterStyle.class);
for (CharacterStyle c : this.prevStyles)
{
if (c instanceof StyleSpan)
{
if (((StyleSpan)c).getStyle() == Typeface.BOLD)
boldButton.setChecked(true);
else
boldButton.setChecked(false);
}
}
}
else
isDeletion = false;
}
}
}
I have an app where people need to login with a password. I would like for only the last character typed to be shown, but all I seem to get is all chars dots or all chars visible.
I tried a few things:
password.setInputType(InputType.TYPE_TEXT_VARIATION_PASSWORD);
password.setTransformationMethod(PasswordTransformationMethod.getInstance());
password.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
and setting inputtype in the xml.
I have only tested on a galaxy s2 as that is the only android device at my office at the moment, so I don't know if the problem is only with my device.
edit:
Just tested on an HTC Sensation from a colleague and it does work as intended on his phone, but the question remains how to get this same thing on the Galaxy S2?
It's been almost 1.5 years since this was asked :P. But I had the same requirement and was successfully able to implement it. Pass an object of the MyTransformation class as a parameter in the setTransformationMethod and you're good to go :) Here's the code.
public class MyTransformation 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) {
//This is the check which makes sure the last character is shown
if(index != mSource.length()-1)
return '•';
else
return mSource.charAt(index);
}
public int length() {
return mSource.length(); // Return default
}
public CharSequence subSequence(int start, int end) {
return mSource.subSequence(start, end); // Return default
}
}
}
Improving on AndyFaizan's answer, my solution doesn't show characters while using backspace.
Without this (simple) change it'd be possible to reveal the whole password by deleting characters at the end of the input.
public class CustomPasswordTransformation extends PasswordTransformationMethod {
boolean lastActionWasDelete = false;
#Override
public CharSequence getTransformation(CharSequence source, View view) {
return new PasswordCharSequence(source);
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
super.onTextChanged(s, start, before, count);
this.lastActionWasDelete = before > count;
}
private class PasswordCharSequence implements CharSequence {
private CharSequence source;
PasswordCharSequence(CharSequence source) {
this.source = source;
}
public char charAt(int index) {
//This is the check which makes sure the last character is shown
if (!lastActionWasDelete && index == source.length() - 1) return source.charAt(index);
return '•';
}
public int length() {
return source.length(); // Return default
}
public CharSequence subSequence(int start, int end) {
return source.subSequence(start, end); // Return default
}
}
}
As ethan already mentioned ages ago, the user can toggle the password character display behaviour in the system security settings.
To hide the password just use
password.setInputType(InputType.TYPE_TEXT_VARIATION_PASSWORD);
the rest is taken care of by the operation system.
In my case i wondered about different behaviour on a variation of devices.
After some investigation the security setting 'Visible Passwords' was turned off on the device which did not show the last character.
If you must ignore the system wide setting, AndyFaizan 's password transformation solution is still the way to go.
I am working on the task that requires the password field (i.e.the Edit Text) to hide user input using asterisks(*) rather than dots(.). Currently it shows as dots.
Kindly tell me the way to do it if its possible using android's native methods. Or please post the code to do it if anyone has already done that.
Thanks in advance..
Very late answer, and I'm sure you don't care anymore, but someone else might.
Initialize EditText Field .
EditText UPL =(EditText) findViewById(R.id.UserPasswordToLogin) ;
UPL.setTransformationMethod(new AsteriskPasswordTransformationMethod());
Then Create a new java class ,Called AsteriskPasswordTransformationMethod.java Which extends PasswordTransformationMethod
Here is code :
import android.text.method.PasswordTransformationMethod;
import android.view.View;
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
}
}
};
public final void setTransformationMethod (TransformationMethod method)
Since: API Level 1
Sets the transformation that is applied to the text that this TextView is displaying.
Related XML Attributes
android:password
android:singleLine
allows you to change any char
I would imagine you could override the listener class methods to modify the text to display so that it reads as "*", but keep the actual string in the background somewhere. So each time the user enters a letter, you add it to your cumulative "password" string, and instead, replace that character in the displayed string with *
I have a dialog with EditText in it. The EditText is already populated when it is created. When the user places the cursor on or near certain parts of the text a Toast will pop up.
My problem is listening for changes in cursor position. Another post asks the same question and the accepted solution was
You can override onSelectionChanged (int selStart, int selEnd) to get notified about selection changes. If the cursor is moved, this is called as well (in this case selStart == selEnd)
onSelectionChanged (int selStart, int selEnd) is a protected method of the TextView class. How do override it?
Just subclass or extend the class EditText and add the following code to the newly create class:
#Override
protected void onSelectionChanged(int selStart, int selEnd) {
// Do ur task here.
}
Don't forget to add constructors to the subclass. :)
You can actually listen to selection changes without subclassing an EditText. It's a little more complicated but still manageable. To do it you need to add a SpanWatcher to a text and handle changes of selection spans.
final SpanWatcher watcher = new SpanWatcher() {
#Override
public void onSpanAdded(final Spannable text, final Object what,
final int start, final int end) {
// Nothing here.
}
#Override
public void onSpanRemoved(final Spannable text, final Object what,
final int start, final int end) {
// Nothing here.
}
#Override
public void onSpanChanged(final Spannable text, final Object what,
final int ostart, final int oend, final int nstart, final int nend) {
if (what == Selection.SELECTION_START) {
// Selection start changed from ostart to nstart.
} else if (what == Selection.SELECTION_END) {
// Selection end changed from ostart to nstart.
}
}
};
editText.getText().setSpan(watcher, 0, 0, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
if anyone is still looking for a solution that does not Subclass the EditText:
(Code is in kotlin)
editText.setAccessibilityDelegate(object : View.AccessibilityDelegate() {
override fun sendAccessibilityEvent(host: View?, eventType: Int) {
super.sendAccessibilityEvent(host, eventType)
if (eventType == AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED){
// here you can access selection of the editText
// with `editText.selectionStart`
// and `editText.selectionEnd``
}
}
})
I debugged a related problem across different versions of Android (feel free to comment on your findings and I'll add them to the list).
Summary of findings:
4.1.6 (Samsung device)
onSelectionChanged() gets called on TEXT EDITS only.
4.0.6 (HTC Device)
4.0.4 (reported by user Arch1tect on Samsung Note 1 device)
onSelectionChanged() gets called on cursor changes (clicks, moves etc) but NOT on Text Edits.
In the cases above where you are not informed of the section changes (text edits in some versions of Android), you will have to do that using a TextWatcher, for example in the afterTextChanged() method.
Oh goodness, thanks so much for this idea. There's absolutely no reason why this feature shouldn't be in the SDK. I have an quick subclass that implements this idea but adding on the additional feature of listeners for when the selection changes. Hope it's useful.
public class EditTextSelectable extends EditText {
public interface OnSelectionChangedListener {
public void onSelectionChanged(int selStart, int selEnd);
}
private List<onSelectionChangedListener> listeners
= new ArrayList<onSelectionChangedListener>();
public void addOnSelectionChangedListener(OnSelectionChangedListener l) {
listeners.add(l);
}
public void removeOnSelectionChangedListener(OnSelectionChangedListener l) {
listeners.remove(l);
}
#Override
protected void onSelectionChanged(int selStart, int selEnd) {
for (onSelectionChangedListener l : listeners)
l.onSelectionChanged(selStart, selEnd);
}
}
}
The java version of the above answer,
mEtEditor.setAccessibilityDelegate(new View.AccessibilityDelegate(){
/**
* Sends an accessibility event of the given type. If accessibility is not
* enabled this method has no effect.
* <p>
* The default implementation behaves as {#link View#sendAccessibilityEvent(int)
* View#sendAccessibilityEvent(int)} for the case of no accessibility delegate
* been set.
* </p>
*
* #param host The View hosting the delegate.
* #param eventType The type of the event to send.
* #see View#sendAccessibilityEvent(int) View#sendAccessibilityEvent(int)
*/
#Override
public void sendAccessibilityEvent(View host, int eventType) {
super.sendAccessibilityEvent(host, eventType);
if (eventType == AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED){
// here you can access selection of the editText
// with `editText.selectionStart`
// and `editText.selectionEnd``
}
}
});
Here is an Extension version of what #Saeed Entezari posted:
fun EditText.setSelectionChangedListener(onSelectionChangedListener: (editText: EditText, selectionStart: Int, selectionEnd: Int) -> Unit) {
setAccessibilityDelegate(object : View.AccessibilityDelegate() {
override fun sendAccessibilityEvent(host: View?, eventType: Int) {
super.sendAccessibilityEvent(host, eventType)
if (eventType == AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED){
val editText = this#setSelectionChangedListener
onSelectionChangedListener.invoke(editText, editText.selectionStart, editText.selectionEnd)
}
}
})
}
The Question asker originally posted their answer into the question. I'm moving it into an answer to keep the question and answer separated.
Step One: Create the sub class
package com.example;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.EditText;
import android.widget.Toast;
public class EditTextCursorWatcher extends EditText {
public EditTextCursorWatcher(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
}
public EditTextCursorWatcher(Context context, AttributeSet attrs) {
super(context, attrs);
}
public EditTextCursorWatcher(Context context) {
super(context);
}
#Override
protected void onSelectionChanged(int selStart, int selEnd) {
Toast.makeText(getContext(), "selStart is " + selStart + "selEnd is " + selEnd, Toast.LENGTH_LONG).show();
}
}
Step Two: refer to the class in the layout file (eg main.xml (though mine was a custom dialog layout)). Don't forget to use full package name (in this case com.example.EditTextCursorWatcher, eg
<com.example.EditTextCursorWatcher
android:id="#+id/etEdit"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="top"
android:minLines="5"
android:inputType="textMultiLine"/>
editText.doAfterTextChanged {
doSomething(editText.selectionStart , editText.selectionEnd)
}
editText.setOnClickListener {
doSomething(editText.selectionStart , editText.selectionEnd)
}