Description:
I am creating an app in which you can change your profile´s name. The problem is that users can write their names with a lot of empty spaces and I don´t know how to avoid this problem.
Example:
(I am going to represent empty spaces with underscore "_")
Edit your name: T_____omas_Men________n____i_ti //// (31 chars)
If someone writes that on an edit text, empty spaces are going to be saved and then we will see that large name into user's profile.
What do I want?
I need a code able to check when the edit text has more than one (> 1) empty spaces in a row in order to delete them. The code should be able to detect that "name" must have one empty space after "last name"
Use TextWatcher as described here: Android: How can I validate EditText input?
Then in onTextChanged(CharSequence s, int start, int before, int count) method you can validate user input, here is example :
if (s.contains(" ")) {
// ask user to edit input
// or change text programatically
String after = before.trim().replaceAll(" +", " ");
// set new text
}
You can restrict EditText by using TextWatcher in tricky way.
Working flow for below code
Create and added TextWatcher to EditText. Did the logic inside onTextChanged function
Logic 1 : To avoid empty space as a first character
Logic 2 : Restrict EditText to allow only one space after a character
Logic 3 : Restrict EditText to allow only one space for entire string
Logic 4 : Remove Text Change Lister before setting value in editText
Logic 5 : Set proper value in editText
Logic 6 : Again add Text Change Lister for editText
Try the below code.
package com.example.application;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.widget.EditText;
import androidx.appcompat.app.AppCompatActivity;
import com.example.myapplication.R;
public class SampleActivity extends AppCompatActivity {
EditText editText;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sample);
editText = findViewById(R.id.editText);
editText.addTextChangedListener(textWatcher);
}
TextWatcher textWatcher = new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
String preValue = editText.getText().toString();
/*
* To avoid empty space as a first character
* */
if (preValue.length() > 0 && preValue.trim().isEmpty()) {
editText.setText(preValue.substring(0, s.length() - 1));
editText.setSelection(editText.getText().toString().length());
}
/*
* Restrict EditText to allow only one space after a character
* */
if (preValue.endsWith(" ")) {
editText.setText(preValue.substring(0, s.length() - 1));
editText.setSelection(editText.getText().toString().length());
}
/*
* Restrict EditText to allow only one space for entire string
* */
if (preValue.trim().contains(" ") && preValue.endsWith(" ")) {
/*
* Remove Text Change Lister before setting value in editText
* */
editText.removeTextChangedListener(textWatcher);
/*
* Set proper value in editText
* */
editText.setText(preValue.substring(0, s.length() - 1));
editText.setSelection(editText.getText().toString().length());
/*
* Again add Text Change Lister for editText
* */
editText.addTextChangedListener(textWatcher);
}
if (preValue.contains("---") && preValue.endsWith(" ")) {
String[] words = preValue.split(" ");
if (words.length >= 2) {
editText.setText(preValue.substring(0, s.length() - 1));
editText.setSelection(editText.getText().toString().length());
return;
}
StringBuilder stringBuilder = new StringBuilder();
for (String word : words) {
stringBuilder.append(word).append(" ");
}
/*
* Remove Text Change Lister before setting value in editText
* */
editText.removeTextChangedListener(textWatcher);
/*
* Set proper value in editText
* */
editText.setText(stringBuilder);
editText.setSelection(editText.getText().toString().length());
/*
* Again add Text Change Lister for editText
* */
editText.addTextChangedListener(textWatcher);
}
}
#Override
public void afterTextChanged(Editable s) {
}
};
}
I hope this will solve your problem.
I'm offering a different kind of solution that focuses on working with bare Strings instead of the EditText. Bear in mind that this answer works in combination with the other answers here.
private static String filterMultipleSpaces(String s) {
char[] result = new char[s.length()];
int iC = 0;
for (int iS = 0; iS < s.length(); ++iS) {
char currentChar = s.charAt(iS);
if (currentChar != ' ' || (iC > 0 && result[iC - 1] != ' ')) {
result[iC] = currentChar;
++iC;
}
}
return new String(result);
}
The above static function takes a String and removes multiple spaces. Ideally you would use this when the text changes (see #Divakar's answer) or when submitting the EditText's value if you wish
Related
on MultpuAutocompletTexView while Typing I want to make some special word that is start with # want to make that word alone with some colour, how can I do this any help.
so when ever I type with # that word should be in colour text.
like this image
Use TextWatcher with Spanable text, and every time you need to check the last enter word, means the 0 index of last word is #, if YES then apply Spanable for EditText.
please have a look on below code.
define this variable on public level
int beforeChangeSize = 0, afterChangeSize = 0, cusrsorPos = 0, posOflastSpace = 0;
String lastcursorSubString = "";
#Override
public void afterTextChanged(Editable editable) {
if (afterChangeSize > beforeChangeSize) {
cusrsorPos = 0;
posOflastSpace = 0;
lastcursorSubString = "";
cusrsorPos = txt_search.getSelectionStart();
String sudString = txt_search.getText().toString().substring(0, cusrsorPos);
posOflastSpace = sudString.lastIndexOf(" ");
posOflastSpace = (posOflastSpace == -1) ? 0 : posOflastSpace;
lastcursorSubString = sudString.substring(posOflastSpace, cusrsorPos).trim();
if ((lastcursorSubString.length() > 1 &&(lastcursorSubString.charAt(0) == '#') {
textlable.setText(""+lastcursorSubString);
// txt_search.getText().replace(posOflastSpace+1, cusrsorPos, Html.fromHtml("<font color=#FE642E>" + lastcursorSubString + "</font>"));
// txt_search.showDropDown();
editable.setSpan(new ForegroundColorSpan(Color.RED),
posOflastSpace+1,
cusrsorPos,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
}
you should use combination of textWatcher and spannable text
use text watcher to see what user is typing and use spannable text to give special color or format to part of your text in textView or EditText
good tutorial for spannable text :
https://medium.com/google-developers/spantastic-text-styling-with-spans-17b0c16b4568
and documentation for text watcher here :
https://developer.android.com/reference/android/widget/TextView.html#addTextChangedListener(android.text.TextWatcher)
You can add your own TextChangedListener and implement onTextChanged method
myAutoTxtView.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence text, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence text, int start, int before, int count) {
if (text.charAt(start) == '#') {
//change color
}
}
#Override
public void afterTextChanged(Editable s) {
}
});
this code to change color - use it in on text change listener like above (its kotlin you need to modify it to Java if you want to)
//get text
val str = editText.text.toString()
//get last word
val splitStr = str.trim { it <= ' ' }.split("\\s+".toRegex()).dropLastWhile {
it.isEmpty() }.toTypedArray()
val ColorWord = splitStr.last()
//get the sentence without the last word
val textButLast = str.substring(0, str.lastIndexOf(" "))
//change the last word color
val LastWord = "<font color='#EE0000'>$ColorWord</font>"
//put the last word with the sentence again
editText.setText(Html.fromHtml(textButLast + LastWord))
I have a requirement that Edittext should have all words start with Capital letter. If the user writes it in a smaller case(first letter of the word), then also it should be converted it into Uppercase.
I have done it in layout as below so far :
<EditText
android:id="#+id/edGymName"
style="#style/LoginRegisterEditText"
android:layout_marginTop="#dimen/size_10"
android:layout_toLeftOf="#+id/txtStatusGymStatus"
android:hint="#string/gym_tag"
android:inputType="textPersonName|textCapWords|textNoSuggestions"
android:maxLength="30" />
But, I don't want to allow the user to write the first letter of the word in the small letter. This is working but the user is able to write the first letter of the word in the small case. What if we forcefully do not allow it.
Set the input type to TYPE_CLASS_TEXT| TYPE_TEXT_FLAG_CAP_CHARACTERS.
android:inputType="textCapCharacters" for every character
android:inputType="textCapSentences" for senteces
android:inputType="textCapWords" for every words
Use this it will work.
android:inputType="textCapSentences"
In your case
<EditText
android:id="#+id/edGymName"
style="#style/LoginRegisterEditText"
android:layout_marginTop="#dimen/size_10"
android:layout_toLeftOf="#+id/txtStatusGymStatus"
android:hint="#string/gym_tag"
android:inputType="textCapSentences"
android:maxLength="30" />
Change input type to input type to TYPE_CLASS_TEXT| TYPE_TEXT_FLAG_CAP_CHARACTERS.
android:inputType="text|textCapCharacters"
or from java code
editText.setInputType(InputType.TYPE_CLASS_TEXT |InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS);
Statically (i.e. in your layout XML file): set
android:inputType="textCapSentences" on your EditText.
Programmatically: you have to include InputType.TYPE_CLASS_TEXT in the InputType of the EditText, e.g.
EditText editor = new EditText(this);
editor.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_CAP_SENTENCES);
User can manually change the text caps from Soft keyBoard to manage this case you can set a input filter. Android provide a AllCap filter for this.
edittext.setFilters(new InputFilter[] {new InputFilter.AllCaps()});
Setting filter will reset some other attribute which you set in manifest. So beware of it . Like if you have set maxlenth attribute set in xml then after setting filter you need to reset it at runtime otherwise it won't work . Below is and example.
editText.setFilters(new InputFilter[] {new InputFilter.AllCaps(),new InputFilter.LengthFilter(40)});
So the best way to do it Prevent all previous filter and just add a new one.
InputFilter[] oldFilters = editText.getFilters();
InputFilter[] newFilters = new InputFilter[oldFilters.length + 1];
System.arraycopy(oldFilters, 0, newFilters, 0, oldFilters.length);
newFilters[oldFilters.length] = new InputFilter.AllCaps();
editText.setFilters(newFilters);
This will forcefully retype your whole text/sentence from your editText and make every first letter of the word capital:
String oldText = ""; //this must be outside the method where the addTextChangedListener on the editText is set. (Preferrably outside the onCreate())
editText.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
#Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
#Override
public void afterTextChanged(Editable editable) {
if (editable.toString().length() > 0 &&
!editable.toString().equals(oldText)) {
oldText = editable.toString(); //prevent infinite loop
editText.setText(capitalizeFirstLetterWord(editable.toString()));
editText.setSelection(editText.getText().length()); //set the cursor to the end of the editText
}
}
});
method called: (I've modified it a little bit, refer to the link)
/**
* reference: http://www.penguincoders.net/2015/06/program-to-capitalize-first-letter-of-each-word-in-java.html
*
* #param s sentence to be capitalize each first letter of each word
* #return capitalized sentence
*/
public static String capitalizeFirstLetterWord(String s) {
StringBuilder cap = new StringBuilder();
for (int i = 0; i < s.length(); i++) {
try {
char x = s.charAt(i);
if (x == ' ') {
cap.append(" ");
char y = s.charAt(i + 1);
cap.append(Character.toUpperCase(y));
i++;
} else {
cap.append(x);
}
} catch (IndexOutOfBoundsException ignored) {
}
}
//finally, capitalize the first letter of the sentence
String sentence = cap.toString();
if (sentence.length() > 0) {
sentence = String.valueOf(sentence.charAt(0)).toUpperCase(); //capitalize first letter
if (cap.toString().length() > 1) { //check if there's succeeding letters
sentence += cap.toString().substring(1); //append it also
}
}
return sentence;
}
I made a code where user can't enter first space in a string.
User is allowed to enter white space after min 2 characters.
I need to redefine my method so user enters white space once, and only once after the two or more characters. After that it should be prevented. How do I do that?
case UPDATE_NAME:
if (firstName.getText().toString().startsWith(" "))
firstName.setText(firstName.getText().toString().trim());
if (firstName.getText().toString().contains(" "))
firstName.setText(firstName.getText().toString().replace(" ", " "));
int indexOfSpace = firstName.getText().toString().lastIndexOf(" ");
if (indexOfSpace > 0) {
String beforeSpace = firstName.getText().toString().substring(0, indexOfSpace);
String[] splitted = beforeSpace.split(" ");
if (splitted != null && splitted.length > 0) {
if (splitted[splitted.length - 1].length() < 2)
firstName.setText(firstName.getText().toString().trim());
}
}
Use a regex pattern. I made one that should match your requirements.
\S{2}\S*\s\S*\n
Explanation:
\S{2} two non whitespace
\S* n non whitespace
\s a whitespace
\S* n non whitespace
\n newline (i only added that for regexr, you may not need it)
Alternate way:
Iterate over String.charAt(int), return false if there is a whitespace in the first two chars, count all whitespaces, return false if n > 1.
This method should meet your requirements:
private static boolean isValidFirstName(String firstName) {
if (firstName != null && !firstName.startsWith(" ")) {
int numberOfSpaces = firstName.length() - firstName.replace(" ", "").length();
if (firstName.length() < 2 || numberOfSpaces <= 1) {
return true;
}
}
return false;
}
What you need to do is use a TextWatcher
public class CustomWatcher implements TextWatcher {
private String myText;
private int count = 0;
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after){
myText= s;
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
#Override
public void afterTextChanged(Editable s) {
//check if there is a space in the first 2 characters, if so, sets the string to the previous before the space
if(s.length() < 3 && s.contains(" "))
s= myText;
//if the length is higher than 2, and the count is higher than 0 (1 space added already), puts the string back if a space is entered
else if(s.contains(" ") && count > 0)
s= myText;
//If none of the above is verified and you enter a space, increase count so the previous if statement can do its job
else if(s.contains(" "))
count++;
}
}
And then, set it to your EditText
mTargetEditText.addTextChangedListener(new CustomWatcher());
You can control your editText(I assume) with a TextWatcher, you would only need to check inside afterTextChanged() if length is <2 and else if the string contains the char " ".
I have 2 edittexts and 1 textview. 1 edittext for input the price another one the percentage and the textview will display the result of them both (the price * percentage/100) and i want to make the 1st edittext input(for the price) will change the format of the input and display it on the same edittext with decimal format. For example :
edittext1
100
the user type 100 it will just display 100 ,but when the user type one or more number(S) it will add "," every 3 number
edittext1
1,000
edittext1
10,000
edittext1
100,000
edittext1
1,000,000
and so on
i have the functions, one will autocalculate the value for textview1 , another will convert automatically the input of edittext. However they cant work together because the format for calculation function, it uses int/long/double and for the converter it uses decimalformat . If i use them both the app will crash with javanumberformatexception unable to parse int "1,000"(if we put 1000 into edittext)
my function for autocalculate
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.simulasikredit);
ethint1 = (EditText) findViewById(R.id.ethint);
etpersen2 = (EditText) findViewById(R.id.etpersen);
textvDP1 = (TextView) findViewById(R.id.textvDP);
etpersen2.addTextChangedListener(new TextWatcher() {
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
String text1 = ethint1.getText().toString();
String text2 = etpersen2.getText().toString();
long input1 = 0;
long input2 = 0;
if(text1.length()>0)
input1 = Long.valueOf(text1);
if(text2.length()>0)
input2 = Long.valueOf(text2);
if (text1.length() != 0) {
long output = (input1 * input2) / 100;
textvDP1.setText(""+output);
}
else if(text2.length() == 0){
textvDP1.setText("");
}
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void afterTextChanged(Editable s) {
}
}); }
et stands for edittext, tv stands for textview
and makedecimal function
public void makedecimal(View v)
{
ethint1.setRawInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_DECIMAL);
DigitsKeyListener dkl = new DigitsKeyListener(true,true);
ethint1.setKeyListener(dkl);
ethint1.addTextChangedListener(new TextWatcher(){
private String current = "";
#Override
public void afterTextChanged(Editable s) {
String userInput=s.toString();
if(!userInput.toString().equals(current)){
ethint1.removeTextChangedListener(this);
String cleanString = userInput.replaceAll("[,]", "");
if(cleanString.length()>0){
double parsed = Double.parseDouble(cleanString);
String formated = DecimalFormat.getNumberInstance().format(parsed);
current = formated;
ethint1.setText(formated);
ethint1.setSelection(formated.length());
}else{
ethint1.setText(cleanString);
ethint1.setSelection(cleanString.length());
}
ethint1.addTextChangedListener(this);
}
this makedecimal is android:onClick from ethint , ethint is the id(these two come from 1 edittext)
I need to fulfil a similar requirements before where we need to format the number in thousands and also support fractions.
My approach is to register a TextWatcher format text every time input changed, and provide a public method to get numeric value by stripping separators, which is quite tricky. My solution also caters for locale-specific separator by utilizing DecimalFormatSymbols class.
private final char GROUPING_SEPARATOR = DecimalFormatSymbols.getInstance().getGroupingSeparator();
private final char DECIMAL_SEPARATOR = DecimalFormatSymbols.getInstance().getDecimalSeparator();
...
/**
* Return numeric value repesented by the text field
* #return numeric value or {#link Double.NaN} if not a number
*/
public double getNumericValue() {
String original = getText().toString().replaceAll(mNumberFilterRegex, "");
if (hasCustomDecimalSeparator) {
// swap custom decimal separator with locale one to allow parsing
original = StringUtils.replace(original,
String.valueOf(mDecimalSeparator), String.valueOf(DECIMAL_SEPARATOR));
}
try {
return NumberFormat.getInstance().parse(original).doubleValue();
} catch (ParseException e) {
return Double.NaN;
}
}
/**
* Add grouping separators to string
* #param original original string, may already contains incorrect grouping separators
* #return string with correct grouping separators
*/
private String format(final String original) {
final String[] parts = original.split("\\" + mDecimalSeparator, -1);
String number = parts[0] // since we split with limit -1 there will always be at least 1 part
.replaceAll(mNumberFilterRegex, "")
.replaceFirst(LEADING_ZERO_FILTER_REGEX, "");
// only add grouping separators for non custom decimal separator
if (!hasCustomDecimalSeparator) {
// add grouping separators, need to reverse back and forth since Java regex does not support
// right to left matching
number = StringUtils.reverse(
StringUtils.reverse(number).replaceAll("(.{3})", "$1" + GROUPING_SEPARATOR));
// remove leading grouping separator if any
number = StringUtils.removeStart(number, String.valueOf(GROUPING_SEPARATOR));
}
// add fraction part if any
if (parts.length > 1) {
number += mDecimalSeparator + parts[1];
}
return number;
}
It's quite tedious to elaborate here so I'll only give a link for your own reading:
https://gist.github.com/hidroh/77ca470bbb8b5b556901
is there a way to set a custom space (in pixels) between letters to an editText? I found only how to set spaces between the lines, but bot between letters on the same row
Using android:letterSpacing i was able to add spacing between characters in a textview
<android.support.v7.widget.AppCompatTextView
android:id="#+id/textViewValue"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:letterSpacing="0.35"
android:maxLines="1" />
Related methods:
setLetterSpacing(float)
I had to do this myself today so here are some updates about this problem :
From API 21 you can use XML attribute android:letterSpacing="2" or from code myEditText.setLetterSpacing(2);
Before API 21, use a TextWatcher with the following code
private static final String LETTER_SPACING = " ";
private EditText myEditText;
private String myPreviousText;
...
// Get the views
myEditText = (EditText) v.findViewById(R.id.edt_code);
myEditText.addTextChangedListener(this);
...
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
// Nothing here
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// Nothing here
}
#Override
public void afterTextChanged(Editable s) {
String text = s.toString();
// Only update the EditText when the user modify it -> Otherwise it will be triggered when adding spaces
if (!text.equals(myPreviousText)) {
// Remove spaces
text = text.replace(" ", "");
// Add space between each character
StringBuilder newText = new StringBuilder();
for (int i = 0; i < text.length(); i++) {
if (i == text.length() - 1) {
// Do not add a space after the last character -> Allow user to delete last character
newText.append(Character.toUpperCase(text.charAt(text.length() - 1)));
}
else {
newText.append(Character.toUpperCase(text.charAt(i)) + LETTER_SPACING);
}
}
myPreviousText = newText.toString();
// Update the text with spaces and place the cursor at the end
myEditText.setText(newText);
myEditText.setSelection(newText.length());
}
}
You could implament a custom TextWatcher, and add X spaces every time the user enteres 1.
i have used this, and works for most API levels if not all of them.
KerningViews
Provides a set of views which allows to adjust the spacing between the characters of that view, AKA, Kerning effect.
https://github.com/aritraroy/KerningViews