Android cyclic listeners - android

I've got a small issue in my code:
I've a SeekBar and an EditText in a layout.
What I want to do is to set the EditText value when the SeekBar changes and set the SeekBar progress when the EditText changes.
So far, I've the following code:
SeekBar seek_coagulation = (SeekBar) findViewById(R.id.seek_coagulation);
EditText edit_coagulation = (EditText) findViewById(R.id.edit_coagulation);
seek_coagulation.incrementProgressBy(1);
seek_coagulation.setMax(1000);
seek_coagulation.setOnSeekBarChangeListener( new OnSeekBarChangeListener() {
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser)
{
double value = progress / 100.0;
edit_coagulation.setText(String.valueOf(value));
}
public void onStartTrackingTouch(SeekBar seekBar)
{
}
public void onStopTrackingTouch(SeekBar seekBar)
{
}
});
edit_coagulation.addTextChangedListener(new TextWatcher() {
public void afterTextChanged(Editable s)
{
double value = (s != null && s.toString().length() > 0) ? Double.parseDouble(s.toString().replace(',', '.')) : 0D;
int progress = (int) (value * 100);
seek_coagulation.setProgress(progress);
}
public void beforeTextChanged(CharSequence s, int start, int count, int after)
{
}
public void onTextChanged(CharSequence s, int start, int before, int count)
{
}
});
The first case (SeekBar to EditText) works well, but not the second.
The problem is that it is a loop (SeekBar to EditText to SeekBar to EditText etc.).
Is there a way to fix that ?

I suggest to use two boolean parameters to lock changes, like this:
boolean mIsTextLocked = false;
boolean mIsSeekBarLocked = false;
seek_coagulation = (SeekBar) findViewById(R.id.seek_coagulation);
EditText edit_coagulation = (EditText) findViewById(R.id.edit_coagulation);
seek_coagulation.incrementProgressBy(1);
seek_coagulation.setMax(1000);
seek_coagulation.setOnSeekBarChangeListener( new OnSeekBarChangeListener() {
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser)
{
if(!mIsSeekBarLocked){
mIsTextLocked = true;
double value = progress / 100.0;
edit_coagulation.setText(String.valueOf(value));
}
}
public void onStartTrackingTouch(SeekBar seekBar)
{
}
public void onStopTrackingTouch(SeekBar seekBar)
{
mIsTextLocked = false;
}
});
edit_coagulation.addTextChangedListener(new TextWatcher() {
public void afterTextChanged(Editable s)
{
if(!mIsTextLocked){
mIsSeekBarLocked = true;
double value = (s != null && s.toString().length() > 0) ?Double.parseDouble(s.toString().replace(',', '.')) : 0D;
int progress = (int) (value * 100);
seek_coagulation.setProgress(progress);
mIsSeekBarLocked = false;
}
}
public void beforeTextChanged(CharSequence s, int start, int count, int after)
{
}
public void onTextChanged(CharSequence s, int start, int before, int count)
{
}
});
I haven't tested it but it should work.

Related

Updating seekbar value

I have a seekbar where which have following intervals: minimum value = 100000 and max value is 2500000 and interval is 25000
I need to update my seekbar on edittext change and also validate for figure which does not fall in range of 25000.
Here is my code:
int mininumloan_amount = 100000;
int maximum_loan_amount = 2500000;
int interval = 25000;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_seekbar);
loanAmountSeekbar = findViewById(R.id.loanAmountSeekbar);
loanAmountValue = findViewById(R.id.loanAmountValue);
setEditText();
setSeekBar();
}
private void setEditText() {
loanAmountValue.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) {
int porgress = Math.round((int) Double.parseDouble(charSequence.toString()));
if (porgress > mininumloan_amount)
loanAmountSeekbar.setProgress(porgress);
}
#Override
public void afterTextChanged(Editable editable) {
}
});
}
private void setSeekBar() {
loanAmountSeekbar.setMax((maximum_loan_amount - mininumloan_amount) / interval);
loanAmountSeekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
#Override
public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
double value = mininumloan_amount + (i * interval);
loanAmountValue.setText(value + "");
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
}
I find it easiest to work with the progress bar based on a percentage of 100.
So simply put, I would convert the input to a percentage of the maximum number you want to allow for the loan.
Here is the code I used for my demo to have a proof of concept:
private void setEditText() {
loanAmountValue.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) {
double actualProgress = computeProgress(charSequence);
Log.d(TAG, "Progress: " + actualProgress);
// if the progress is 100 or less its in range
if(actualProgress <= 100) {
loanAmountSeekbar.setProgress((int) actualProgress);
}
}
#Override
public void afterTextChanged(Editable editable) {
}
});
}
/*
Mostly where all the magic is, we convert input to percent
then check against minimum value, tell user to enter more.
If the percent is more than 100, we default to 100%.
*/
private Double computeProgress(CharSequence charSequence) {
try {
// Parse the value, also, used android:inputType="decimalNumber" in xml
double editTextNumber = Double.parseDouble(charSequence.toString());
if(editTextNumber < interval){
Toast.makeText(MainActivity.this, "Please enter a value greater than "+ interval, Toast.LENGTH_SHORT).show();
return 0.0;
}
// Turn the number into a percentage of the max value
double percentageOfMax = editTextNumber / maximum_loan_amount;
// Turn the percentage back into a number between 0 and 100
double percent = 100 * percentageOfMax;
// handle the max percent
if(percent > 100){
return 100.0;
}else {
return percent;
}
} catch (NumberFormatException ex) { // just in case something odd
ex.printStackTrace();
Toast.makeText(MainActivity.this, "Unable to parse: " + charSequence.toString(), Toast.LENGTH_SHORT).show();
}catch(ArithmeticException ex){ // extra since we check a min above, probably can exlude this
Toast.makeText(MainActivity.this, "value must not be 0 ", Toast.LENGTH_SHORT).show();
}
return 0.0;
}
private void setSeekBar() {
loanAmountSeekbar.setMax(100);
loanAmountSeekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
#Override
public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
if(b){
double value = interval * i;
loanAmountValue.setText(value + "");
}
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
}
Disclaimer
This was a pretty quick solution, so you may need to explore additional error handling. But the basic idea is, take the input, convert it to a percent of 100.
Good Luck and Happy Coding!

Minimum Value on Android SeekBar

How can I set the minimum value of a SeekBar?
I need a minimum of 435 and a max of 5435, but I don't know how to add the minimum value in this case.
My current code is here:
private void bindSeekAndEditText() {
mSeekBar = (SeekBar) mRootView.findViewById(R.id.sb_value);
mSeekBar.setMax(5435);
mEtOtherValue = (EditText) mRootView.findViewById(R.id.et_value);
mSeekBar.setProgress((int) (mSeekBar.getProgress() == 0 ? 5435 : mSeekBar.getProgress()));
mEtOtherValue.addTextChangedListener(mEtOtherValue);
mEtOtherValue.setText((double) (mSeekBar.getProgress() == 0 ? 5435 : mSeekBar.getProgress());
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void afterTextChanged(Editable s) {
}
});
mSeekBar.setOnSeekBarChangeListener(new OnSeekBarStepChangeListener());
}
private final class OnSeekBarStepChangeListener implements OnSeekBarChangeListener {
#Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
progress = ((int) Math.round(progress / 50)) * 50;
mSeekBar.setProgress(progress);
mEtOtherValue.setText(FormatingUtil.parseMoneyToBr((double) progress));
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
}
Can anyone please help me?
As of API Level 26 the method setMin(int) was added to class android.widget.AbsSeekBar (see here). Since SeekBar is a subclass of AbsSeekBar, this method can also be used in SeekBar.

Automatically add colon to Edittext

I want to add a mac address to my database via EditText.
Is it possible to add a colon (:) after every second character?
The colon should be displayed directly in the EditText.
EDIT: Tried it. And I think I am on the right way ( your anwers confirm this :P )
inputMac = (EditText) view.findViewById(R.id.editText_mac);
inputMac.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 (s.length() == 2 || s.length() == 5 || s.length() == 7 || s.length() == 9 || s.length() == 12 ){
inputMac.setText(inputMac.getText() + ":");
}
}
#Override
public void afterTextChanged(Editable s) {
}
});
But now after 12 characters I get e.g. 123456789123:::::
I've already answered a similar question, so this is how you can achieve it:
String mTextValue;
Character mLastChar = '\0'; // init with empty character
int mKeyDel;
myEditText.addTextChangedListener(new TextWatcher() {
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
boolean flag = true;
String eachBlock[] = myEditText.getText().toString().split(":");
for (int i = 0; i < eachBlock.length; i++) {
if (eachBlock[i].length() > 6) {
flag = false;
}
}
if (flag) {
myEditText.setOnKeyListener(new View.OnKeyListener() {
#Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_DEL)
mKeyDel = 1;
return false;
}
});
if (mKeyDel == 0) {
if (((myEditText.getText().length() + 1) % 3) == 0) {
myEditText.setText(myEditText.getText() + ":");
myEditText.setSelection(myEditText.getText().length());
}
mTextValue = myEditText.getText().toString();
} else {
mTextValue = myEditText.getText().toString();
if (mLastChar.equals(':')) {
mTextValue = mTextValue.substring(0, mTextValue.length() - 1);
myEditText.setText(mTextValue);
myEditText.setSelection(mTextValue.length());
}
mKeyDel = 0;
}
} else {
myEditText.setText(mTextValue);
}
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
if (s.length()>0) {// save the last char value
mLastChar = s.charAt(s.length() - 1);
} else {
mLastChar = '\0';
}
}
#Override
public void afterTextChanged(Editable s) {}
});
PS: It also handle deleting characters.
I tried I think I found a way wich is not that complicated. (Its not perfect but I think I will make it)
inputMac.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
macLengthBefore = inputMac.length();
Log.d("Textlänge BEFORE", macLengthBefore.toString());
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
#Override
public void afterTextChanged(Editable s) {
macLengthAfter = inputMac.length();
Log.d("Textlänge AFTER", macLengthAfter.toString());
if (macLengthAfter > macLengthBefore && ((inputMac.getText().length() + 1) % 3 == 0) && inputMac.length() <= 15) {
inputMac.setText(inputMac.getText() + ":");
inputMac.setSelection(inputMac.getText().length());
}
}
});
Thanks #Rami for modulo query
After few trial and errors I was able to write a simple and working code:
mEditText.addTextChangedListener(new TextWatcher() {
public void afterTextChanged(Editable s) {
}
public void beforeTextChanged(CharSequence s, int start,
int count, int after) {
}
public void onTextChanged(CharSequence s, int start,
int before, int count) {
if ((s.toString().length() < 17) && ((before == 1 && count == 2) || (before == 4
&& count == 5))) {
String string = mEditText.getText().toString();
string = string.concat(":");
mEditText.setText(string);
mEditText.setSelection(string.length());
}
}
});
Below code goes into your xml file:
<EditText
android:id="#+id/edit_text"
style="#style/textfield_wh"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLength="17"
android:digits="abcdefABCDEF0123456789:"
android:inputType="text" />
Try this,
editText1.addTextChangedListener(new TextWatcher() {
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// TODO Auto-generated method stub
*****APPLY YOUR LOGIC HERE*****
}
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
// TODO Auto-generated method stub
}
#Override
public void afterTextChanged(Editable s) {
// TODO Auto-generated method stub
}
});
}

How can i detect ENTER pressed without using OnkeyListner

I am having a problem with the on key listener, which i have completely explained here Unable to get line number on press of ENTER
Now if i am using a textwatcher to implement certain functionalities on my edittext, i need the line number everytime when my cursor moves to the next line. i want to know when the ENTER key is pressed, how can i do that
here is the code of textwatcher
scene.addTextChangedListener(new TextWatcher() {
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
//here is your code
strcheck = s.toString().substring((s.length()-1),s.length());
if (nowUpper)
strcheck = strcheck.toUpperCase();
else if (nowLower)
strcheck = strcheck.toLowerCase();
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
// TODO Auto-generated method stub
}
#Override
public void afterTextChanged(Editable s) {
if (nowUpper){
if(ix==1)
{
if(strcheck=="\n")
strcheck="a";
Log.v(strcheck, strcheck);
ix=0;
scene.setText(scene.getText().toString().substring(0,scene.length()-1) + strcheck);
scene.setSelection(scene.getText().length());
}
else
{
ix=1;
}
// TODO Auto-generated method stub
}
}
});
try this TextWatcher:
TextWatcher watcher = new TextWatcher() {
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
checkForNewLine(s, start, count, "new line entered");
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
checkForNewLine(s, start, count, "new line deleted");
}
#Override
public void afterTextChanged(Editable s) {
}
private void checkForNewLine(CharSequence s, int start, int count, String msg) {
int end = start + count;
for (int i = start; i < end; i++) {
if (s.charAt(i) == '\n') {
Log.d(TAG, msg);
}
}
}
};
You can add OnKeyListener for your EditText:
myEdit.setOnKeyListener(new OnKeyListener() {
#Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_UP
&& keyCode == KeyEvent.KEYCODE_ENTER) {
int currentLine = getCurrentLine(myEdit);
}
return false;
}
});
private int getCurrentLine(EditText editText) {
int lineNumber = 0;
String text = editText.getText().toString()
.substring(0, editText.getSelectionStart());
for (int i = 0; i < text.length(); i++) {
if (String.valueOf(text.charAt(i)).equalsIgnoreCase("\n")) {
lineNumber++;
}
}
return lineNumber;
}

Custom format edit text input android to accept credit card number

how to make edit text accept input in format
4digitnumber-4dignumber-4dignumber-4dignumber
The code
text.addTextChangedListener(new TextWatcher() {
int len = 0;
String string ;
#Override
public void afterTextChanged(Editable s) {
text.setOnKeyListener(new OnKeyListener()
{ public boolean onKey(View v, int keyCode, KeyEvent event)
{
if (keyCode == KeyEvent.KEYCODE_DEL)
{
}
else{
string = text.getText().toString();
len = string.length()+1;
if(len%5==0){text.append("-");}
}
return false; } });
}
});
works fine upon adding, but deleting or editing causes problem.
Now this works fine for soft/hard keyboard for all delete/edit ops.
tx 4 ur help..
package com.and;
import android.app.Activity;
import android.app.AlertDialog;
import android.inputmethodservice.KeyboardView;
import android.os.Bundle;
import android.telephony.PhoneNumberFormattingTextWatcher;
import android.text.Editable;
import android.text.Selection;
import android.text.Spannable;
import android.text.TextWatcher;
import android.text.format.Formatter;
import android.text.method.NumberKeyListener;
import android.view.KeyEvent;
import android.view.View;
import android.view.View.OnKeyListener;
import android.widget.EditText;
import android.widget.Toast;
public class ccformat extends Activity {
String a;
int keyDel;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
final EditText text = (EditText) findViewById(com.and.R.id.editText1);
text.addTextChangedListener(new TextWatcher() {
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
boolean flag = true;
String eachBlock[] = text.getText().toString().split("-");
for (int i = 0; i < eachBlock.length; i++) {
if (eachBlock[i].length() > 4) {
flag = false;
}
}
if (flag) {
text.setOnKeyListener(new OnKeyListener() {
#Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_DEL)
keyDel = 1;
return false;
}
});
if (keyDel == 0) {
if (((text.getText().length() + 1) % 5) == 0) {
if (text.getText().toString().split("-").length <= 3) {
text.setText(text.getText() + "-");
text.setSelection(text.getText().length());
}
}
a = text.getText().toString();
} else {
a = text.getText().toString();
keyDel = 0;
}
} else {
text.setText(a);
}
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
// TODO Auto-generated method stub
}
#Override
public void afterTextChanged(Editable s) {
}
});
}
}
This is working:
public class EditTextSample extends Activity {
// This regexp has to be improved, it does not detect case where you have
// more than 4 digits in a middle group like: 1234-12345-123
static final Pattern CODE_PATTERN = Pattern.compile("([0-9]{0,4})|([0-9]{4}-)+|([0-9]{4}-[0-9]{0,4})+");
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.edit_text_sample);
final EditText editText = (EditText) findViewById(R.id.input);
editText.addTextChangedListener(new TextWatcher() {
#Override
public void afterTextChanged(Editable s) {
Log.w("", "input" + s.toString());
if (s.length() > 0 && !CODE_PATTERN.matcher(s).matches()) {
String input = s.toString();
String numbersOnly = keepNumbersOnly(input);
String code = formatNumbersAsCode(numbersOnly);
Log.w("", "numbersOnly" + numbersOnly);
Log.w("", "code" + code);
editText.removeTextChangedListener(this);
editText.setText(code);
// You could also remember the previous position of the cursor
editText.setSelection(code.length());
editText.addTextChangedListener(this);
}
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
private String keepNumbersOnly(CharSequence s) {
return s.toString().replaceAll("[^0-9]", ""); // Should of course be more robust
}
private String formatNumbersAsCode(CharSequence s) {
int groupDigits = 0;
String tmp = "";
for (int i = 0; i < s.length(); ++i) {
tmp += s.charAt(i);
++groupDigits;
if (groupDigits == 4) {
tmp += "-";
groupDigits = 0;
}
}
return tmp;
}
});
}
}
If you want to just group visually the numbers, but you don't want to alter the value of the EditText adding dashes, you can use this Span approach:
EditText editText = findViewById(R.id.editText);
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 editable) {
Object[] paddingSpans = editable.getSpans(0, editable.length(), DashSpan.class);
for (Object span : paddingSpans) {
editable.removeSpan(span);
}
addSpans(editable);
}
private static final int GROUP_SIZE = 4;
private void addSpans(Editable editable) {
final int length = editable.length();
for (int i = 1; i * (GROUP_SIZE) < length; i++) {
int index = i * GROUP_SIZE;
editable.setSpan(new DashSpan(), index - 1, index,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
});
where the DashSpan class looks like this:
/**
* A {#link ReplacementSpan} used for spacing in {#link android.widget.EditText}
* to space things out. Adds '-'s
*/
public class DashSpan extends ReplacementSpan {
#Override
public int getSize(#NonNull Paint paint, CharSequence text, int start, int end, FontMetricsInt fm) {
float padding = paint.measureText("-", 0, 1);
float textSize = paint.measureText(text, start, end);
return (int) (padding + textSize);
}
#Override
public void draw(#NonNull Canvas canvas, CharSequence text, int start, int end, float x, int top, int y,
int bottom, #NonNull Paint paint) {
canvas.drawText(text.subSequence(start, end) + "-", x, y, paint);
}
}
This way you will have visually the grouping using the dashes, but the getText() will return the text without any grouping.
To force only numbers you can add the attributes android:digits="0123456789" and android:inputType="number" to the EditText.
This solution is based on the code of this library.
In my case below code is working fine.
editTextCreditCard.addTextChangedListener(new FourDigitCardFormatWatcher());
Add custom class for TextWatcher.
public class FourDigitCardFormatWatcher implements TextWatcher {
private static final char space = ' ';
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void afterTextChanged(Editable s) {
if (s.length() > 0 && (s.length() % 5) == 0) {
final char c = s.charAt(s.length() - 1);
if (space == c) {
s.delete(s.length() - 1, s.length());
}
}
if (s.length() > 0 && (s.length() % 5) == 0) {
char c = s.charAt(s.length() - 1);
if (Character.isDigit(c) && TextUtils.split(s.toString(), String.valueOf(space)).length <= 3) {
s.insert(s.length() - 1, String.valueOf(space));
}
}
}
}
Hope this would help you.
It works in all cases, when you insert or remove a character, the format will always be right. Make sure you set
android:inputType="number"
/
myEditText.addTextChangedListener(new TextWatcher() {
private final String space = "-"; // you can change this to whatever you want
private final Pattern pattern = Pattern.compile("^(\\d{4}"+space+"{1}){0,3}\\d{1,4}$"); // check whether we need to modify or not
#Override
public void onTextChanged(CharSequence s, int st, int be, int count) {
String currentText = myEditText.getText().toString();
if (currentText.isEmpty() || pattern.matcher(currentText).matches())
return; // no need to modify
String numbersOnly = currentText.trim().replaceAll("[^\\d.]", "");; // remove everything but numbers
String formatted = "";
for(int i = 0; i < numbersOnly.length(); i += 4)
if (i + 4 < numbersOnly.length())
formatted += numbersOnly.substring(i,i+4)+space;
else
formatted += numbersOnly.substring(i);
myEditText.setText(formatted);
myEditText.setSelection(myEditText.getText().toString().length());
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
#Override
public void afterTextChanged(Editable e) {}
});
It seems to me the answers presented here do not work properly with delete, delete from the middle operations, etc.
Here is my code. It doesn't restrict the length of input, but seems to be ok with various insertions and deletions:
import android.text.Editable;
import android.text.TextWatcher;
import android.view.KeyEvent;
import android.view.View;
import android.widget.EditText;
public class HyphenDelimitTextWatcher implements TextWatcher {
EditText mEditText;
boolean mInside = false;
boolean mWannaDeleteHyphen = false;
boolean mKeyListenerSet = false;
final static String MARKER = "|"; // filtered in layout not to be in the string
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
if(!mKeyListenerSet) {
mEditText.setOnKeyListener(new View.OnKeyListener() {
#Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
try {
mWannaDeleteHyphen = (keyCode == KeyEvent.KEYCODE_DEL
&& mEditText.getSelectionEnd() - mEditText.getSelectionStart() <= 1
&& mEditText.getSelectionStart() > 0
&& mEditText.getText().toString().charAt(mEditText.getSelectionEnd() - 1) == '-');
} catch (IndexOutOfBoundsException e) {
// never to happen because of checks
}
return false;
}
});
mKeyListenerSet = true;
}
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if (mInside) // to avoid recursive calls
return;
mInside = true;
int currentPos = mEditText.getSelectionStart();
String string = mEditText.getText().toString().toUpperCase();
String newString = makePrettyString(string);
mEditText.setText(newString);
try {
mEditText.setSelection(getCursorPos(string, newString, currentPos, mWannaDeleteHyphen));
} catch (IndexOutOfBoundsException e) {
mEditText.setSelection(mEditText.length()); // last resort never to happen
}
mWannaDeleteHyphen = false;
mInside = false;
}
#Override
public void afterTextChanged(Editable s) {
}
private String makePrettyString(String string) {
String number = string.replaceAll("-", "");
boolean isEndHyphen = string.endsWith("-") && (number.length()%4 == 0);
return number.replaceAll("(.{4}(?!$))", "$1-") + (isEndHyphen ?"-":"");
}
private int getCursorPos(String oldString, String newString, int oldPos, boolean isDeleteHyphen) {
int cursorPos = newString.length();
if(oldPos != oldString.length()) {
String stringWithMarker = oldString.substring(0, oldPos) + MARKER + oldString.substring(oldPos);
cursorPos = (makePrettyString(stringWithMarker)).indexOf(MARKER);
if(isDeleteHyphen)
cursorPos -= 1;
}
return cursorPos;
}
public HyphenDelimitTextWatcher(EditText editText) {
mEditText = editText;
}
}
Usage:
mSomeEditText.addTextChangedListener(new HyphenDelimitTextWatcher(mSomeEditText));
if you neeed this efect,ou can use this code in EditText
Here is a formatting regex used to show card details in format XXXX XXXX XXXX XXXX
etCreditCardNumber.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) {
etCreditCardNumber.setFloatingLabel(MaterialEditText.FLOATING_LABEL_HIGHLIGHT);
String initial = s.toString();
// remove all non-digits characters
String processed = initial.replaceAll("\\D", "");
// insert a space after all groups of 4 digits that are followed by another digit
processed = processed.replaceAll("(\\d{4})(?=\\d)(?=\\d)(?=\\d)", "$1 ");
//Remove the listener
etCreditCardNumber.removeTextChangedListener(this);
int index = etCreditCardNumber.getSelectionEnd();
if (index == 5 || index == 10 || index == 15)
if (count > before)
index++;
else
index--;
//Assign processed text
etCreditCardNumber.setText(processed);
try {
etCreditCardNumber.setSelection(index);
} catch (Exception e) {
e.printStackTrace();
etCreditCardNumber.setSelection(s.length() - 1);
}
//Give back the listener
etCreditCardNumber.addTextChangedListener(this);
}
#Override
public void afterTextChanged(Editable s) {
}
});

Categories

Resources