마스터Q&A 안드로이드는 안드로이드 개발자들의 질문과 답변을 위한 지식 커뮤니티 사이트입니다. 안드로이드펍에서 운영하고 있습니다. [사용법, 운영진]

EditText 동적생성 후 TextWatcher를 이용해 값 입력을 하면 바로 계산되게 하고 싶은데 방법을 모르겠습니다.

0 추천
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle presses on the action bar items
        int id = item.getItemId();
        if( id == R.id.plus){

            TextView newEPM1 = new TextView(getApplicationContext());
            newEPM1.setText("매수가:");
            FrameLayout.LayoutParams param1 = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT);
            param1.topMargin = a;
            param1.leftMargin = 130;
            a += 120;
            newEPM1.setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
            newEPM1.setTextSize(18);
            newEPM1.setLayoutParams(param1);
            newEPM1.setTextColor(0xFF000000);
            addLayout.addView(newEPM1);

            EPMt1 = new EditText(getApplicationContext());
            FrameLayout.LayoutParams param4 = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT);
            param4.width = 660;
            param4.topMargin = b;
            b+= 120;
            param4.leftMargin = 350;
            EPMt1.setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
         EPMt1.setInputType(InputType.TYPE_CLASS_NUMBER|InputType.TYPE_NUMBER_FLAG_DECIMAL |InputType.TYPE_NUMBER_FLAG_SIGNED);
            EPMt1.setTextSize(14);
            EPMt1.setLayoutParams(param4);
            addLayout.addView(EPMt1);

            TextView newEPM2 = new TextView(getApplicationContext());
            newEPM2.setText("매수량:");
            FrameLayout.LayoutParams param2 = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT);
            param2.topMargin = a;
            param2.leftMargin = 130;
            a += 120;
            newEPM2.setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
            newEPM2.setTextSize(18);
            newEPM2.setLayoutParams(param2);
            newEPM2.setTextColor(0xFF000000);
            addLayout.addView(newEPM2);

            EPMt2 = new EditText(getApplicationContext());
            FrameLayout.LayoutParams param5 = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT);
            param5.width = 660;
            param5.topMargin = b;
            b += 120;
            param5.leftMargin = 350;EPMt2.setInputType(InputType.TYPE_CLASS_NUMBER|InputType.TYPE_NUMBER_FLAG_DECIMAL |InputType.TYPE_NUMBER_FLAG_SIGNED);
            EPMt2.setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
            EPMt2.setTextSize(14);
            EPMt2.setLayoutParams(param5);
            addLayout.addView(EPMt2);
        }

        return super.onOptionsItemSelected(item);
    }

이런식으로 해당 액션바에 있는 버튼을 눌렀을 때 텍스트가 동적으로 생성이 되도록 코드를 만들어봤는데 버튼을 한번 누르면 두 개의 TextView와 두 개의 EditText가 나오는데 TextWatcher를 사용해서 이 EditText의 값 두개를 곱하고 버튼을 한번 더 눌러서 또다른 EditText를 생성하면 거기에 또 다른 값 두개를 넣어서 곱한 후 앞에서 곱한 값과 덧셈을 하고 싶은데 각각 텍스트의 값들을 어떻게 받아야 할지를 모르겠습니다. TextWatcher를 이용할 수 없다면 Onclick 버튼을 이용해서라도 구현해보고 싶습니다.

아니면 텍스트를 이렇게 만드는 방법말고 레이아웃에 텍스트를 미리만들어 놓고 그 레이아웃을 여러번 불러서 이벤트 처리를 해도 되는건가요? 된다면 그 방법으로 하고싶습니다

나아기코린이응애 (140 포인트) 님이 2021년 7월 14일 질문
나아기코린이응애님이 2021년 7월 14일 수정
XML에 EditText를 놓고 visibility를 동적으로 변경하는 것과 텍스트 변경에 TextWatcher를 사용한다는 걸 아시니 이미 스스로 답을 구하신 것 같은데요.
참고로. TextWatcher는 사용 후에는 removeTextWatcher를 통해 해제해 주시는 것이 혹 생길 수 있는 메모리 누수를 방지하는데 좋습니다.
TextWatcher를 사용하고 싶은데 어떻게 사용해야할지 감이 안오네요
새로운 텍스트가 생길때마다 그 텍스트에 각각이벤트처리를 하고싶은데 그 방법을 모르겠습니다 ㅠ
            EPMt1.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) {
                    if(EPMt1.getText().toString().equals("-") || EPMt2.getText().toString().equals("-")
                            || EPMt1.getText().toString().equals(".")|| EPMt2.getText().toString().equals(".")
                            || EPMt1.getText().toString().length() == 0 || EPMt2.getText().toString().length() == 0) {
                       res.setText("");
                        return;
                    }
                    else if (EPMt1.getText().toString().length() != 0 && EPMt2.getText().toString().length() != 0) {
                        n1 = Integer.parseInt(EPMt1.getText().toString());
                        n2 = Integer.parseInt(EPMt2.getText().toString());
                        re = n1 * n2;
                            String commaNum = NumberFormat.getInstance().format(re);
                            res.setText(commaNum);
                    }
                }
            });
일단 이렇게 해봤는데 이렇게 하면 텍스트를 하나 더 만들었을때 이전에 있던 텍스트는 이벤트 처리가 안됩니다 ㅠㅠ

1개의 답변

0 추천

EditText를 동적으로 생성한다면 TextWatcher를 하나 만들고 이걸 공유하면 될 것 같은데요. 이렇게 한번 해보시죠.

님의 코드에서 EditText등의 이름이 뭘 하는지 알 수가 없어서 코드를 읽는데 방해가 됩니다. 남들이 봐도 알기 쉬운 이름을 사용하세요. 이게 좋은 코드를 만드는데 첫번째로 강조되는 내용입니다.

먼저, TextWatcher에서 실제로 사용되는 메소드가 하나이므로 간단히 abstract class를 만듦니다. 이렇게 함으로써 MainActivity 에서 코드를 읽을 때 좀 더 집중하기가 쉬워집니다.

import android.text.TextWatcher;

public abstract class AfterTextChangedListener implements 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) {

    }
}

 

EditText의 값을 검사하는 로직은 MainActivity에 있을 필요가 없고 반복되므로 반복을 줄이기 위한 클래스를 하나 만듭니다.
저의 예제는 심플한 구현이지 최선은 아닙니다.

public class EditField {
    private String text;
    private Integer value;

    public String getText() {
        return text;
    }

    public void setText(String text) {
        try {
            value = Integer.valueOf(text);
        } catch (NumberFormatException e) {
            value = null;
        }
        this.text = text;
    }

    public int getValue() {
        return value;
    }

    public boolean validate() {
        return value != null;
    }
}


위의 클래스는 EditText 에 들어온 값 한개만 입력받아 검사하는 클래스이므로 3개의 입력의 받아서 검사하는 클래스를 만듭니다.

public class EditFieldsValidator {
    private final EditField field1;
    private final EditField field2;
    private final EditField field3;

    public EditFieldsValidator() {
        field1 = new EditField();
        field2 = new EditField();
        field3 = new EditField();
    }

    public int getValue1() {
        return this.field1.getValue();
    }

    public int getValue2() {
        return this.field2.getValue();
    }

    public int getValue3() {
        return this.field3.getValue();
    }

    public void setInput1(String text) {
        this.field1.setText(text);
    }

    public void setInput2(String text) {
        this.field2.setText(text);
    }

    public void setInput3(String text) {
        this.field3.setText(text);
    }

    public void setInputs(String text1, String text2, String text3) {
        setInput1(text1);
        setInput2(text2);
        setInput3(text3);
    }

    public boolean validate() {
        return field1.validate() &&
                field2.validate() &&
                field3.validate();
    }
}

 

마찬가지로 최대한 간편하게 코드를 만들었습니다. 

다음은, Activity에 위의 클래스들을 적용한 코드입니다. 생각나는대로 만든 코드이므로 필요한 곳은 맞춰서 수정하시기 바랍니다.
그리고 계산을 하는 부분의 로직은 아직 좀 더 좋게 만들 수 있는 여지가 많습니다.

public class MainActivity extends AppCompatActivity {

    private ViewGroup rootView;
    private EditText EPMt1;
    private EditText EPMt2;
    private EditText EPMt3 = null;
    private Button newEditBtn;
    private TextView resultTxt;

    private EditFieldsValidator editFieldsValidator;
    private EditFieldsValidator validator() {
        if (editFieldsValidator == null) {
            editFieldsValidator =new EditFieldsValidator();
        }
        return editFieldsValidator;
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        bindViews();
    }

    private void bindViews() {
        rootView = findViewById(R.id.rootView);
        EPMt1 = findViewById(R.id.EPMt1);
        EPMt2 = findViewById(R.id.EPMt2);
        resultTxt = findViewById(R.id.resultTxt);

        newEditBtn = findViewById(R.id.newEditBtn);
        newEditBtn.setOnClickListener(v -> {
            if (EPMt3 != null) return;
            EPMt3 = createEditText();
            EPMt3.addTextChangedListener(textChangedListener);
            EPMt3.requestFocus();
        });
    }

    @Override
    protected void onDestroy() {
        textChangedListener = null;
        EPMt3 = null;
        super.onDestroy();
    }

    private AfterTextChangedListener textChangedListener = new AfterTextChangedListener() {
        @Override
        public void afterTextChanged(Editable s) {
            if (s == null) return;

            calculate();
        }
    };

    private void calculate() {
        String input1 = EPMt1.getText().toString();
        String input2 = EPMt2.getText().toString();

        if (EPMt3 == null) {
            // 필요한 처리 추가
            return;
        }

        String input3 = EPMt3.getText().toString();

        EditFieldsValidator validator = validator();
        validator.setInputs(input1, input2, input3);
        if (!validator.validate()) {
            // 필요한 처리 추가
            return;
        }

        int result = validator.getValue1() * validator.getValue2() + validator.getValu3();
        resultTxt.setText("Result: " + result);
    }

    private EditText createEditText() {
        EditText view = new EditText(this);
        rootView.addView(view);
        return view;
    }

    @Override
    protected void onStart() {
        super.onStart();
        addTextChangedListeners();
    }
 
    private void addTextChangedListeners() {
        EPMt1.addTextChangedListener(textChangedListener);
        EPMt2.addTextChangedListener(textChangedListener);
        if (EPMt3 != null) {
            EPMt3.addTextChangedListener(textChangedListener);
        }  
    }

    @Override
    protected void onStop() {
        removeTextChangedListeners();
        super.onStop();
    }

    private void removeTextChangedListeners() {
        EPMt1.removeTextChangedListener(textChangedListener);
        EPMt2.removeTextChangedListener(textChangedListener);
        if (EPMt3 != null) {
            EPMt3.removeTextChangedListener(textChangedListener);
        }
    }
}


담당하는 일이 다르다면 메소드나 클래스 등으로 분리를 하는 것이 좋습니다.  참고하셔서 님의 요구에 맞게 업그레이드 하시면 좋을 듯합니다.

spark (226,420 포인트) 님이 2021년 7월 14일 답변
spark님이 2021년 7월 14일 수정
...