14

私はAndroidアプリに取り組んでおり、ユーザーが数字を入力できるEditTextを持っています。さまざまな通貨形式(たとえば##、##、###)を使用して数値をフォーマットしたいのですが、その場で、つまりユーザーが各桁を入力したとき(Enterキーが押されたときではない)にフォーマットしたいと思います。グーグルで検索して、最初に有望だと思ったTextWatcherに出くわしましたが、それは絶対的な苦痛であることがわかりました。ソフトキーボードしかないHTCDesire電話でコードをデバッグしています。

ここで、ユーザーが数字(0〜9)、del(バックスペース)キーを押してキーを入力したときにコールバックを取得したいと思います。私のテストから、私はこれらを見つけました(少なくとも私の電話で)

1)editText onKeyListenerは、ユーザーがdelまたはEnterキーを押すと呼び出されます。ユーザーがEnterキーを押すと、onKey関数が1回のEnterに対して2回呼び出されます(これはACTION_UPとACTION_DOWNの場合だと思います)。ユーザーがdelを押すと、onKeyが1回呼び出されます(ACTION_DOWNの場合のみ)。理由はわかりません。ユーザーが数字(0から9)を押しても、onKeyが呼び出されることはありませんが、これも理解できません。

2)TextWatchers 3のコールバック関数は、ユーザーが任意の数字(0〜9)キーを押すたびに呼び出されます(beforeTextChanged、onTextChanged、afterTextChanged)。したがって、TextWatcherとonKeyListenerを一緒に使用することで、必要なすべてのコールバックを取得できると思いました。

今私の質問はこれらです。

1)最初に私のHTCソフトキーボードにキー(下向き矢印の付いたキーボード記号)があり、それをクリックすると、キーボードはコールバックを与えずに辞任します。私はまだ、Androidがユーザーにフィールドの編集を許可し、プログラムに編集を処理(保存)させずに辞任させることを信じることができません。これで、editTextに1つの値が表示され、オブジェクトに別の値が表示されます(Enterキーを押したときにユーザーの編集を保存し、editText値をオブジェクトの値にリセットしてキーボードを押し戻す処理を行っていますが、このキーボードのダウンキーに対する回答がありません) 。

2)次に、ユーザーが新しい桁を入力した後に数値をフォーマットしたいと思います。すでにeditTextに123があり、ユーザーが4を押して入力したとすると、editTextに1,234を表示させます。onTextChanged()とafterTextChanged()で完全な数値を取得し、数値をフォーマットして、これらのコールバックのいずれかでeditTextに戻すことができます。どちらを使うべきですか?ベストプラクティスはどれですか?

3)3番目は最も重大な問題です。アプリの起動時に、現在のオブジェクト値をeditTextに入れます。onResume()に123を置き、ユーザーが数字を入力すると(たとえば、4)1234にします。しかし、onTextChangedコールバックでは、取得するのは4123です。もう1つのキー(たとえば、5)を押すと、 45123を取得します。したがって、ユーザー入力の場合、editTextカーソルはテキストの終わりを指しています。ただし、値を手動で設定すると、editTextカーソルが更新されていないように見えます。textWatcherコールバックで何かをしなければならないと思いますが、何をすべきかわかりません。

以下にコードを投稿しています。

public class AppHome extends AppBaseActivity {
    private EditText ed = null;
    private NumberFormat amountFormatter = null;
    private boolean  isUserInput = true;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.app_home_screen);

        ed = (EditText)findViewById(R.id.main_amount_textfield);
        amountFormatter = new DecimalFormat("##,##,###");


        ed.setOnKeyListener(new View.OnKeyListener() {
            @Override
            public boolean onKey(View v, int keyCode, KeyEvent event) {
                if(event.getAction() == KeyEvent.ACTION_DOWN) 
                    return true;
                String strippedAmount = ed.getText().toString().replace(",", "");
                if(keyCode == KeyEvent.KEYCODE_DEL){
                    //delete pressed, strip number of comas and then delete least significant digit.
                    strippedAmount = strippedAmount.substring(0, strippedAmount.length() - 1);
                    int amountNumeral = 0;
                    try{
                        amountNumeral = Integer.parseInt(strippedAmount);
                    } catch(NumberFormatException e){
                    }
                    myObject.amount = amountNumeral;
                    isUserInput = false;
                    setFormattedAmount(amountNumeral,ed.getId());
                }else if(keyCode == KeyEvent.KEYCODE_ENTER){
                    //enter pressed, save edits and resign keyboard
                    int amountNumeral = 0;
                    try{
                        amountNumeral = Integer.parseInt(strippedAmount);
                    } catch(NumberFormatException e){
                    }
                    myObject.amount = amountNumeral;
                    isUserInput = false;
                    setFormattedAmount(myObject.amount,ed.getId());
                    //save edits
                    save();
                    //resign keyboard..
                    InputMethodManager in = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
                    in.hideSoftInputFromWindow(AppHome.this.getCurrentFocus().getWindowToken(),InputMethodManager.HIDE_NOT_ALWAYS);
                }
                return true;
            }
        });

        TextWatcher inputTextWatcher = new TextWatcher() {
            public void afterTextChanged(Editable s) { 
                if(isUserInput == false){
                    //textWatcher is recursive. When editText value is changed from code textWatcher callback gets called. So this variable acts as a flag which tells whether change is user generated or not..Possibly buggy code..:(
                    isUserInput = true;
                    return;
                }
                String strippedAmount = ed.getText().toString().replace(",", "");
                int amountNumeral = 0;
                try{
                    amountNumeral = Integer.parseInt(strippedAmount);
                } catch(NumberFormatException e){
                }
                isUserInput = false;
                setFormattedAmount(amountNumeral,ed.getId());
            }

            public void beforeTextChanged(CharSequence s, int start, int count, int after){
            }
            public void onTextChanged(CharSequence s, int start, int before, int count) {
            }
        };

        ed.addTextChangedListener(inputTextWatcher);
    }//end of onCreate...

    public void setFormattedAmount(Integer amount, Integer inputBoxId){
        double amountValue = 0;
        String textString =null;
        TextView amountInputBox = (TextView) findViewById(inputBoxId);

        amountValue = Double.parseDouble(Integer.toString(amount));
        textString = amountFormatter.format(amountValue).toString();
        amountInputBox.setText(textString);
    }
}

大きな問題だとは思いますが、同じ問題に2日間取り組んでいます。私はAndroidを初めて使用しますが、テキストエディットデータをその場で処理する簡単な方法がないとはまだ信じられません(iPhoneでも同じことを簡単に実行できます)。皆さんありがとう

編集:入力フィルターを使用した後

InputFilter filter = new InputFilter() { 
    public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) { 
            String strippedAmount = dest.toString() + source;
            strippedAmount = strippedAmount.replace(",", "");

        int amountNumeral = 0;
        try{
            amountNumeral = Integer.parseInt(strippedAmount);
        } catch(NumberFormatException e){
        }           
            return amountFormatter.format(amountNumeral).toString(); 
    } 
}; 

ed.setFilters(new InputFilter[]{filter}); 

アプリが起動すると、editTextに1,234を追加します

myObject.amount = 1234;
ed.setText(amountFormatter.format(myObject.amount).toString());

次に、ユーザーがeditTextをクリックすると、キーボードがポップアップし、ユーザーが数字の6を入力したと言います。

私が得ている:61234私が欲しい:12346

4

3 に答える 3

8

さて、何度も頭を叩いた後、カーソル位置の問題の回避策を見つけました。それが正しい方法かどうかはわかりませんが、うまくいきました。

    TextWatcher inputTextWatcher = new TextWatcher() {
        public void afterTextChanged(Editable s) { 
            if(isUserInput == false){
                //textWatcher is recursive. When editText value is changed from code textWatcher callback gets called. So this variable acts as a flag which tells whether change is user generated or not..Possibly buggy code..:(
                isUserInput = true;
                ed.setSelection(ed.getText().length());
                return;
            }
            String strippedAmount = ed.getText().toString().replace(",", "");
            int amountNumeral = 0;
            try{
                amountNumeral = Integer.parseInt(strippedAmount);
            } catch(NumberFormatException e){
            }
            isUserInput = false;
            setFormattedAmount(amountNumeral,ed.getId());
        }

        public void beforeTextChanged(CharSequence s, int start, int count, int after){
        }
        public void onTextChanged(CharSequence s, int start, int before, int count) {
        }
    };
ed.addTextChangedListener(inputTextWatcher);


ed.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            int length          =   ed.getText().length();
            ed.setCursorVisible(true);
            ed.setSelection(length);
        }
    });

ed.setOnKeyListener(new View.OnKeyListener() {
    @Override
    public boolean onKey(View v, int keyCode, KeyEvent event) {
            if(event.getAction() == KeyEvent.ACTION_UP) 
                return true;
            String strippedAmount = ed.getText().toString().replace(",", "");
            if(keyCode == KeyEvent.KEYCODE_DEL){
                //delete pressed, strip number of comas and then delete least significant digit.
                strippedAmount = strippedAmount.substring(0, strippedAmount.length() - 1);
                int amountNumeral = 0;
                try{
                    amountNumeral = Integer.parseInt(strippedAmount);
                } catch(NumberFormatException e){
                }
                isUserInput = false;
                setFormattedAmount(amountNumeral,ed.getId());
                return true;
            }else if(keyCode == KeyEvent.KEYCODE_ENTER){
                //enter pressed, save edits and resign keyboard
                int amountNumeral = 0;
                try{
                    amountNumeral = Integer.parseInt(strippedAmount);
                } catch(NumberFormatException e){
                }
                isUserInput = false;
                setFormattedAmount(amountNumeral,ed.getId());
                //save edits
                //resign keyboard..
                InputMethodManager in = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
                in.hideSoftInputFromWindow(AppHome.this.getCurrentFocus().getWindowToken(),InputMethodManager.HIDE_NOT_ALWAYS);
                return true;
            }
            return false;
    }
});

私が行ったことは、editTextのonClick()で、現在のEditTextテキストの最後にカーソルを強制的に置き、ユーザーが任意の桁を押したときに同じことを行いました。それが誰かを助けることを願っています..助けようとしたすべての人に感謝します。

于 2011-04-14T03:42:01.540 に答える
4

マスクされた入力の場合、InputFilterをサブクラス化できます

以下は、すべての小文字を大文字にするサンプルのInputFilterサブクラスです。

   /**
     * This filter will capitalize all the lower case letters that are added
     * through edits.
     */
    public static class AllCaps implements InputFilter {
        public CharSequence filter(CharSequence source, int start, int end,
                                   Spanned dest, int dstart, int dend) {
            for (int i = start; i < end; i++) {
                if (Character.isLowerCase(source.charAt(i))) {
                    char[] v = new char[end - start];
                    TextUtils.getChars(source, start, end, v, 0);
                    String s = new String(v).toUpperCase();

                    if (source instanceof Spanned) {
                        SpannableString sp = new SpannableString(s);
                        TextUtils.copySpansFrom((Spanned) source,
                                                start, end, null, sp, 0);
                        return sp;
                    } else {
                        return s;
                    }
                }
            }

            return null; // keep original
        }
    }

上記のコードは、AndroidのInputFilterの実装から取得されています

于 2011-04-13T06:53:35.040 に答える
1

数時間働いた後、私は電話入力マスクを作りました。たとえば、「123456」と入力すると、「+ 1(234)56」に変換されます。任意の位置から任意のシンボルを削除した後、カーソルは開始または終了ではなく、正しい位置に移動します。

活動中:

    etPhone.addTextChangedListener(new PhoneWatcher(etPhone));

クラスで:

private class PhoneWatcher implements TextWatcher {
    private static final String PHONE_MASK = "+# (###) ###-##-##";
    private final char[] PHONE_MASK_ARRAY = PHONE_MASK.toCharArray();

    private boolean isInTextChanged;
    private boolean isInAfterTextChanged;
    private EditText editText;
    private int shiftCursor;
    private String text;
    private int cursor;

    public PhoneWatcher(EditText editText) {
        super();
        this.editText = editText;
        isInTextChanged = false;
        isInAfterTextChanged = false;
    }

    @Override
    public synchronized void beforeTextChanged(CharSequence s, int start, int count, int after) {
        shiftCursor = after - count;
    }

    @Override
    public synchronized void onTextChanged(CharSequence s, int start, int before, int count) {
        if (!isInTextChanged) {
            isInTextChanged = true;

            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < s.length(); i++) {
                char symbol = s.charAt(i);
                if (symbol >= '0' && symbol <= '9')
                    sb.append(symbol);
            }
            String digits = sb.toString();

            sb.setLength(0);
            int j = 0;
            for (int i = 0; i < digits.length(); i++) {
                char digit = digits.charAt(i);
                while (j < PHONE_MASK_ARRAY.length) {
                    if (PHONE_MASK_ARRAY[j] == '#') {
                        sb.append(digit);
                        j++;
                        break;
                    } else {
                        sb.append(PHONE_MASK_ARRAY[j]);
                        j++;
                    }
                }
            }

            cursor = editText.getSelectionStart();
            text = sb.toString();

            if (shiftCursor > 0) {
                if (cursor > text.length())
                    cursor = text.length();
                else {
                    while (cursor < PHONE_MASK_ARRAY.length && PHONE_MASK_ARRAY[cursor - 1] != '#') {
                        cursor++;
                    }
                }
            } else if (shiftCursor < 0) {
                while (cursor > 0 && PHONE_MASK_ARRAY[cursor - 1] != '#') {
                    cursor--;
                }
            }
        }
    }

    public synchronized void afterTextChanged(Editable s) {
        if (!isInAfterTextChanged) {
            isInAfterTextChanged = true;

            editText.setText(text);
            editText.setSelection(cursor);

            isInTextChanged = false;
            isInAfterTextChanged = false;
        }
    }
}
于 2015-12-17T14:21:48.857 に答える