今天來聊聊TextWatcher~

TextWatcher是google在android.text之下提供的一個方法

https://developer.android.com/reference/android/text/TextWatcher

簡單來說他的功能是即時監控EditText輸入狀況的一個方法

像是想要限制只能輸入到小數點以下幾位、或者限制輸入範圍並即時反饋

都可以在這個方法中實踐(´・з・)

今天的範例將利用此功能特性,即時計算出十進位轉為各種進位的應用

以下為範例

以及GitHub

https://github.com/thumbb13555/EditTextTextWatcherExample

OK,開始


本次功能描述:

1.輸入10進制數值時即時轉為其他進制並顯示

2.輸入範圍僅-500~+500

3.輸入開頭不可為0

4.超過超入範圍即立即修正為最大值或最小值

 

在開始之前要先了解這項功能以及所附帶的三個複寫

首先先畫好layout

https://github.com/thumbb13555/EditTextTextWatcherExample/blob/master/app/src/main/res/layout/activity_main.xml

在Layout設計上,EditText中有設置幾個EditText常用的方法

android:digits="-1234567890"

表示限制輸入只能為以上所輸入的這些

android:inputType="numberDecimal|numberSigned"

表示輸入限制為數字以及可輸入小數點

至於其他三個EditText我則是在xml中把他設置為不可點擊的狀態

方法則是

android:focusable="false"

另外,inputType也可以在程式中設置

edInput.setInputType(InputType.TYPE_NUMBER_FLAG_DECIMAL|InputType.TYPE.NUMBER_FLAG_SIGNED);

 

OK回到程式

首先要先來處理轉進制的問題

老實講我覺得Java中將別的進制轉過來10進制麻煩

但是要把10進制轉為其他進制卻都是一行可以搞定的事情(´・д・`)

轉換時只要這樣(紅字)就可以了

int getVal = Integer.parseInt(String.valueOf(100));
edBinary.setText(Integer.toBinaryString(getVal));//轉二進
edOctal.setText(Integer.toOctalString(getVal));//轉八進
edHex.setText(Integer.toHexString(getVal));//轉十六進

 

一如既往地先在onCreate內抓好元件後,就可以來做今天的重點了

先po一下onCreate內準備工作

public class MainActivity extends AppCompatActivity {
    EditText edInput,edBinary,edOctal,edHex;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        edInput = findViewById(R.id.edInput);
        edBinary= findViewById(R.id.edBinary);
        edOctal = findViewById(R.id.edOctal);
        edHex   = findViewById(R.id.edHex);
     }
}

然後一樣在onCreate內,打上這行

edInput.addTextChangedListener(new TextWatcher() {...}

按下TextWatcher後,就會跳出我們要的東西囉

跳出來的東西如下

 

edInput.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) {

    }
});

總共會跳出三個回調

分別是

beforeTextChanged

onTextChanged

afterTextChanged

如果單從字面上的意思解讀,就是

文字變更之前

文字變更

文字變更

好我知道還是聽得霧颯颯..(・ω・`)………..

於是我們來做一個實驗

我在所有方法中都放一個Log來觀察他值變化的狀況

首先當我們在沒有輸入時輸入"4"

可以看見輸入前的s紀錄了"",因為原本是還沒輸入的

紀錄中的s=4, 以及修改會結果為4

再來輸入一個“2”

現在畫面中顯示是“42”了

然後再一個”3“

現在是"423"

現在我們刪掉最後的"3"

現在是"42"

最後我們刪掉"42"中的"4"

現在畫面是"2"了

分析過程就不再贅述,直接講結論

在beforeTextChanged中

s代表是修改(輸入)以前的數值

start代表是發生修改的位置

count是發生修改的時的文字長度,新增輸入時回傳0

after則是修改後的字串長度,刪除字串時回傳0

 

在onTextChanged中

s代表變更後的字串

start代表被變更的序號

before代表被改變的字串長度,新增時回傳0

count為添加字符的長度,如果是刪除的話回傳0

 

最後afterTextChanged就單純很多

s就是修改後的文字

 

最後我要分享一個踩過的坑

而且搞笑的是這是在官網中所寫過的注意事項..

蛤?你問我什麼意思(╭ರ_⊙)

一言以敝之就是

因為這兩個複寫方法會即時偵測EditText內的數值變更

因此如果這時候你在這個複寫內去修改本來的EditText的值的話

那你就會陷入無窮迴圈,導致當機。

雖然我不是照著英文翻給你聽,但是就是這麼一回事

於是我來採坑給你看你就知道我在說什麼啦(*-ω-)

首先我來在beforeTexttChanged內加入edInput.setText();

然後看著LogCat執行..

喔淦(✿・∀・)┌∩┐

醬子有沒有理解了一咪咪呢XDDDDDDD

 

好的,坑先踩到這邊,我們繼續

再來的工作是要先設置即時顯示的值

於是我在onTextChanged寫

edInput.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) {
                
             
                    int getVal = Integer.parseInt(String.valueOf(s));
                    edBinary.setText(Integer.toBinaryString(getVal));
                    edOctal.setText(Integer.toOctalString(getVal));
                    edHex.setText(Integer.toHexString(getVal));

            }
            @Override
            public void afterTextChanged(Editable s) {
               
            }
        });

好的來測試吧XD

看起來是沒問題...喔淦 凸(✿・∀・)凸

閃退惹 (´・д・`)

來看一下LogCat

恩...數字格式錯誤因為輸入值為""

好ㄅ,只好來解決問題了

我的方法是索性加入try-catch,直接給他去錯這樣

於是我的程式變成這樣

edInput.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) {
             
                try {
                    int getVal = Integer.parseInt(String.valueOf(s));
                    edBinary.setText(Integer.toBinaryString(getVal));
                    edOctal.setText(Integer.toOctalString(getVal));
                    edHex.setText(Integer.toHexString(getVal));

                }catch (Exception e){
                    edBinary.setText("");
                    edOctal.setText("");
                    edHex.setText("");
                }


            }
            @Override
            public void afterTextChanged(Editable s) {
                

            }
        });

醬子就完成囉(`・ω・´)b

接下來我們要來完成功能2,限制輸入範圍

上面有提到我們不能夠在beforeTextChanged以及onTextChanged內做任何edInput的更動

那怎模辦?所以只能找剩下的啦~~

沒錯~就是寫在afterTextChanged內٩(•౪•٩)三

 edInput.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) {
               
                try {
                    int getVal = Integer.parseInt(String.valueOf(s));
                    edBinary.setText(Integer.toBinaryString(getVal));
                    edOctal.setText(Integer.toOctalString(getVal));
                    edHex.setText(Integer.toHexString(getVal));

                }catch (Exception e){
                    edBinary.setText("");
                    edOctal.setText("");
                    edHex.setText("");
                }


            }
            @Override
            public void afterTextChanged(Editable s) {
               
                try {
                    int getInput = Integer.parseInt(String.valueOf(s));
                    if (getInput>500){
                        edInput.setText("500");
                        edInput.setSelection(s.length());
                    }else if (getInput<-500){
                        edInput.setText("-500");
                        edInput.setSelection(s.length());
                    }
                  
                }catch (Exception e){
                    edBinary.setText("");
                    edOctal.setText("");
                    edHex.setText("");
                }

            }
        });

至於第四項要求只要在條件是內設定setText就可以囉!

至於最後的那個edInput.setSelection(s.length());則是控制光標的位置

好的,最後來完成第三項

有時候齁...不管你程式再怎麼用心就是總有那麼幾個人能夠超越86

像這樣

欸欸工程師~你可以限制讓客人不要亂輸入嗎˚▱˚

...XXX好好的輸入是不會膩!一定要這麼智障嗎?

好啦就結論而言我並沒有解決掉畫面中那種輸入

但是我現在講的邏輯延伸是可以

第三項要求:數字前不可有零

方法很簡單,簡單來說就是偵測並取代

於是我的程式長這樣

 edInput.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
                Log.d("MyTAG", "beforeTextChanged: s= "+s+", start= "
                        +start+", count= "+count+", after= "+after);

            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
                Log.d("MyTAG", "onTextChanged: s= "+s+", start= "
                        +start+", before= "+before+", count= "+count);
                try {
                    int getVal = Integer.parseInt(String.valueOf(s));
                    edBinary.setText(Integer.toBinaryString(getVal));
                    edOctal.setText(Integer.toOctalString(getVal));
                    edHex.setText(Integer.toHexString(getVal));

                }catch (Exception e){
                    edBinary.setText("");
                    edOctal.setText("");
                    edHex.setText("");
                }


            }
            @Override
            public void afterTextChanged(Editable s) {
                Log.d("MyTAG", "afterTextChanged: s= "+s);
                Log.d("MyTAG", "=========分隔線=========");
                try {
                    int getInput = Integer.parseInt(String.valueOf(s));
                    if (getInput>500){
                        edInput.setText("500");
                        edInput.setSelection(s.length());
                    }else if (getInput<-500){
                        edInput.setText("-500");
                        edInput.setSelection(s.length());
                    }

                    if (s.toString().length() > 1 && s.toString().startsWith("0")) {
                        s.replace(0,1,"");
                    }

                }catch (Exception e){
                    edBinary.setText("");
                    edOctal.setText("");
                    edHex.setText("");
                }

            }
        });

條件:如果輸入長度>0而且開頭第一個字為"0"的話

就將第零位替換掉變成""

 

就這樣而已~沒了XD

很簡單吧哈哈


結論:

TextWatcher雖然不是個很常用的元件,但是卻是在專業APP中的常用元件之一

一個好的APP操作體驗佳是很重要滴...我是這麼認為啦

這個功能再結合進制轉換以及加減乘除

就可以完成像計算機一樣的APP了!

那今天的介紹到這!掰逼~

arrow
arrow

    碼農日常 發表在 痞客邦 留言(1) 人氣()