Textwatcher giving Stackoverflow error

≯℡__Kan透↙ 提交于 2020-02-25 21:39:08

问题


i am making a length converter which uses TextWatcher. the shortened code is

 v1.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) {
                val1=Double.parseDouble(v1.getText().toString()); 
                val2=InchToCm(val1); 
                val3=InchToMl(val1);
                val4=InchToKm(val1);
                val5=InchToM(val1);
                val6=InchToY(val1);
                val7=InchToFt(val1);
                val8=InchToLg(val1);
                val9=InchToLgn(val1);
                val11=InchTomm(val1);
                val12=InchToNm(val1);


                v2. setText(Double.toString(val2)); 
                v3. setText(Double.toString(val3)); 
                v4. setText(Double.toString(val4)); 
                v5. setText(Double.toString(val5)); 
                v6. setText(Double.toString(val6)); 
                v7. setText(Double.toString(val7)); 
                v8. setText(Double.toString(val8));
                v9. setText(Double.toString(val9)); 
                v11. setText(Double.toString(val11)); 
                v12. setText(Double.toString(val12));

            }

            @Override
            public void afterTextChanged(Editable s) {

            }
        });
     v2.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) {
                val2=Double.parseDouble(v2.getText().toString()); 
                val1=CmToInch(val2);
                val3=CmToMl(val2);
                val4=CmToKm(val2);
                val5=CmToM(val2);
                val6=CmToY(val2);
                val7=CmToFt(val2);
                val8=CmToLg(val2);
                val9=CmToLgn(val2);
                val11=CmTomm(val2);
                val12=CmToNm(val2);

                v1. setText(Double.toString(val1)); 
                v3. setText(Double.toString(val3)); 
                v4. setText(Double.toString(val4)); 
                v5. setText(Double.toString(val5)); 
                v6. setText(Double.toString(val6)); 
                v7. setText(Double.toString(val7)); 
                v8. setText(Double.toString(val8));
                v9. setText(Double.toString(val9)); 
                v11. setText(Double.toString(val11)); 
                v12. setText(Double.toString(val12));

            }

            @Override
            public void afterTextChanged(Editable s) {

            }
        });

i am getting a stackoverflow error.if i remove the textwatcher code for v2 completely.the text watcher for v1 works perfectly.. i just want all my edittext to work with textwatcher. what am i missing?and what should i change?

heres the logcat

08-20 20:31:28.122: E/AndroidRuntime(30377): FATAL EXCEPTION: main
08-20 20:31:28.122: E/AndroidRuntime(30377): java.lang.StackOverflowError
08-20 20:31:28.122: E/AndroidRuntime(30377):    at           android.text.TextUtils.getChars(TextUtils.java:77)
08-20 20:31:28.122: E/AndroidRuntime(30377): android.text.TextUtils.indexOf(TextUtils.java:110)
08-20 20:31:28.122: E/AndroidRuntime(30377):    at     android.text.StaticLayout.generate(StaticLayout.java:182)
08-20 20:31:28.122: E/AndroidRuntime(30377):    at android.text.DynamicLayout.reflow(DynamicLayout.java:284)
08-20 20:31:28.122: E/AndroidRuntime(30377):    at android.text.DynamicLayout.<init>(DynamicLayout.java:170)
08-20 20:31:28.122: E/AndroidRuntime(30377):    at android.widget.TextView.makeSingleLayout(TextView.java:6078)
08-20 20:31:28.122: E/AndroidRuntime(30377):    at android.widget.TextView.makeNewLayout(TextView.java:5976)
08-20 20:31:28.122: E/AndroidRuntime(30377):    at android.widget.TextView.checkForRelayout(TextView.java:6515)
08-20 20:31:28.122: E/AndroidRuntime(30377):    at android.widget.TextView.setText(TextView.java:3787)
08-20 20:31:28.122: E/AndroidRuntime(30377):    at android.widget.TextView.setText(TextView.java:3645)
08-20 20:31:28.122: E/AndroidRuntime(30377):    at android.widget.EditText.setText(EditText.java:80)
08-20 20:31:28.122: E/AndroidRuntime(30377):    at android.widget.TextView.setText(TextView.java:3620)
08-20 20:31:28.122: E/AndroidRuntime(30377):    at com.tdx.simplelegthconverter.MainActivity$1.onTextChanged(MainActivity.java:55)
08-20 20:31:28.122: E/AndroidRuntime(30377):    at android.widget.TextView.sendOnTextChanged(TextView.java:7338)
08-20 20:31:28.122: E/AndroidRuntime(30377):    at android.widget.TextView.setText(TextView.java:3790)
08-20 20:31:28.122: E/AndroidRuntime(30377):    at android.widget.TextView.setText(TextView.java:3645)
08-20 20:31:28.122: E/AndroidRuntime(30377):    at android.widget.EditText.setText(EditText.java:80)
08-20 20:31:28.122: E/AndroidRuntime(30377):    at android.widget.TextView.setText(TextView.java:3620)
08-20 20:31:28.122: E/AndroidRuntime(30377):    at   com.tdx.simplelegthconverter.MainActivity$2.onTextChanged(MainActivity.java:95)

回答1:


You are creating an infinite loop of Text Changes between v1 and v2.

Whenever you edit something in v1, it's TextWatcher sets the text of v2 which in turn triggers the onTextChanged() of your v2's TextWatcher. Now, v2's TextWatcher sets text of v1 (which triggers the onTextChanged() of your v1 again). This is creating a never-ending loop and you are getting the StackOverflow exception.

You have to change the flow of your code.

EDIT: I am showing an example, how to get rid of the StackOverflow error for two EditText fields. You can follow the similar coding for as many EditTexts as you want.

First of all, you can extend the TextWatcher like this (This will help you to keep your code organized. And, you can use this same TextWatcher for all your EditTexts):

class MyInputWatcher implements TextWatcher {
        private EditText et;

        private MyInputWatcher(EditText editText) {
            et = 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) {

            switch (et.getId()) {
            case R.id.editText1: {
                v2.removeTextChangedListener(watcher2);
                v2.setText(s);// set whatever text you want to set in editText2
                v2.addTextChangedListener(watcher2);
                //add the above 3 lines of code for other EditTexts, too (if any)
                break;
            }
            case R.id.editText2: {
                v1.removeTextChangedListener(watcher1);
                v1.setText(s); // set whatever text you want to set in editText1
                v1.addTextChangedListener(watcher1);

                break;
            }
            }
        }

        @Override
        public void afterTextChanged(Editable s) {

        }
    }

The trick is to remove the onTextChangeListeners before you call setText(). And, then add them back after you are done with setting text.

Declare these member variables:

private EditText v1, v2;
private MyInputWatcher watcher1, watcher2;

Now, in the onCreate() method, implement this:

v1 = (EditText)findViewById(R.id.editText1);
v2 = (EditText)findViewById(R.id.editText2);

watcher1 = new MyInputWatcher(v1);
watcher2 = new MyInputWatcher(v2);

v1.addTextChangedListener(watcher1);
v2.addTextChangedListener(watcher2);

For your convenience, the whole Activity class looks like this:

import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.EditText;

public class MainActivity extends ActionBarActivity {

    EditText v1, v2;
    private MyInputWatcher watcher1, watcher2;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        v1 = (EditText)findViewById(R.id.editText1);
        v2 = (EditText)findViewById(R.id.editText2);

        watcher1 = new MyInputWatcher(v1);
        watcher2 = new MyInputWatcher(v2);

        v1.addTextChangedListener(watcher1);
        v2.addTextChangedListener(watcher2);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();
        if (id == R.id.action_settings) {
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    class MyInputWatcher implements TextWatcher {
        private EditText et;

        private MyInputWatcher(EditText editText) {
            et = 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) {

            switch (et.getId()) {
            case R.id.editText1: {
                v2.removeTextChangedListener(watcher2);
                v2.setText(s);// set whatever text you want to set in editText2
                v2.addTextChangedListener(watcher2);
                break;
            }
            case R.id.editText2: {
                v1.removeTextChangedListener(watcher1);
                v1.setText(s); // set whatever text you want to set in editText1
                v1.addTextChangedListener(watcher1);

                break;
            }
            }
        }

        @Override
        public void afterTextChanged(Editable s) {

        }
    }
}



回答2:


Do not do setText inside onTextChanged(),it is causing recursion.

You can try the following: Set a boolean variable when you do setText from inside the onTextChange(). This boolean variable will help to identify that onTextChange was called because text was change programmatically and not by the user,thus avoiding recursion

        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {
                      if(isTextChangedByUser){
                          //calculate and set different values only when user has changed       values
                           editext.setText("programmatically setting the text,change the flag")
                           isTextChangedByUser=false;
                       }else{
                           //to avoid recursion
                            return;
                       }
        }

Second time when onTextChanged is called it will simply return since isTextChangeByUser flag is false.In this manner we will avoid recursion.

Alternatively,to make the implementation more simple you can put a "calculate" button next to each editext.You will only start calculation when the user presses this button and not when text changes.



来源:https://stackoverflow.com/questions/25407959/textwatcher-giving-stackoverflow-error

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!