I'm trying to implement iPhone like PIN-code authorization with 4 EditText blocks like this:
I'm using TextWatcher to check if field was changed so I can jump between blocks.
Here is the code:
#Override
public void afterTextChanged(Editable s) {
if (del && index > 0) {
pin[index - 1].requestFocus();
}
else if ( ! del && ind < 3) {
pin[index + 1].requestFocus();
}
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
del = count != 0;
}
Everything works fine except when block is empty and DEL/BACKSPACE is pressed, I want to go back to previous block and clear its' value.
This is where TextWatcher fails me since no change was made in empty block it doesn't do anything.
I have tried using keyEventListener but it only catches events on emulator not on actual device via virtual keyboard.
Can anyone suggest an idea of how can I catch DEL event or any other way to implement this?
Maybe you can check if the field is empty before and after change? Don't know if any other key press can leave the field empty and so you can say, back was pressed and jump to previous field. Ok this is not a real technical solution, but just a different way of viewing the problem.
I found a workaround for this issue. This is probably not the best solution, but it works for me.
Aside from TextWatcher I've added InputFilter to blocks
#Override
public CharSequence filter(CharSequence source, int start, int end,
Spanned dest, int dstart, int dend) {
if ( end == 0 && ind > 0 && dest.length() == 0 ) {
pin[ind - 1].requestFocus();
}
return null;
}
I also think it's a better idea to port the rest of the code from TextWatcher to InputFilter
Related
I have an editText box and when user writes "hello" I want only change "hello" font-family change to italic but others text font-family stay same only change "hello"
String detectText, text;
detectText = "hello";
text = title.getText().toString();
detectText.
Could you please help me?
As someone said, you need a text watcher to be able to make changes when the text is changed. You also need spans to be able to style parts of the text. Here's a way to do it:
editText.addTextChangedListener(new TextWatcher() {
private static final String KEYWORD = "hello";
#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) {
// Remove previous spans.
for (StyleSpan span : s.getSpans(0, s.length(), StyleSpan.class)) {
s.removeSpan(span);
}
// Add new spans for every occurrence of the keyword.
int i = 0;
while (i != -1) {
i = text.toString().indexOf(KEYWORD, i);
if (i != -1) {
s.setSpan(new StyleSpan(Typeface.ITALIC), i, i + KEYWORD.length(),
Spannable.SPAN_INCLUSIVE_INCLUSIVE);
i += KEYWORD.length();
}
}
}
});
The text watcher has three methods called at different times of the editing, but it's only safe to make changes to the text in afterTextChanged. There, all previous style spans are removed then the text is scanned to add new ones.
Note that performance might be a problem if you intend to turn this into something a lot more complex, like a syntax highlighter. Right now all spans get readded everytime the user changes a single character.
I searched different questions but found nothing specific about my problem. I am changing text color by selecting color and works successfully however when I start deleting my edit text after typing a color text I get this error.
myedittext.addTextChangedListener(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) {
spannableString.setSpan(new ForegroundColorSpan(Color.parseColor(txtColor)), start, start+1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
#Override
public void afterTextChanged(Editable s) {
}
});
}
I get the following error
java.lang.IndexOutOfBoundsException: setSpan (118 ... 119) ends beyond length 118
at android.text.SpannableStringBuilder.checkRange(SpannableStringBuilder.java:1309)
at android.text.SpannableStringBuilder.setSpan(SpannableStringBuilder.java:680)
at android.text.SpannableStringBuilder.setSpan(SpannableStringBuilder.java:672)
at com.apps.primalnotes.Fragments.EditorFragment$16.onTextChanged(EditorFragment.java:842)
at android.widget.TextView.sendOnTextChanged(TextView.java:10611)
at android.widget.TextView.handleTextChanged(TextView.java:10715)
at android.widget.TextView$ChangeWatcher.onTextChanged(TextView.java:14057)
I am writing in two colors now like this
now when i save it. it saves in pink color only and shows me like this
but now when i save it again without any changes it save in the colors i wrote it
The onTextChanged method is invoked to tell you that within CharSequence s, number of character count beginning at start have just replaced old text that had length before.
What is happening is that when user presses backspace, start is at the upper limit of your charsequence i.e. If you had seven character before, start is 6 which is the same as the last element. You are doing start+1 which is always a number out of index range.
myedittext.addTextChangedListener(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) {
if(start < s.length() - 1 || count > before){
spannableString.setSpan(new ForegroundColorSpan(Color.parseColor(txtColor)), start, start+1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
#Override
public void afterTextChanged(Editable s) {
}
});
}
Didn't get to try that code but it should work. It's just to show you an idea of what you are doing wrong and what you should be doing.
When you delete text from the end of the string, the onTextChanged() method will be invoked with values representing the fact that the string is now shorter. It looks like you've written your code to always assume that the length will be longer; you're coloring in the just-typed character. But if you delete a character instead, you probably don't want to do this.
You could add a check to make sure that count is greater than before, this at the very least will indicate that text was added to the string.
if (count > before) {
spannableString.setSpan(...);
}
Whenever my back_button reaches the String[0] and I try proceeding going backwards my App just crashes.
Instead of simply going from String[0] to my currently last string [6] and continue to go backwards (if the conditions are met), why it doesnt do that ?
My code for that Button, btw im new to programming and I know my Code is EXTREMLY TRASH.. but, that's another topic, please xD :
back_button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
forward_button.setVisibility(View.GONE);
backButton();
if (mediator == 10) {
forward_button.setVisibility(View.VISIBLE);
backk--;
display.setText(list[backk]);
}
if (backk == currentnumber-5 ) {
back_button.setClickable(false);
}
if (backk != currentnumber-5) {
back_button.setClickable(true);
back_button.setEnabled(true);
}
if (mediator != 10){
back_button.setEnabled(false);
display.setText(list[currentnumber]);
}
}
});
Btw. I thought it maybe has something to do with this Code right here in my other's Button Logic, maybe there is a similiar function to call for when going from String [0] to String [last string(6)] ?
if (currentnumber == list.length) {
currentnumber = 0;
backk = 0;
back = 1;
EDIT : I deleted if ( backk < 0 ) { ... , I dont know why it was in there to begin with, sorry, that wasnt supposed to be in there.
In your block, make the following change:
if (mediator == 10) {
forward_button.setVisibility(View.VISIBLE);
backk--;
// If we go below the size of the array, add the array
// size to loop back to the last element in the array
if (backk < 0){
backk += list.length;
}
display.setText(list[backk]);
}
If you explain what it is you're trying to accomplish and post more of your code, I might be able to help clean up your code a little with comments on why I make the choices I do. But the change above will fix that array out of bounds crash.
I have a text field where the start symbol is $ (could be euro or pound depending on an application setting). I need to make it so that if the user clicks before the symbol nothing will happen. In other words, the selection must remain after the symbol. I tried doing something like this but it seems wrong and it gave me an error:
billAmount.addTextChangedListener(new TextWatcher() {
//other methods
#Override
public void afterTextChanged(Editable s) {
billAmount.setText(currencySymbol + billAmount.getText().toString());
}
});
I was thinking of using an inputFilter but nothing I tried worked. I'm also not allowed to use a TextView right before the EditText.
First, in your code sample, the reason you are getting an error is because, as others have said, you are calling the setText method inside the afterTextChanged method. Calling setText is obviously changing the text which causes afterTextChanged to be called again. This results in the afterTextChanged being called continuously until there is eventually a stack overflow.
You have two issues: 1) You want to always keep the cursor positioned after the currency symbol, and 2) You want to make sure the currency symbol is never somehow removed.
The easiest way to solve #1 is to create a subclass of EditText and override the onSelectionChanged method.
public class MyEditText extends EditText {
// ...
#Override
public void onSelectionChanged(int selStart, int selEnd) {
super.onSelectionChanged(selStart, selEnd);
// Make sure the text's length is greater than zero.
// Then, if the cursor is at position zero...
if (getText().length() > 0 && selStart == 0) {
// ...move it over to position one.
setSelection(1, selEnd);
}
}
}
This will force the cursor to always go after the currency symbol even if the user attempts to move it before it. The check getText().length() > 0 is to ensure that the EditText contains at least one character, otherwise attempting to move the cursor will result in an Exception.
As for #2, there are a couple ways to go at it. You can attempt to use some instance variables inside your TextWatcher to keep track of when the text needs to be formatted, but that won't prevent the unnecessary method calls from actually happening, and it adds some unneeded complexity. I think it would be easier to simply use an InputFilter, which you can specify in your extended EditText's constructor.
public class MyEditText extends EditText {
public MyEditText(Context context) {
super(context);
// Set the EditText's input filter.
setFilters(new InputFilter[] { new InputFilter {
#Override
public CharSequence filter(CharSequence source, int start, int end,
Spanned dest, int dstart, int dend) {
// If the currency symbol is about to be replaced...
if (dstart == 0)
// Add the currency symbol to the front of the source.
return currencySymbol + source;
// else
// Return null to indicate that the change is okay.
return null;
}
}});
}
// ...
}
In the filter method, the dest parameter represents the EditText's text, and the dstart and dend parameters represent the start and end positions of the portion of the text that is about to be replaced. Since the currency symbol should always be the first character, we know it is about to be replaced if dstart is zero, in which case we simply return the source (which represents the replacement text) with the currency symbol placed in front. Otherwise, we indicate that the change is okay by returning null.
I tested it, and it seems to work for what you need.
On a side note, although I understand that you're not "allowed" to use a TextView, I think it's worth reiterating that using one would provide a much better solution to this problem. One particularly useful solution being to have a hidden EditText contain the raw input from the user, and having the TextView on top of the EditText. You would use a TextWatcher to update the TextView with the properly formatted input from the EditText.
I guess you are getting StackOverFlowException.
try this
String oldValue="";//instace variable
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
//bilamount always starts with your currency symbol
if (s != null && s.length() == 0) {
editText.setText("$");
editText.setSelection(1);
}
if (s != null && s.length() > 0) {
String billAmount = s.toString();
if (!oldValue.equals(billAmount)) {//minimize unnecessary setText() method call
oldValue= billAmount;
editText.setText(billAmount);
editText.setSelection(billAmount.length());
}
}
}
Note : your EditText first init with your currencySymbol i.e. billAmount.setText(currencySymbol);
This question already has an answer here:
EditText Minimum Length & Launch New Activity
(1 answer)
Closed 8 years ago.
I am a new developer on Android and Java. How can I make at least 10 characters in EditText ? Also, when the user enter a value less than 10, the application send an error message on screen. How can I do these ? [ edittext > = 10 ]
Use something like this:
EditText et = (EditText) findViewById(YOUR_EDITTEXT);
String s = et.getText().toString();
if(s.length() <= 10){
et.setError("Must exceed 10 characters!");
} else {
// ...
}
You can do that in several ways, but you can try this way:
if (myEditText.getText().length() < minLength) {
//Your message to there is no enough caracters
} else {
//Your action if it is satisfied.
}
You can set minLenght to 10, or whatever, or simply ask if value is less than 10.
I hope that you get idea from this.
You can use text watcher to check the user input and decide what to do inside
EditText editText = new EditText(this);
editText.addTextChangedListener(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) {
}
#Override
public void afterTextChanged(Editable s) {
}
});
Yo can validate your TextView and if it doesn't fit your requirements, use myTextView.setError(String) to show an error.
If not you will have to implement myTextView.addTextChangedListener(...) and do things manually.
Hope it helps.