Is there a way to have a Multi-Line EditText present and use the IME Action Label "Done" on Android 2.3?
In Android 2.2 this is not a problem, the enter button shows the IME Action Label "Done" (android:imeActionLabel="actionDone"), and dismisses Soft Input when clicked.
When configuring an EditText for multi-line, Android 2.3 removes the ability to show the "Done" action for the Soft Input keyboard.
I have managed to alter the behaviour of the Soft Input enter button by using a KeyListener, however the enter button still looks like an enter key.
Here is the declaration of the EditText
<EditText
android:id="#+id/Comment"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:layout_marginBottom="0dp"
android:lines="3"
android:maxLines="3"
android:minLines="3"
android:maxLength="60"
android:scrollHorizontally="false"
android:hint="hint"
android:gravity="top|left"
android:textColor="#888"
android:textSize="14dp"
/>
<!-- android:inputType="text" will kill the multiline on 2.3! -->
<!-- android:imeOptions="actionDone" switches to a "t9" like soft input -->
When I check the inputType value after loading setting the content view in the activity, it shows up as:
inputType = 0x20001
Which is:
class = TYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_NORMAL
flags = InputType.TYPE_TEXT_FLAG_MULTI_LINE
Well, after re-reading the TextView and EditorInfo docs, it has become clear that the platform is going to force IME_FLAG_NO_ENTER_ACTION for multi-line text views.
Note that TextView will automatically
set this flag for you on multi-line
text views.
My solution is to subclass EditText and adjust the IME options after letting the platform configure them:
#Override
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
InputConnection connection = super.onCreateInputConnection(outAttrs);
int imeActions = outAttrs.imeOptions&EditorInfo.IME_MASK_ACTION;
if ((imeActions&EditorInfo.IME_ACTION_DONE) != 0) {
// clear the existing action
outAttrs.imeOptions ^= imeActions;
// set the DONE action
outAttrs.imeOptions |= EditorInfo.IME_ACTION_DONE;
}
if ((outAttrs.imeOptions&EditorInfo.IME_FLAG_NO_ENTER_ACTION) != 0) {
outAttrs.imeOptions &= ~EditorInfo.IME_FLAG_NO_ENTER_ACTION;
}
return connection;
}
In the above, I'm forcing IME_ACTION_DONE too, even though that can be achieved through tedious layout configuration.
Ohhorob's answer is basically correct, but his code is really really redundant! It is basically equivalent to this much simpler version (full code for lazy readers):
package com.example.views;
import android.content.Context;
import android.util.AttributeSet;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.widget.EditText;
// An EditText that lets you use actions ("Done", "Go", etc.) on multi-line edits.
public class ActionEditText extends EditText
{
public ActionEditText(Context context)
{
super(context);
}
public ActionEditText(Context context, AttributeSet attrs)
{
super(context, attrs);
}
public ActionEditText(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
}
#Override
public InputConnection onCreateInputConnection(EditorInfo outAttrs)
{
InputConnection conn = super.onCreateInputConnection(outAttrs);
outAttrs.imeOptions &= ~EditorInfo.IME_FLAG_NO_ENTER_ACTION;
return conn;
}
}
Note that some inputType options such as textShortMessage make this not work! I suggest you start with inputType="text". Here is how you could use it in your XML.
<com.example.views.ActionEditText
android:id=...
android:layout_stuff=...
android:imeOptions="actionDone"
android:inputType="textAutoCorrect|textCapSentences|textMultiLine"
android:maxLines="3" />
An alternative solution to subclassing the EditText class is to configure your EditText instance with this:
editText.setHorizontallyScrolling(false);
editText.setMaxLines(Integer.MAX_VALUE);
At least, this works for me on Android 4.0. It configures the EditText instance so that the user edits a single-line string that is displayed with soft-wrapping on multiple lines, even if an IME action is set.
Following previous answer
public class MultiLineText extends EditText {
public MultiLineText(Context context) {
super(context);
}
public MultiLineText(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MultiLineText(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
InputConnection connection = super.onCreateInputConnection(outAttrs);
int imeActions = outAttrs.imeOptions&EditorInfo.IME_MASK_ACTION;
if ((imeActions&EditorInfo.IME_ACTION_DONE) != 0) {
// clear the existing action
outAttrs.imeOptions ^= imeActions;
// set the DONE action
outAttrs.imeOptions |= EditorInfo.IME_ACTION_DONE;
}
if ((outAttrs.imeOptions&EditorInfo.IME_FLAG_NO_ENTER_ACTION) != 0) {
outAttrs.imeOptions &= ~EditorInfo.IME_FLAG_NO_ENTER_ACTION;
}
return connection;
}
}
Use this like
<myapp.commun.MultiLineText
android:id="#+id/textNotes"
android:layout_height="wrap_content"
android:minHeight="100dp"
android:layout_width="wrap_content"
android:hint="Notes"
android:textSize="20sp"
android:padding="7dp"
android:maxLines="4"/>
for put the action Done, you could use:
XML
android:inputType="text|textCapSentences"
JAVA
editText.setHorizontallyScrolling(false);
editText.setMaxLines(Integer.MAX_VALUE);
I hope its work for you.
Apparently the answer to the original question is Yes but I believe the Android team are trying to make developers think a little bit about how they use the multi-line EditText. They want the enter key to add newlines and probably expect that you provide a button or another input means to raise the event that you are done editing.
I have the same issue and my obvious solution was simply to add a done button and let the enter button add the newlines.
Use these attribute in your XML.
android:inputType="textImeMultiLine"
android:imeOptions="actionDone"
Related
I've created a SettingsActivity with the template and put an EditTextPreference in my root_preferences.xml. It should contain a password, so I edited it like this:
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:background="#color/colorBackground">
<EditTextPreference
android:id="#+id/etPassword"
android:dialogTitle="Passwort"
android:inputType="textPassword"
android:key="pref_password"
android:selectAllOnFocus="true"
android:singleLine="true"
android:title="Passwort" />
My problem is that neither inputType, singleLine nor setAllOnFocus is working. Do you know what's the problem?
In my case, InputType doesn't hide the password on input and also in the summary.
Here's how I got around the problem
XML file
<PreferenceScreen
xmlns:app="http://schemas.android.com/apk/res-auto">
<EditTextPreference
app:key="pref_password"
app:title="Password"
app:dialogTitle="Set password"
app:useSimpleSummaryProvider="true"
/>
</PreferenceScreen>
In the PreferenceFragmentCompat set in your XML, find your EditTextPreference in the onCreatePreferences section and add an OnBindEditTextListener on it.
public static class YourFragment extends PreferenceFragmentCompat {
#Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
// Find the password EditText
EditTextPreference etpPassword = getPreferenceManager().findPreference("pref_password");
etpPassword.setOnBindEditTextListener(new EditTextPreference.OnBindEditTextListener() {
#Override
public void onBindEditText(#NonNull EditText editText) {
// Set keyboard layout and some behaviours of the field
editText.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
// Replace -> android:singleLine="true"
// Not needed for password field, or set it before setTransformationMethod
// otherwise the password will not be hidden
//editText.setSingleLine(true);
// Replace -> android:inputType="textPassword"
// For hiding text
editText.setTransformationMethod(PasswordTransformationMethod.getInstance());
// Replace -> android:selectAllOnFocus="true"
// On password field, you cannot make a partial selection with .setSelection(start, stop)
editText.selectAll();
// Replace -> android:maxLength="99"
editText.setFilters(new InputFilter[]{new InputFilter.LengthFilter(99)});
}
});
}
}
You also can create your own EditTextPreference class and set other things.
public class YourEditTextPreference extends EditTextPreference {
// Add some preferences, which can be used later for checking
private Integer mPasswordMinSize = 6;
public EditTextPreferencePassword(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init();
}
public EditTextPreferencePassword(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
public EditTextPreferencePassword(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public EditTextPreferencePassword(Context context) {
super(context);
init();
}
private void init(){
// Set Dialog button text
this.setNegativeButtonText("RETURN");
this.setPositiveButtonText("CHECK");
this.setOnBindEditTextListener(new OnBindEditTextListener() {
#Override
public void onBindEditText(#NonNull EditText editText) {
// Put field parameters here
}
});
}
public void setMinSize(int minSize) { mPasswordMinSize = minSize; }
public Integer getMinSize(){ return mPasswordMinSize; }
// Hide password by stars
#Override
public CharSequence getSummary() {
return getText().equals("") ? super.getSummary() : "*******";
}
}
And in your XML, change <EditTextPreference to <complet.path.to.YourEditTextPreference
As pointed out in a previous answer, inputType is not understood by EditTextPreference and it seems it's not able to pass it to the underlying EditText object by itself, as one would expect.
Adding to the problem, there seems to be a lack of consistency depending on which libraries you are using. The accepted answer doesn't seem to work with AndroidX because there's no such getEditText() method in that EditTextPreference implementation. There's a way, though, by adding a listener that's exactly there for that (Kotlin example):
val myPref = findPreference<EditTextPreference>(
"my_pref_key"
)
myPref?.setOnBindEditTextListener {
it.inputType = InputType.TYPE_CLASS_NUMBER
}
Finally does the trick, but I have wasted a good hour trying to figure out a solution for this, mostly reading outdated questions and answers on the matter.
I see it's been awhile but setting this inside the SettingsFragment onViewCreated() worked for me...
val pw = preferenceScreen.preferenceManager.findPreference<EditTextPreference>("password")
pw?.setOnBindEditTextListener {
it.inputType = InputType.TYPE_CLASS_TEXT + InputType.TYPE_TEXT_VARIATION_PASSWORD
}
You can't do it from XML, but EditTextpreference exposes the EditText so you can do it programmatically. After you load the preferences in your Activity/Fragment, you can do:
EditTextPreference pref = (EditTextPreference)
PreferenceManager.findPreference("edit");
EditText prefEditText = pref.getEditText();
prefEditText.setInputType(InputType.TYPE_CLASS_TEXT); // set properties here
prefEditText.setSingleLine(true);
to speed up the development of an App I created this editText with a label attached.
This is the class:
public class EditTextWithLabel extends LinearLayout {
#InjectView(R.id.text_edittext_with_label)
protected TextView label;
#InjectView(R.id.edittext_edittext_with_label)
protected EditText editText;
public EditTextWithLabel(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
getAttributes(context, attrs);
}
...
private void init(Context context) {
LayoutInflater.from(context).inflate(R.layout.layout_edittext_with_label, this, true);
ButterKnife.inject(this);
setOrientation(VERTICAL);
}
private void getAttributes(Context context, AttributeSet attrs) {
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.EditTextWithLabel, 0, 0);
try {
CharSequence label = a.getText(R.styleable.EditTextWithLabel_label);
if (!TextUtils.isEmpty(label))
setLabel(label);
CharSequence text = a.getText(R.styleable.EditTextWithLabel_android_text);
if (!TextUtils.isEmpty(text))
setText(text);
CharSequence hint = a.getText(R.styleable.EditTextWithLabel_android_hint);
if (!TextUtils.isEmpty(hint))
setHint(hint);
int maxLength = a.getInt(R.styleable.EditTextWithLabel_android_maxLength, -1);
if (maxLength > 0)
setMaxLength(maxLength);
int type = a.getInt(R.styleable.EditTextWithLabel_android_inputType, InputType.TYPE_CLASS_TEXT);
setInputType(type);
} finally {
a.recycle();
}
}
...
}
And this is xml:
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<TextView
android:id="#+id/text_edittext_with_label"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="#dimen/edit_text_radius"
android:paddingBottom="5dp"
android:text="#string/username"
android:textColor="#color/text"
android:textSize="#dimen/text_edit_text"
/>
<EditText
android:id="#+id/edittext_edittext_with_label"
style="#style/EditText"
android:inputType="textEmailAddress"/>
</merge>
I found out that if I put more of these in a fragment, when I restore it ALL the editTexts show the text that is wrote in the last one.
I cannot explain this behavior, so I hope that somebody could enlight me.
Thank you
EDIT
Thanks to J. Dow answer I was able to solve the issue, I've added at the end of the init method this code:
label.setId((int) System.currentTimeMillis());
editText.setId((int) System.currentTimeMillis());
This randomized the ids enough to avoid the issue.
What merge is doing is basically a simple include. So your final Layout will include multiple "copies" of your EditText.
From the Android documentation:
Note: In order for the Android system to restore the state of the views in our activity, each view must have a unique ID, supplied by the android:id attribute.
http://developer.android.com/training/basics/activity-lifecycle/recreating.html
Thus, when restoring your merged layout, the Android system will encounter multiple EditTexts with the same id and therefore restore each of them with the same state.
I have a fragment within a TabHost that has multiple text fields in it. The virtual keyboard works just fine to enter text into using inputType set, but the hardware keyboard (on Droid, Droid 2, etc) does not work.
From my testing as soon as you start typing on the hardware keyboard, the EditText loses focus and the "typing" seems to go elsewhere in the application. I have tried both configurations below:
<EditText
android:id="#+id/editTextPlusFat"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="0.15"
android:background="#drawable/textfield_default_holo_light"
android:digits="0123456789."
android:ems="10"
android:hint="#string/str_CalcHintFat"
android:inputType="number" >
AND
<EditText
android:id="#+id/editTextPlusFat"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="0.15"
android:background="#drawable/textfield_default_holo_light"
android:ems="10"
android:hint="#string/str_CalcHintFat"
android:inputType="numberDecimal" >
Does anyone have any ideas why this happens? Thank you.
My solution was to add onTouchListener() to all EditTexts in each Fragment - see below.
OnTouchListener foucsHandler = new OnTouchListener() {
#Override
public boolean onTouch(View arg0, MotionEvent event) {
// TODO Auto-generated method stub
arg0.requestFocusFromTouch();
return false;
}
};
currentActivity.findViewById(R.id.editTextPlusServings).setOnTouchListener(foucsHandler);
currentActivity.findViewById(R.id.editTextPlusFoodName).setOnTouchListener(foucsHandler);
As in the duplicate question, the better answer is to remove the focus switching by overriding onTouchModeChanged() from TabHost.
Add a new class extending TabHost:
package net.lp.collectionista.ui.views;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.TabHost;
public class BugFixedTabHost extends TabHost {
public BugFixedTabHost(Context context, AttributeSet attrs) {
super(context, attrs);
}
public BugFixedTabHost(Context context) {
super(context);
}
#Override
public void onTouchModeChanged(boolean isInTouchMode) {
// leave it empty here. It looks that when you use hard keyboard,
// this method would have be called and the focus will be taken.
}
}
In your Fragment (or Activity) replace the TabHost type with BugFixedTabHost.
Finally, assuming you use TabHost in layout xmls too, change it to your custom view (full package name):
<net.lp.collectionista.ui.views.BugFixedTabHost
android:id="#android:id/tabhost" ...
I'm not sure why this did not work for #mattdonders, but this is the right way to go. And it is cheaper than attaching listeners to every EditText. By the way, have we figured out yet why mCurrentView.hasFocus() is False or so?
I would like to be able to assign a xml attribute or style to a TextView that will make whatever text it has in ALL CAPITAL LETTERS.
The attributes android:inputType="textCapCharacters" and android:capitalize="characters" do nothing and look like they are for user inputed text, not a TextView.
I would like to do this so I can separate the style from the content. I know I could do this programmically but again I want keep style out of the content and the code.
I though that was a pretty reasonable request but it looks like you cant do it at this time. What a Total Failure. lol
Update
You can now use
textAllCaps
to force all caps.
What about android:textAllCaps?
By using AppCompat textAllCaps in Android Apps supporting older API's (less than 14)
There is one UI widgets that ships with AppCompat named CompatTextView is a Custom TextView extension that adds support for textAllCaps
For newer android API > 14 you can use :
android:textAllCaps="true"
A simple example:
<android.support.v7.internal.widget.CompatTextView
android:id="#+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:textAllCaps="true"/>
Source:developer.android
Update:
As it so happens CompatTextView was replaced by AppCompatTextView in
latest appcompat-v7 library ~ Eugen Pechanec
It is really very disappointing that you can't do it with styles (<item name="android:textAllCaps">true</item>) or on each XML layout file with the textAllCaps attribute, and the only way to do it is actually using theString.toUpperCase() on each of the strings when you do a textViewXXX.setText(theString).
In my case, I did not wanted to have theString.toUpperCase() everywhere in my code but to have a centralized place to do it because I had some Activities and lists items layouts with TextViews that where supposed to be capitalized all the time (a title) and other who did not... so... some people may think is an overkill, but I created my own CapitalizedTextView class extending android.widget.TextView and overrode the setText method capitalizing the text on the fly.
At least, if the design changes or I need to remove the capitalized text in future versions, I just need to change to normal TextView in the layout files.
Now, take in consideration that I did this because the App's Designer actually wanted this text (the titles) in CAPS all over the App no matter the original content capitalization, and also I had other normal TextViews where the capitalization came with the the actual content.
This is the class:
package com.realactionsoft.android.widget;
import android.content.Context;
import android.util.AttributeSet;
import android.view.ViewTreeObserver;
import android.widget.TextView;
public class CapitalizedTextView extends TextView implements ViewTreeObserver.OnPreDrawListener {
public CapitalizedTextView(Context context) {
super(context);
}
public CapitalizedTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CapitalizedTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
public void setText(CharSequence text, BufferType type) {
super.setText(text.toString().toUpperCase(), type);
}
}
And whenever you need to use it, just declare it with all the package in the XML layout:
<com.realactionsoft.android.widget.CapitalizedTextView
android:id="#+id/text_view_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
Some will argue that the correct way to style text on a TextView is to use a SpannableString, but I think that would be even a greater overkill, not to mention more resource-consuming because you'll be instantiating another class than TextView.
I've come up with a solution which is similar with RacZo's in the fact that I've also created a subclass of TextView which handles making the text upper-case.
The difference is that instead of overriding one of the setText() methods, I've used a similar approach to what the TextView actually does on API 14+ (which is in my point of view a cleaner solution).
If you look into the source, you'll see the implementation of setAllCaps():
public void setAllCaps(boolean allCaps) {
if (allCaps) {
setTransformationMethod(new AllCapsTransformationMethod(getContext()));
} else {
setTransformationMethod(null);
}
}
The AllCapsTransformationMethod class is not (currently) public, but still, the source is also available. I've simplified that class a bit (removed the setLengthChangesAllowed() method), so the complete solution is this:
public class UpperCaseTextView extends TextView {
public UpperCaseTextView(Context context) {
super(context);
setTransformationMethod(upperCaseTransformation);
}
public UpperCaseTextView(Context context, AttributeSet attrs) {
super(context, attrs);
setTransformationMethod(upperCaseTransformation);
}
public UpperCaseTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
setTransformationMethod(upperCaseTransformation);
}
private final TransformationMethod upperCaseTransformation =
new TransformationMethod() {
private final Locale locale = getResources().getConfiguration().locale;
#Override
public CharSequence getTransformation(CharSequence source, View view) {
return source != null ? source.toString().toUpperCase(locale) : null;
}
#Override
public void onFocusChanged(View view, CharSequence sourceText,
boolean focused, int direction, Rect previouslyFocusedRect) {}
};
}
Basically, write this in TextView of XML file:
android:textAllCaps="true"
It seems like there is permission on mobile keypad setting, so the easiest way to do this is:
editText.setFilters(new InputFilter[]{new InputFilter.AllCaps()});
hope this will work
PixlUI project allows you to use textAllCaps in any textview or subclass of textview including:
Button,
EditText
AutoCompleteEditText
Checkbox
RadioButton
and several others.
You will need to create your textviews using the pixlui version rather than the ones from the android source, meaning you have to do this:
<com.neopixl.pixlui.components.textview.TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/hello_world"
pixlui:textAllCaps="true" />
PixlUI also allows you to set a custom typeface/font which you put in your assets folder.
I'm working on a Gradle fork of the PixlUI framework which uses gradle and allows one to specify textAllCaps as well as the typeface from styles rather than requiring them inline as the original project does.
When an EditText is in password mode, it seems that the hint is shown in a different font (courrier?). How can I avoid this? I would like the hint to appear in the same font that when the EditText is not in password mode.
My current xml:
<EditText
android:hint="#string/edt_password_hint"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:password="true"
android:singleLine="true" />
Changing the typeface in xml didn't work on the hint text for me either. I found two different solutions, the second of which has better behavior for me:
Remove android:inputType="textPassword" from your xml file and instead, in set it in java:
EditText password = (EditText) findViewById(R.id.password_text);
password.setTransformationMethod(new PasswordTransformationMethod());
With this approach, the hint font looks good but as you're typing in that edit field, you don't see each character in plain text before it turns into a password dot. Also when making input in fullscreen, the dots will not appear, but the password in clear text.
Leave android:inputType="textPassword" in your xml. In Java, ALSO set the typeface and passwordMethod:
EditText password = (EditText) findViewById(R.id.register_password_text);
password.setTypeface(Typeface.DEFAULT);
password.setTransformationMethod(new PasswordTransformationMethod());
This approach gave me the hint font I wanted AND gives me the behavior I want with the password dots.
I found this useful tip from Dialogs Guide
Tip: By default, when you set an EditText element to use the "textPassword" input type, the font family is set to monospace, so you should change its font family to "sans-serif" so that both text fields use a matching font style.
For example
android:fontFamily="sans-serif"
This is what I did to fix this problem. For some reason I didn't have to set the transformation method so this may be a better solution:
In my xml:
<EditText
android:id="#+id/password_edit_field"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="Password"
android:inputType="textPassword" />
In my Activity:
EditText password = (EditText) findViewById( R.id.password_edit_field );
password.setTypeface( Typeface.DEFAULT );
The setTransformationMethod approach breaks android:imeOption for me, and allows carriage returns to be typed into the password field. Instead I'm doing this:
setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
setTypeface(Typeface.DEFAULT);
And am not setting android:password="true" in XML.
The answer manisha provided does work, but it leaves the password field in a nonstandard state compared to the default. That is, the default fontface then applies also to the password field, including both the dot replacements and the preview characters that appears before being replaced with the dots (as well as when it is a "visible password" field).
To fix this and make it 1) look and act exactly like the default textPassword input type, but also 2) allow the hint text to appear in a default (non-monospace) font, you need to have a TextWatcher on the field that can toggle the fontface properly back and forth between Typeface.DEFAULT and Typeface.MONOSPACE based on whether it is empty or not. I created a helper class that can be used to accomplish that:
import android.graphics.Typeface;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.inputmethod.EditorInfo;
import android.widget.TextView;
/**
* This class watches the text input in a password field in order to toggle the field's font so that the hint text
* appears in a normal font and the password appears as monospace.
*
* <p />
* Works around an issue with the Hint typeface.
*
* #author jhansche
* #see <a
* href="http://stackoverflow.com/questions/3406534/password-hint-font-in-android">http://stackoverflow.com/questions/3406534/password-hint-font-in-android</a>
*/
public class PasswordFontfaceWatcher implements TextWatcher {
private static final int TEXT_VARIATION_PASSWORD =
(EditorInfo.TYPE_CLASS_TEXT | EditorInfo.TYPE_TEXT_VARIATION_PASSWORD);
private TextView mView;
/**
* Register a new watcher for this {#code TextView} to alter the fontface based on the field's contents.
*
* <p />
* This is only necessary for a textPassword field that has a non-empty hint text. A view not meeting these
* conditions will incur no side effects.
*
* #param view
*/
public static void register(TextView view) {
final CharSequence hint = view.getHint();
final int inputType = view.getInputType();
final boolean isPassword = ((inputType & (EditorInfo.TYPE_MASK_CLASS | EditorInfo.TYPE_MASK_VARIATION))
== TEXT_VARIATION_PASSWORD);
if (isPassword && hint != null && !"".equals(hint)) {
PasswordFontfaceWatcher obj = new PasswordFontfaceWatcher(view);
view.addTextChangedListener(obj);
if (view.length() > 0) {
obj.setMonospaceFont();
} else {
obj.setDefaultFont();
}
}
}
public PasswordFontfaceWatcher(TextView view) {
mView = view;
}
public void onTextChanged(final CharSequence s, final int start, final int before, final int count) {
// Not needed
}
public void beforeTextChanged(final CharSequence s, final int start, final int count, final int after) {
if (s.length() == 0 && after > 0) {
// Input field went from empty to non-empty
setMonospaceFont();
}
}
public void afterTextChanged(final Editable s) {
if (s.length() == 0) {
// Input field went from non-empty to empty
setDefaultFont();
}
}
public void setDefaultFont() {
mView.setTypeface(Typeface.DEFAULT);
}
public void setMonospaceFont() {
mView.setTypeface(Typeface.MONOSPACE);
}
}
Then to make use of it, all you need to do is call the register(View) static method. Everything else is automatic (including skipping the workaround if the view does not require it!):
final EditText txtPassword = (EditText) view.findViewById(R.id.txt_password);
PasswordFontfaceWatcher.register(txtPassword);
There are many way for solving this problem but each way have pros and cons. Here is my testing
I only face this font problem in some device (list at the end of my answer) when enable input password by
edtPassword.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
If I use android:inputType="textPassword", this problem don't happened
Something I have tried
1) Use setTransformationMethod instead inputType
edtPassword.setTransformationMethod(PasswordTransformationMethod.getInstance());
Font will working well
Keyboard display not very well (it only display text, don't display number on top of text)
2) Use Typeface.DEFAULT
setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
setTypeface(Typeface.DEFAULT);
Keyboard display well,
Font may working not well. Example sans-serif-light is a default font for all View in my application => after setTypeface(Typeface.DEFAULT), the EditText font still look different in some device
3) Use android:fontFamily="sans-serif"
For some device, it will CRASH, check my answer here https://stackoverflow.com/a/52421199/5381331. And also font still look different
MY SOLUTION
cache the typeface before setInputType then reuse it
Typeface cache = edtPassword.getTypeface();
edtPassword.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
edtPassword.setTypeface(cache);
Testing
Some device face font problem
Xiaomi A2 (8.0.1)
Pixel XL (8.1.0)
Sony Xperia Z5 Au (SOV32) (6.0)
Arrow NX (F-04G) (6.0.1)
Kyocera (S2) (7.0)
Some device not face font problem
Samsung S4 (SC-04E) (5.0.1)
Samsung Galaxy Node 5 (5.1.1)
Samsung S7 Edge (SM-G935F) (7.0)
The other answers are the right solution for most cases.
However, if you're using a custom EditText subclass to, say, apply a custom font by default, there's a subtle issue. If you set the custom font in the constructor of your subclass, it will still be overwritten by the system if you set inputType="textPassword".
In this case, move your styling to onAttachedToWindow after your super.onAttachedToWindow call.
Example implementation:
package net.petosky.android.ui;
import android.content.Context;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.widget.EditText;
/**
* An EditText that applies a custom font.
*
* #author cory#petosky.net
*/
public class EditTextWithCustomFont extends EditText {
private static Typeface customTypeface;
public EditTextWithCustomFont(Context context) {
super(context);
}
public EditTextWithCustomFont(Context context, AttributeSet attrs) {
super(context, attrs);
}
public EditTextWithCustomFont(
Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
/**
* Load and store the custom typeface for this app.
*
* You should have a font file in: project-root/assets/fonts/
*/
private static Typeface getTypeface(Context context) {
if (customTypeface == null) {
customTypeface = Typeface.createFromAsset(
context.getAssets(), "fonts/my_font.ttf");
}
return customTypeface;
}
/**
* Set a custom font for our EditText.
*
* We do this in onAttachedToWindow instead of the constructor to support
* password input types. Internally in TextView, setting the password
* input type overwrites the specified typeface with the system default
* monospace.
*/
#Override protected void onAttachedToWindow() {
super.onAttachedToWindow();
// Our fonts aren't present in developer tools, like live UI
// preview in AndroidStudio.
if (!isInEditMode()) {
setTypeface(getTypeface(getContext()));
}
}
}
I know this may be the older one but I have humped into something related to this issue when I used InputTypeand app:passwordToggleEnabled="true"together.
So, writing this, as it may help someone over here.
I want to use a custom font to password field along with app:passwordToggleEnabled option for my password input field. But in 27.1.1 (while writing this) support library, it was crashing.
So the code was like below,
<android.support.design.widget.TextInputLayout
android:id="#+id/input_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="#dimen/_10dp"
android:layout_marginTop="#dimen/_32dp"
android:hint="#string/current_password"
android:textColorHint="#color/hint_text_color"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:passwordToggleEnabled="true"
app:passwordToggleTint="#color/black">
<EditText
android:id="#+id/password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="start|left"
android:maxLines="1"
android:textAlignment="viewStart"
android:textColor="#color/black"
android:textColorHint="#color/camel"
android:textSize="#dimen/txt_16sp"
app:font_style="regular"
app:drawableEnd="#drawable/ic_remove_eye" />
</android.support.design.widget.TextInputLayout>
Above code do not have inputType defined in XML
EditText password = (EditText) findViewById(R.id.password);
password.setTransformationMethod(new PasswordTransformationMethod());
And in Java, setTransformationMethod will help me acquire the properties of textPassword input type and also I'm happy my custom font style.
But the below-mentioned crash happened in all API levels with 27.1.1 support library.
java.lang.NullPointerException: Attempt to invoke virtual method 'void
android.support.design.widget.CheckableImageButton.setChecked(boolean)'
on a null object reference
This was crashing due to the onRestoreInstanceState inside TextInputLayout class.
Reproduce Steps: Toggle the password visibility and minimize the app and open from the recent apps. Uh,ho Crashed!
All I needed is default password toggle option (using support library) and custom font in the password input field.
After some time, figured out by doing as below,
<android.support.design.widget.TextInputLayout
android:id="#+id/input_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="#dimen/_10dp"
android:layout_marginTop="#dimen/_32dp"
android:hint="#string/current_password"
android:textColorHint="#color/hint_text_color"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:passwordToggleEnabled="true"
app:passwordToggleTint="#color/black">
<EditText
android:id="#+id/password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="start|left"
android:maxLines="1"
android:textAlignment="viewStart"
android:textColor="#color/black"
android:textColorHint="#color/camel"
android:textSize="#dimen/txt_16sp"
app:font_style="regular"
app:drawableEnd="#drawable/ic_remove_eye"
android:inputType="textPassword" />
</android.support.design.widget.TextInputLayout>
In XML, added android:inputType="textPassword"
TextInputLayout inputPassword = findViewById(R.id.input_password);
EditText password = findViewById(R.id.password);
EditText userName = findViewById(R.id.user_name);
// Get the typeface of user name or other edit text
Typeface typeface = userName.getTypeface();
if (typeface != null)
inputLayout.setTypeface(typeface); // set to password text input layout
In above java code,
I acquired the custom typeface from username EditText and applied it to TextInputLayout of the password field. Now you don't need to set the typeface explicitly to the password EditText as it will acquire the TextInputLayout property.
Also, I removed password.setTransformationMethod(new PasswordTransformationMethod());
By doing this way, passwordToggleEnabled is working, the custom font is also applied and bye-bye to the crash. Hope this issue will be fixed in upcoming support releases.
You can also use a custom Widget. It's very simple and it doesn't clutter your Activity/Fragment code.
Here's the code:
public class PasswordEditText extends EditText {
public PasswordEditText(Context context) {
super(context);
init();
}
public PasswordEditText(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public PasswordEditText(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init() {
setTypeface(Typeface.DEFAULT);
}
}
And your XML will look like this:
<com.sample.PasswordEditText
android:id="#+id/password_edit_field"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="Password"
android:inputType="textPassword"
android:password="true" />
use the calligraphy library.
then it still wont update the password fields with the right font. so do this in code not in xml:
Typeface typeface_temp = editText.getTypeface();
editText.setInputType(inputType); /*whatever inputType you want like "TYPE_TEXT_FLAG_NO_SUGGESTIONS"*/
//font is now messed up ..set it back with the below call
editText.setTypeface(typeface_temp);
I recently added the ability to change toggle monospace on/off to a extension of EditText specifically for passwords it may help some people. It doesn't use android:fontFamily so is compatible <16.
You can also use the
<android.support.design.widget.TextInputLayout/>
together with
<android.support.v7.widget.AppCompatEditText/>
I use this solution to toggle the Typeface depending on hint visibility. It's similar to Joe's answer, but extending EditText instead:
public class PasswordEditText extends android.support.v7.widget.AppCompatEditText {
public PasswordEditText(Context context) {
super(context);
}
public PasswordEditText(Context context, AttributeSet attrs) {
super(context, attrs);
}
public PasswordEditText(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
super.onTextChanged(text, start, lengthBefore, lengthAfter);
if (text.length() > 0) setTypeface(Typeface.MONOSPACE);
else setTypeface(Typeface.DEFAULT);
}
}
In case you are using the calligraphy library in combination with a TextInputLayout and an EditText, the following code works well.
EditText password = (EditText) findViewById(R.id.password);
TextInputLayout passwordLayout = (TextInputLayout) findViewById(R.id.passwordLayout);
Typeface typeface_temp = password.getTypeface();
password.setInputType(InputType.TYPE_CLASS_TEXT |
InputType.TYPE_TEXT_VARIATION_PASSWORD);
password.setTypeface(typeface_temp);
passwordLayout.setTypeface(typeface_temp);
A weird case perhaps, but I have experimented with this and found out that:
password.setInputType(InputType.TYPE_TEXT_VARIATION_PASSWORD);
password.setTransformationMethod(new PasswordTransformationMethod());
changed the size of the font of the hint instead of the font itself! This is still an undesired effect. Strangely enough, the reverse operation:
password.setTransformationMethod(new PasswordTransformationMethod());
password.setInputType(InputType.TYPE_TEXT_VARIATION_PASSWORD);
Keeps the same font size.
I found a sure solution to this problem
Best way to Hello ,I found a sure solution to this problem
The best way is to create a custom editText and save value of typeface as a temp and then apply the method to the InputType changes , Finally, we set back the value of temp type face to the editText. like so :
public class AppCompatPasswordEditText extends AppCompatEditText {
public AppCompatPasswordEditText(Context context) {
super(context);
}
public AppCompatPasswordEditText(Context context, AttributeSet attrs) {
super(context, attrs);
}
public AppCompatPasswordEditText(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
// Our fonts aren't present in developer tools, like live UI
// preview in AndroidStudio.
Typeface cache = getTypeface();
if (!isInEditMode() && cache != null) {
setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
setTypeface(cache);
}
}
}
This how to make input password that has hint which not converted to * and default typeface !!.
On XML :
android:inputType="textPassword"
android:gravity="center"
android:ellipsize="start"
android:hint="Input Password !."
On Activity :
inputPassword.setTypeface(Typeface.DEFAULT);
thanks to : mango and rjrjr for the insight :D.
like the above but make sure the fields do not have the bold style in xml as they will never look the same even with the above fix!