This question already has answers here:
android - text input type person name isn't working
(4 answers)
Closed 7 years ago.
I need to automatic capitalise both first letters in compound names, for example: Gay-Lussac (French physicist), Иванов-Вано (Russian animation director).
Android EditText capitalises only first letter in whole name, and I get Gay-lussac and Иванов-вано.
Is there any method?
My EditText is
<EditText
android:id="#+id/nameET"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="31dp"
android:hint="#string/hint_name"
android:imeOptions="actionNext"
android:inputType="textCapWords|textCapSentences|textPersonName"
android:singleLine="true" />
Also I tried (also it does not work for second part):
<EditText
...
android:capitalize="words"
android:singleLine="true" />
and
<EditText
...
android:capitalize="words"
android:inputType="textCapWords|textCapSentences"
android:singleLine="true" />
You could operate in 2 steps:
1 - Have your strings spaced (ie.: "Gay - Lussac") in strings.xml, and let Android capitalize them.
2 - Then replace " - " with "-", in Java.
[EDIT]
If the input is from the user, then replace step 1 step:
1 - First, replace the user input string: "-" with " - "
You should create a Class that contains a static method like this:
public static CharSequence capitalize(CharSequence name){
if(name == null) return null;
String sName = name.toString().toLowerCase().trim();
String result = "";
sName = capFirst(sName);
if(sName.contains("-")){
result = "";
String[] subParts = sName.split("-");
for(int i = 0; i < subParts.length; i++){
String subPart = subParts[i];
result += capFirst(subPart);
if(i < subParts.length - 1) result += "-";
}
}
else result = sName;
return result.trim();
}
private static String capFirst(String text){
if(text == null) return null;
text = text.toLowerCase().trim();
String result = "";
if(text.length() > 0){
text = text.substring(0, 1).toUpperCase() + text.substring(1);
if(text.contains(" ")){
for(String word : text.split(" ")){
word = word.trim();
if(word.length() > 0) result += " " + word.substring(0, 1).toUpperCase() + word.substring(1);
}
}
else result = text;
}
return result.trim();
}
Then add a listener to the apropriate event of your EditText and set it´s text using YourClass.capitalize().
I made my own capitalisation.
It capitalise last entered character if this character is last character in string and it is located after any non-letter character (or it is single character in string).
public class EditName extends EditText {
public EditName(Context context, AttributeSet attrs) {
super(context, attrs);
addTextChangedListener(new TextWatcher() {
String lastChanged = "";
boolean isLastSingleCharChanged = false;
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// check if only last character was changed
isLastSingleCharChanged =
count - before == 1 // was added one character
&& start + count == s.length() // last character was in changed group
&& (start == 0 || count == 1); // or total change, or last character
}
#Override
public void afterTextChanged(Editable s) {
if (!isLastSingleCharChanged) return; // only if last character changed (see above)
int len = s.length();
if (len == 0) return;
if (len > 1 && Character.isLetter(s.charAt(len - 2))) return; // correct letter only after none-letter
char c = s.charAt(len - 1);
if (!Character.isLetter(c)) return;
if (!Character.isLowerCase(c)) return;
String text = s.toString();
if (text.equals(lastChanged)) return; // do not do the same correction twice
lastChanged = text;
EditName.this.setText(text.substring(0, text.length() - 1)
+ Character.toUpperCase(c));
EditName.this.setSelection(text.length()); // position cursor
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
});
}
}
Related
In android edit text, how to separate 10 digit number input by space? I am using android text watcher and I am trying to input multiple 10 digit numbers in the field. The issue arises when multiple numbers are copied and pasted in the field and that time, it doesn't take those spaces. Kindly let me know a solution in order to allow multiple number input with a space after every 10 digit number, when the number is copied from other place.
This will work for both type and copy/paste from other place.
yourEditText.addTextChangedListener(new TextWatcher() {
private static final char space = ' ';
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
#Override
public void afterTextChanged(Editable s) {
int pos = 0;
while (true) {
if (pos >= s.length()) break;
if (space == s.charAt(pos) && (((pos + 1) % 11) != 0 || pos + 1 == s.length())) {
s.delete(pos, pos + 1);
} else {
pos++;
}
}
pos = 10;
while (true) {
if (pos >= s.length()) break;
final char c = s.charAt(pos);
if (Character.isDigit(c)) {
s.insert(pos, "" + space);
}
pos += 11;
}
}
});
Edit and Use the following code as per your needs
StringBuilder s;
s = new StringBuilder(yourTxtView.getText().toString());
for(int i = 10; i < s.length(); i += 10){
s.insert(i, " "); // this line inserts a space
}
yourTxtView.setText(s.toString());
and when you need to get the String without spaces do this:
String str = yourTxtView.getText().toString().replace(" ", "");
i have simple Edittext and when I'm going to change input letters in im setting listener new textWatcher and it's onTextChanged() method like:
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
Log.d("qwer", "onTextChanged: " + s + " " + start + " " + before + " " + count);
String originalText = s.toString();
int originalTextLength = originalText.length();
int currentSelection = textHeading.getSelectionStart();
StringBuilder sb = new StringBuilder();
boolean hasChanged = false;
for (int i = 0; i < originalTextLength; i++) {
char currentChar = originalText.charAt(i);
if (isAllowed(currentChar) && i < 21) {
sb.append(currentChar);
} else {
hasChanged = true;
textHeading.setError("Please insert current letters");
}
}
if (hasChanged) {
String newText = sb.toString();
textHeading.setText(capitalize(newText));
textHeading.setSelection(currentSelection);
}
}
endless cycle begins when i'm setting validated data back to the edittext becouse it calls method ontextCahnged() again. so my goal is dynamically change input letters and i have to capitalize it. I know there is more the easiest way to do it. but i need to do by this way.
You problem is with TextWatcher not with the logic what you are writing. Below Code block is causing the issue (endless cycle begins when i'm setting validated data back to the edittext becouse it calls method ontextCahnged() again)
if (hasChanged) {
String newText = sb.toString();
textHeading.setText(capitalize(newText)); // <<<<<< This line is culprit which is calling Watcher's method again and again.
textHeading.setSelection(currentSelection);
}
To handle this issue, you need to do below steps
Remove Watcher from EditText
Set text
Add Watcher to EditText.
For more information read How can I change the EditText text without triggering the Text Watcher?
There is many ways to do that:
1- Using common Utils
Library: http://commons.apache.org/proper/commons-lang/
StringUtils.capitalize(..)
2- By Custom method
public static String upperCaseFirst(String value) {
// Convert String to char array.
char[] array = value.toCharArray();
// Modify first element in array.
array[0] = Character.toUpperCase(array[0]);
// Return string.
return new String(array);
}
3- From apache Common
Library: http://commons.apache.org/proper/commons-lang/
WordUtils.capitalize(java.lang.String)
Now you can assign that string to your input box.
You can set the input type (of EditText) to TYPE_CLASS_TEXT | TYPE_TEXT_FLAG_CAP_CHARACTERS.
OR
Set android:inputType="textCapSentences" on your EditText.
You can follow this link https://developer.android.com/reference/android/text/InputFilter.AllCaps
You can use following InputFilter.AllCaps
or this
android:inputType="textCapCharacters"
Why not use a flag ? I've modified your code by adding a boolean setManually flag.
boolean setManually = false;
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
Log.d("qwer", "onTextChanged: " + s + " " + start + " " + before + " " + count);
if (setManually) {
setManually = false;
return;
}
String originalText = s.toString();
int originalTextLength = originalText.length();
int currentSelection = textHeading.getSelectionStart();
StringBuilder sb = new StringBuilder();
boolean hasChanged = false;
for (int i = 0; i < originalTextLength; i++) {
char currentChar = originalText.charAt(i);
if (isAllowed(currentChar) && i < 21) {
sb.append(currentChar);
} else {
hasChanged = true;
textHeading.setError("Please insert current letters");
}
}
if (hasChanged) {
String newText = sb.toString();
setManually = true;
textHeading.setText(capitalize(newText));
textHeading.setSelection(currentSelection);
}
}
You can just simply use in XML
android:inputType="textCapCharacters"
no need to write code for capitlize letter.
Try like this:
String originalText = s.toString().toUpperCase();
or
if (hasChanged) {
String newText = sb.toString().toUpperCase();
textHeading.setText(newText);
textHeading.setSelection(currentSelection);
}
There are many answers out there for the same task I want to accomplish and even I couldn't find any solutions useful in my case
I've seen
Question 1 -- The issue was it is adding commas counting "." (DOT) also as numeric value.
Question 2 -- The issue was it is not showing It's result while text is being changed
Question 3 -- The issue was It is adding 0 continuously after 15th letter.
And many more questions and answers are out there,
But I didn't find anything that meets general requirements for thousand separator.
Is there any useful Tips that helps me adding the thousand separator smoothly as text changed and treats differently for
numbers after decimal like as calculator ?
I will appreciate any kind of helps.
I was having the same issue and researched much for the task To get the proper result. So I finally solved the issueand I am providing you my code that'll definitely help you out.
You can directly copy my codes in your own class, It's fully Tested
Fetures of the following codes
Puts thousand separator as text changes inside EditText.
adds 0. Automatically when pressed period (.) At First.
Ignores 0 input at Beginning.
Class Name : NumberTextWatcherForThousand which implements TextWatcher
import android.text.Editable;
import android.text.TextWatcher;
import android.widget.EditText;
import java.util.StringTokenizer;
/**
* Created by Shreekrishna on 12/14/2014.
*/
public class NumberTextWatcherForThousand implements TextWatcher {
EditText editText;
public NumberTextWatcherForThousand(EditText editText) {
this.editText = editText;
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
#Override
public void afterTextChanged(Editable s) {
try
{
editText.removeTextChangedListener(this);
String value = editText.getText().toString();
if (value != null && !value.equals(""))
{
if(value.startsWith(".")){
editText.setText("0.");
}
if(value.startsWith("0") && !value.startsWith("0.")){
editText.setText("");
}
String str = editText.getText().toString().replaceAll(",", "");
if (!value.equals(""))
editText.setText(getDecimalFormattedString(str));
editText.setSelection(editText.getText().toString().length());
}
editText.addTextChangedListener(this);
return;
}
catch (Exception ex)
{
ex.printStackTrace();
editText.addTextChangedListener(this);
}
}
public static String getDecimalFormattedString(String value)
{
StringTokenizer lst = new StringTokenizer(value, ".");
String str1 = value;
String str2 = "";
if (lst.countTokens() > 1)
{
str1 = lst.nextToken();
str2 = lst.nextToken();
}
String str3 = "";
int i = 0;
int j = -1 + str1.length();
if (str1.charAt( -1 + str1.length()) == '.')
{
j--;
str3 = ".";
}
for (int k = j;; k--)
{
if (k < 0)
{
if (str2.length() > 0)
str3 = str3 + "." + str2;
return str3;
}
if (i == 3)
{
str3 = "," + str3;
i = 0;
}
str3 = str1.charAt(k) + str3;
i++;
}
}
public static String trimCommaOfString(String string) {
// String returnString;
if(string.contains(",")){
return string.replace(",","");}
else {
return string;
}
}
}
Use This Class on your EditText as follows
editText.addTextChangedListener(new NumberTextWatcherForThousand(editText));
To get the input as plain Text Typed as Double
Use the trimCommaOfString method of the same class like this
NumberTextWatcherForThousand.trimCommaOfString(editText.getText().toString());
I am making an android dictionary Vietnam - English (over 20,000 entries).
In my application contain edittext and Listview.
To search item in my listview, i use Binary Search.
But the problem is with the search method and Vietnamese, it doesn't search well
some words it can find, some it can't.
Following is my search code to find word by prefix when edittext change.
public void searchWords(String[] words, String prefix)
{
int first = 0, last = words.length - 1;
int mid = 0;
while (first <= last)
{
mid = (first + last) / 2;
int c = prefix.compareTo(words[mid]);
if (c == 0)
{
first = mid; // first indicates the beginning
break;
}
if (c > 0)
first = mid + 1;
else
last = mid - 1;
}
int i;
for (i = first; i < words.length; i++)
{
if (words[i].startsWith(prefix))
{
pos=i;
break;
}
}
lv.setSelection(pos);
// Toast.makeText(getApplicationContext(), ""+pos,Toast.LENGTH_SHORT).show();
}
and onTextchange code i call my method like this:
public void onTextChanged(CharSequence s, int start, int before, int count){
// TODO Auto-generated method stub
searchWords(w,s.toString());
}
and this is how i load my entries from database to array:
d=handle.retrieve();
if(d.moveToFirst())
{
do
{
w[ii++]=d.getString(1);
}while(d.moveToNext());
}
So, what should i do to make my search working correctly?
Binary Search with "compareTo()" works only with SORTED words (or sorted Strings, i.e. sorted in alphabetic sequence -regardless of language). Meaning: Array starts with A, AA, AAA,... and ends with z, zz, zzz...Btw, UPPER case before lower case. Example:
String[] seq = {"Ape", "Bird", "Donkey", "Eagle", "Fish", "Gnu", "Horse", "Koala"};
String[] ran = {"Gnu", "Koala", "Horse", "Fish", "Bird", "Donkey", "Eagle", "Ape"};
String[] queries = {"Eagle", "Bird", "Donkey", "Fish", "Ape", "Horse", "Eagle", "Gnu"};
// binarySearch with random[] will produce unpredictable results when prefix starts with
// a word that is either after "Bird" or "Ape" or "Gnu"
for (String s : queries) {
System.out.println("Random: BS for " + s + " = " + binarySearch(ran, s));
}
//
// binarySearch with sequence[] will produce correct results whatever prefix is.
for (String s : queries) {
System.out.println("Sequence: BS for " + s + " = " + binarySearch(seq, s));
}
//-------------------------------------------------------------------------
public static int binarySearch(String[] words, String value) {
return binarySearch(words, value, 0, words.length - 1);
}
//
public static int binarySearch(String[] words, String value, int min, int max) {
if (min > max) {
return -1;
}
int mid = (max + min) / 2;
if (words[mid].equals(value)) return mid;
if(words[mid].compareTo(value) > 0)
return binarySearch(words, value, min, mid - 1);
return binarySearch(words, value, mid + 1, max);
}
I am using InputFilter class to make a masked EditText supporting digit grouping. For example when the user inserts" 12345" I want to show "12,345" in EditText. How can I implement it?
This is my incomplete code:
InputFilter IF = new InputFilter() {
#Override
public CharSequence filter(CharSequence source, int start, int end,
Spanned dest, int dstart, int dend) {
for (int i = start; i < end; i++) {
if (!Character.isLetterOrDigit(source.charAt(i))) {
return "";
}
}
if (dest.length() > 0 && dest.length() % 3 == 0)
{
return "," + source;
}
return null;
}
};
edtRadius.setFilters(new InputFilter[] { IF });
Is there any other way to implement this kind of input mask?
This an improvement on the response from #vincent. It adds checks on deleting spaces in a number in the format 1234 5678 9190 so when trying to delete a space it just moves the cursor backon character to the digit before the space. It also keeps the cursor in the same relative place even if spaces are inserted.
mTxtCardNumber.addTextChangedListener(new TextWatcher() {
private boolean spaceDeleted;
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
// check if a space was deleted
CharSequence charDeleted = s.subSequence(start, start + count);
spaceDeleted = " ".equals(charDeleted.toString());
}
public void afterTextChanged(Editable editable) {
// disable text watcher
mTxtCardNumber.removeTextChangedListener(this);
// record cursor position as setting the text in the textview
// places the cursor at the end
int cursorPosition = mTxtCardNumber.getSelectionStart();
String withSpaces = formatText(editable);
mTxtCardNumber.setText(withSpaces);
// set the cursor at the last position + the spaces added since the
// space are always added before the cursor
mTxtCardNumber.setSelection(cursorPosition + (withSpaces.length() - editable.length()));
// if a space was deleted also deleted just move the cursor
// before the space
if (spaceDeleted) {
mTxtCardNumber.setSelection(mTxtCardNumber.getSelectionStart() - 1);
spaceDeleted = false;
}
// enable text watcher
mTxtCardNumber.addTextChangedListener(this);
}
private String formatText(CharSequence text)
{
StringBuilder formatted = new StringBuilder();
int count = 0;
for (int i = 0; i < text.length(); ++i)
{
if (Character.isDigit(text.charAt(i)))
{
if (count % 4 == 0 && count > 0)
formatted.append(" ");
formatted.append(text.charAt(i));
++count;
}
}
return formatted.toString();
}
});
In case you're still searching, I ran into this problem the last day, and found that using a TextWatcher is the best (still not really good) option. I had to group digits of credit card numbers.
someEditText.addTextChagedListener(new TextWatcher()
{
//According to the developer guide, one shall only edit the EditText's
//content in this function.
#Override
public void afterTextChanged(Editable text)
{
//You somehow need to access the EditText to remove this listener
//for the time of the changes made here. This is one way, but you
//can create a proper TextWatcher class and pass the EditText to
//its constructor, or have the EditText as a member of the class
//this code is running in (in the last case, you simply have to
//delete this line).
EditText someEditText = (EditText) findViewById(R.id.someEditText);
//Remove listener to prevent further call due to the changes we're
//about to make (TextWatcher is recursive, this function will be
//called again for every change you make, and in my experience,
//replace generates multiple ones, so a flag is not enough.
someEditText.removeTextChangedListener(this);
//Replace text with processed the processed string.
//FormatText is a function that takes a CharSequence (yes, you can
//pass the Editable directly), processes it the way you want, then
//returns the result as a String.
text.replace(0, text.length(), FormatText(text));
//Place the listener back
someEditText.addTextChangedListener(this);
}
#Override
public void beforeTextChaged(CharSequence s, int start, int count, int after)
{
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count)
{
}
});
My formatting function for the credit card numbers looked like this:
String FormatText(CharSequence text)
{
StringBuilder formatted = new StringBuilder();
int count = 0;
for (int i = 0; i < text.length(); ++i)
{
if (Character.isDigit(text.charAt(i)))
{
//You have to be careful here, only add extra characters before a
//user-typed character, otherwise the user won't be able to delete
//with backspace, since you put the extra character back immediately.
//However, this way, my solution would put a space at the start of
//the string that I don't want, hence the > check.
if (count % 4 == 0 && count > 0)
formatted.append(' ');
formatted.append(text.charAt(i));
++count;
}
}
return formatted.toString();
}
You might have to mind other issues as well, since this solution actually rewrites the EditText's content every time a change is made. For example, you should avoid processing characters you inserted yourself (that is an additional reason for the isDigit check).
use simple function:
public String digit_grouping(String in_digit){
String res = "";
final int input_len = in_digit.length();
for(int i=0 ; i< input_len ; i++)
{
if( (i % 3 == 0) && i > 0 )
res = "," + res;
res = in_digit.charAt(input_len - i - 1) + res;
}
return res;
}