Why is this simple android app recursive? - java

New SO user here and fairly new to Java. Upon running this, it crashes with a java.lang.stackoverflow error. I'm fairly certain it is recursive but I can't figure out why. I've tried stepping through debugging but I get an error that it can't find the class file. Here is the code:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
newPrice.addTextChangedListener(tradeWatch);
tradeIn.addTextChangedListener(tradeWatch);
acc.addTextChangedListener(tradeWatch);
tradeDif.addTextChangedListener(tradeWatch);
}
TextWatcher tradeWatch = new TextWatcher() {
#Override
public void afterTextChanged(Editable s) {
calcTrade();
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
// TODO Auto-generated method stub
}
#Override
public void onTextChanged(CharSequence s, int start, int before,
int count) {
// TODO Auto-generated method stub
}
};
private void calcTrade() {
Editable eValue1 = newPrice.getText(), eValue2 = tradeIn.getText(), eValue3 = acc.getText();
Double value1 = 0.0, value2 = 0.0, value3 = 0.0, result = 0.0;
if (eValue1 != null)
value1 = toDouble(eValue1);
if (eValue2 != null)
value2 = toDouble(eValue2);
if (eValue3 != null)
value3 = toDouble(eValue3);
if (value1 != null && value2 != null && value3 != null)
result = value1 - (value2 + value3);
tradeDif.setText(result.toString());
}
private double toDouble(final Editable editable) {
final String content = editable.toString();
if (content.isEmpty()) {
return 0;
}
return Double.parseDouble(content);
}
public void nextPage(View v) {
Intent i=new Intent(this, Activity2.class);
startActivity(i);
}
}

Problem is line:
tradeDif.setText(result.toString());
You change text and it causes to call
afterTextChanged(Editable s)
again and again.
Simple solution
Remove listener
Change value
Add listener

The problem is that your tradeDif is using your tradeWatch TextWatcher. In that TextWatcher's afterTextChanged(), you call calcTrade(), which calls tradeDif.setText(result.toString());.
After Android finishes setting the text on tradeDif, it will again call afterTextChanged() on your TextWatcher, and it will continue doing so until you stop setting tradeDif's text.

Related

How to change TextView if there's input in multiple EditTexts?

I'm trying to wrote android program which displays Greatest Common Divisor of two integers specified in two different EditText fields. First I've done it with button, everything worked (you can see onclick listener commented out in code below). Now I want to do this: app checks when both EditTexts are not empty and then automatically starts calculating and shows gcd. Buty app crashes when I start typing in any of EditText fields. Also I tried to add TextChangeListener only on one of EditTexts. Everything is good until I delete all input from one of the fields, then app crashes again. I'm only starting to understand android development and made this app mostly by modifying examples found on internet so maybe I did something wrong... Can anyone help me? Thanks
MainActivity.java
public class MainActivity extends Activity
{
EditText a;
EditText b;
TextView gcdResult;
Button calculateGcd;
int a, b, gcdValue
TextWatcher textWatcher = new TextWatcher(){
#Override
public void afterTextChanged(Editable s){}
#Override
public void beforeTextChanged(CharSequence s,int start, int count, int after){}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count){
AutoCalculateGcd();
}
};
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
a = (EditText)findViewById(R.id.aText1);
b = (EditText)findViewById(R.id.bText1);
gcdResult = (TextView)findViewById(R.id.resultTextView1);
calculateGcd = (Button)findViewById(R.id.calcButton1);
/* calculateGcd.setOnClickListener(new OnClickListener(){
public void onClick(View v){
AutoCalculateRatio();
}
});*/
a.addTextChangedListener(textWatcher);
b.addTextChangedListener(textWatcher);
}
//Euclidean alghorithm to find gcd
public static int gcd(int a, int b) {
if (b == 0) return w;
else return gcd(b a % b);
}
public static boolean isInputNotEmpty(EditText a, EditText b){
String a = a.getText().toString();
String b = b.getText().toString();
if(a.equals("") && b.equals("") ){
return false;
}
else{
return true;
}
}
public void AutoCalculateGcd(){
if(isInputNotEmpty(a, b)){
a = Integer.parseInt(width.getText().toString());
b = Integer.parseInt(height.getText().toString());
gcdValue = gcd(a, b);
ratioResult.setText(Integer.toString(gcdValue));
}
else{
//Toast.makeText(this, "No input", Toast.LENGTH_SHORT).show();
}
}
}
Actually, you should replace
public static boolean isInputNotEmpty(EditText a, EditText b) {
String a = a.getText().toString();
String b = b.getText().toString();
if (a.equals("") && b.equals("")) {
return false;
}
else {
return true;
}
}
with
public static boolean isInputNotEmpty(EditText a, EditText b) {
String a = a.getText().toString();
String b = b.getText().toString();
if (a.equals("") || b.equals("")) {
return false;
}
else {
return true;
}
}
Or even
public static boolean isInputNotEmpty(EditText a, EditText b) {
return !(a.getText().toString().isEmpty() || b.getText().toString().isEmpty());
}
Because you want to know if any ( || ) of them is empty, not if both (&&) are.
It might help if you post the stacktrace, but my guess is that you are getting a NumberFormatException from the Integer.parseInt() calls. One approach would be to do something like:
try {
a = Integer.parseInt(width.getText().toString());
b = Integer.parseInt(height.getText().toString());
gcdValue = gcd(a, b);
ratioResult.setText(Integer.toString(gcdValue));
} catch ( NumberFormatException e) {
ratioResult.setText("N/A")
}

creating a chat activity in android

After declaring and creating the textWatcher object, I would like to disable the send button and set it to gray if the chatText (edit text) is empty
I think it's a problem of ranking. Please help.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_chat);
list = (ListView)findViewById(R.id.listView);
list.setTranscriptMode(AbsListView.TRANSCRIPT_MODE_ALWAYS_SCROLL);
chatText = (EditText)findViewById(R.id.editText);
//chatText.setOnKeyListener(this);
me = true;
send = (Button)findViewById(R.id.button);
change = (Button)findViewById(R.id.button2);
list.setTranscriptMode(AbsListView.TRANSCRIPT_MODE_ALWAYS_SCROLL);
adp = new TheAdapter(getApplicationContext(),R.layout.chat);
list.setAdapter(adp);
chatText.addTextChangedListener(textWatcher);
checkFieldsForEmptyValues();
send.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
envoyer();
}
});
change.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
me = !me;
if (!me) {
change.setText(R.string.sender2);
} else {
change.setText(R.string.sender);
}
}
});
}
public void envoyer(){
adp.add(new messages(me, chatText.getText().toString()));
chatText.setText("");
}
private void checkFieldsForEmptyValues(){
String s1 = chatText.getText().toString();
if (s1.length() < 0 ) {
send.setEnabled(false);
} else {
send.setEnabled(true);
send.setBackgroundColor(Color.BLUE);
//send.setBackgroundColor((getResources().getColor(R.color.blue)));
}
}
In the onTextChanged is where you would check to see if the text field is empty:
chatText.addTextChangedListener(new TextWatcher() {
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if (s.length() > 0) { //enable}
else if (s.length() == 0 { //disable }
In your code, you have if (s1.length() < 0 ) which I don't think will ever be true because the text size will never be less than 0.
// Disable on init
send.setEnabled(false);
// add text changer
chatText.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) {
// Enable when input is != null and not empty. You can check string lenght too
send.setEnabled(s != null && !s.toString().isEmpty());
}
#Override
public void afterTextChanged(Editable s) {
}
});
disable button before and add textwatch. You can also check if edittext value is not empty or verify string length.
I hope it will help you

Android app reading in multiple variables from one String input

I am trying to read in multiple variables (int, double, String) from one input. However, the input has to be a specific way, i.e. the user should enter either distance in miles by entering “X miles” (either in decimals or as an integer) or time by entering “Y mins” (only as an integer). Here is an example of what I coded. However, enter a double does not work. I also need to use these values in other methods.
public boolean hintWalkTracker() {
// tracks whether the input for walking/running activity is correct
String text = String.valueOf(hintEditText.getText());
String s = text.replaceAll("\\d","");
int i = Integer.parseInt(text.replaceAll("[\\D]", ""));
double d = Double.parseDouble(text.replaceAll("[\\D]", ""));
if ((activityDropDown.getSelectedItem().equals("Walking") || activityDropDown.getSelectedItem().equals("Running")) && s.equals(" miles")) {
d = d * 88.9;
return true;
} else if ((activityDropDown.getSelectedItem().equals("Walking") || activityDropDown.getSelectedItem().equals("Running")) && s.equals(" mins")) {
d = i * 4.78;
return true;
} else if ((activityDropDown.getSelectedItem().equals("Walking") || activityDropDown.getSelectedItem().equals("Running")) && ((!s.equals(" mins")) || !s.equals(" miles"))) {
// create a new AlertDialog Builder
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
// set dialog's message to display
builder.setMessage(R.string.walk_missing_message);
// provide an OK button that simply dismisses the dialog
builder.setPositiveButton(R.string.OK, null);
// create AlertDialog from the AlertDialog.Builder
AlertDialog errorDialog = builder.create();
errorDialog.show(); // display the modal dialog
return false;
}
return false;
}
You just need to use a TextWatcher on your EditText in order to check what the user is writing and to react as you want when he write what you need.
Code sample :
editText.addTextChangedListener(new TextWatcher() {
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// TODO Auto-generated method stub
hintWalkTracker();
}
#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
}
});

EditText CurrencyFormat without symbol Android

The code below produce an stackoverflow error. The idea is to format the amount when or after user type an amount.
500 -> 500.00
1000 -> 1 000.00
29999.55-> 29 999.55
..
..
..
edit_amount.addTextChangedListener(new TextWatcher() {
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
// TODO Auto-generated method stub
}
#Override
public void afterTextChanged(Editable s) {
if (edit_amount.getText().toString().length()>0){
edit_amount.setText(
currencyFormat(edit_amount.getText().toString()));
}else {}
}
});
public String currencyFormat(String number){
String credits="";
try {
//en, us
if (TextUtils.isEmpty(MyApplication.pref.GetPreferences("AppCurrency"))){
credits = NumberFormat.getCurrencyInstance(new Locale("fr", "FR")).format(Double.valueOf(number));
}else {
if (MyApplication.pref.GetPreferences("AppCurrency").equals("Euro")) {
credits = NumberFormat.getCurrencyInstance(new Locale("fr", "FR")).format(Double.valueOf(number));
} else {
credits = NumberFormat.getCurrencyInstance(new Locale("en", "US")).format(Double.valueOf(number));
}
}
}catch(Exception ex){
credits = "mCredits";
}
return credits;
}
Your code doesn't work because when you call edit_amount.setText(...) then the afterTextChanged(Editable s) is triggered and then edit_amount.setText(...) is triggered and so on. You need to change your logic to do what you want and avoid stack overflow.
For example you could unregister TextWatcher, setText and then register it again.
Alternatively, you can set a flag so that your TextWatcher knows when you change the text yourself and then instruct TextWatcher to ignore it.
To avoid recursive invocation of afterTextChanged callback you can set up additional chek. Smth like this
#Override
public void afterTextChanged(Editable s) {
if (et.getText().toString().length() > 0) {
String src = et.getText().toString();
String origin = currencyFormat(src);
if(!src.equals(origin)) {
et.setText(currencyFormat(et.getText().toString()));
}
}
}
Try below code :
private String current = "";
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if(!s.toString().equals(current)){
[your_edittext].removeTextChangedListener(this);
String cleanString = s.toString().replaceAll("[$,.]", "");
double parsed = Double.parseDouble(cleanString);
String formatted = NumberFormat.getCurrencyInstance().format((parsed/100));
current = formatted;
[your_edittext].setText(formatted);
[your_edittext].setSelection(formatted.length());
[your_edittext].addTextChangedListener(this);
}
}

i want to validate multiple edittexts on click of device keypads done button

I have four edittext (atm pin kind of thing) and I want to validate all these four edittext on click of keypads done button(validations like - empty or wrong pin). so far, I could get the validations but it happens only if I click the done buttn twice. not able to figure out the issue, please help. I am a beginner. thanks!
Below is my code :
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.pin_new);
setTextFocus(edtpin1, edtpin2);
setTextFocus(edtpin2, edtpin3);
setTextFocus(edtpin3, edtpin4);
setTextFocus(edtpin5, edtpin6);
setTextFocus(edtpin6, edtpin7);
setTextFocus(edtpin7, edtpin8);
edtpin1.setOnEditorActionListener(new OnEditorActionListener(){
#Override
public boolean onEditorAction(TextView v, int arg1, KeyEvent arg2) {
switch (v.getId()) {
case R.id.btnBackPin:
Intent i = new Intent(getApplicationContext(),RegistrationProfileEmail.class);
startActivity(i);
break;
case R.id.btnCancelPin:
Intent in = new Intent(getApplicationContext(),Exit.class);
finish();
startActivity(in);
break;
case R.id.btnConfirmPin:
String s = new String();
s = edtpin1.getText().toString() + edtpin2.getText().toString()
+edtpin3.getText().toString() + edtpin4.getText().toString();
if ((edtpin5.getText().toString().trim().isEmpty())
|| (edtpin1.getText().toString().trim().isEmpty())
|| (edtpin2.getText().toString().trim().isEmpty())
|| (edtpin4.getText().toString().trim().isEmpty())
|| (edtpin3.getText().toString().trim().isEmpty())
|| (edtpin6.getText().toString().trim().isEmpty())
|| (edtpin7.getText().toString().trim().isEmpty())
|| (edtpin8.getText().toString().trim().isEmpty())) {
Toast.makeText(getApplicationContext(), "Enter the Pin",
Toast.LENGTH_SHORT).show();
edtpin1.setText(null);
edtpin2.setText(null);
edtpin3.setText(null);
edtpin4.setText(null);
edtpin5.setText(null);
edtpin6.setText(null);
edtpin7.setText(null);
edtpin8.setText(null);
edtpin1.requestFocus();
}
else if (edtpin1.getText().toString().trim().equalsIgnoreCase(edtpin5.getText().toString().trim())&& edtpin2.getText().toString().trim().equalsIgnoreCase(
edtpin6.getText().toString().trim()) && edtpin3.getText().toString().trim().equalsIgnoreCase( edtpin7.getText().toString().trim()) && edtpin4.getText().toString().trim().equalsIgnoreCase( edtpin8.getText().toString().trim()))
{
s = edtpin1.getText().toString() + edtpin2.getText().toString()
+ edtpin3.getText().toString()
+ edtpin4.getText().toString();
RegistrationPin.this.setPin(s);
}
// TODO move to payment option
else {
Toast.makeText(getApplicationContext(), "Pin is not matching",Toast.LENGTH_SHORT).show();
edtpin1.setText(null);
edtpin2.setText(null);
edtpin3.setText(null);
edtpin4.setText(null);
edtpin5.setText(null);
edtpin6.setText(null);
edtpin7.setText(null);
edtpin8.setText(null);
edtpin1.requestFocus();
}
break;
default:
break;
}
return false;
}
});
}
Create a class and implement the textwatcher like this:
private class CheckTextifEmpty implements TextWatcher {
#Override
public void afterTextChanged(Editable s) {
}
#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 (isEmptyCharacters(serverNumber)
|| isEmptyCharacters(textview1)
|| isEmptyCharacters(textview2)
|| isEmptyCharacters(textview3)) {
//do something here
} else {
//do something here
}
}
}
and add this method here:
private boolean isEmptyCharacters(TextView v) {
return v.getText().toString().trim().isEmpty();
}
and use it in your textview like this:
textview1.addTextChangedListener(new CheckTextIfEmpty());
You can use your validation like this without using a button
Refactor your code like this:
private boolean checkifValidate(TextView view1,View TextView2){
return view1.getText().toString().trim().contentEquals(view2.getText().toString());
}
Hope it helps,
edtpin1.setOnKeyListener(onSoftKeyboardDonePress);
private View.OnKeyListener onSoftKeyboardDonePress=new View.OnKeyListener()
{
public boolean onKey(View v, int keyCode, KeyEvent event)
{
if (event.getKeyCode() == KeyEvent.KEYCODE_ENTER)
{
edtpin1.getText().toString();
edtpin2.getText().toString();
edtpin3.getText().toString();
edtpin4.getText().toString();
}
return false;
}
};

Categories