Android Design Support Library--TextInputLayout的使用

引言

Google在2015的IO大會上,給我們帶來了更加詳細(xì)的Material Design設(shè)計規(guī)范,同時,也給我們帶來了全新的Android Design Support Library,Android Design Support Library的兼容性更廣,直接可以向下兼容到Android 2.2,我準(zhǔn)備從最簡單的控件開始,逐漸延伸,把新控件都給熟悉一遍。
先從看起來最簡單的控件開始,也就是TextInputLayout,說實話TextInputLayout
我所見到的平常用的并不多,它的大體作用是在我們正常的EditText左上角顯示出一個浮動標(biāo)簽,這個標(biāo)簽的內(nèi)容就是我們設(shè)置的android:hint
屬性的值。 先來看一下它的繼承結(jié)構(gòu):

繼承結(jié)構(gòu).png

可以很清晰的看到我們的TextInputLayout
繼承于LinearLayout
,那么很明顯這是一個布局,需要配合它的子控件來顯示出想要的效果,這里谷歌把它專門設(shè)計用來包裹EditText
(或者EditText
的子類),然后當(dāng)用戶進(jìn)行輸入動作的時候我們設(shè)置的android:hint
提示就會以動畫的形式運動到左上角,谷歌官方提供的最簡單的使用示例如下:

<android.support.design.widget.TextInputLayout 
                android:layout_width="match_parent" 
                android:layout_height="wrap_content"> 

<android.support.design.widget.TextInputEditText 
                android:layout_width="match_parent" 
                android:layout_height="wrap_content" 
                android:hint="@string/form_username"/> 
</android.support.design.widget.TextInputLayout>

有些人可能會奇怪,之前說好的TextInputLayout 是用來包裹EditText 的,為什么這里出現(xiàn)了TextInputEditText ,先別急,我們看一下谷歌官方對這個控件的描述:

A special sub-class of EditText designed for use as a child of 
TextInputLayout.Using this class allows us to display a hint
in the IME when in 'extract' mode.

大意是說,這只是一種特殊的EditText的子類,用來在'extract' mode下在輸入法編輯器中顯示我們的hint提示信息,這里的'extract' mode其實就是全屏模式,谷歌官方對它的解釋是有時候你的輸入框的UI界面很大,大的不能與你自己的應(yīng)用程序的UI結(jié)合起來,這時候就可以切換到全屏模式來輸入,這么說可能不太明白,上圖: 比如說,下面這種情況使用的是EditText

1.png

我們看到下面那里輸入框已經(jīng)很大了,然后你點擊輸入框進(jìn)行輸入,會發(fā)現(xiàn)這個現(xiàn)象:

2.png

你進(jìn)入到了全屏模式輸入,但是界面上空空如也,對比一下使用TextInputEditText
的情況:

3.png

看到左上角的文字了嘛,這是我們在之前設(shè)置的android:hint屬性的值,這么一看這兩者的區(qū)別的就一目了然了,但是說實話TextInputEditText
用到的地方還是很有限的,所以日常開發(fā)我們還是使用TextInputLayout
去包裹EditText來實現(xiàn)浮動標(biāo)簽的功能。

以上圖片出自 感謝萬能的stackoverflow

接下來看看TextInputLayout里面有什么方法

因為它是繼承自LinearLayout的所以理論上LinearLayout有的屬性它全都有,這里我們只看有關(guān)它本身的屬性:

屬性名 相關(guān)方法 描述
app:counterEnabled setCounterEnabled(boolean) 設(shè)置是否顯示一個計數(shù)器,布爾值
app:counterMaxLength setCounterMaxLength(int) 設(shè)置計數(shù)器的最大計數(shù)數(shù)值,整型
app:errorEnabled setErrorEnabled(boolean) 設(shè)置是否顯示一個錯誤信息,布爾值
app:hintAnimationEnabled setHintAnimationEnabled(boolean) 設(shè)置是否要顯示輸入狀態(tài)時候的動畫效果,布爾值
app:hintEnabled setHintEnabled(boolean) 設(shè)置是否要用這個浮動標(biāo)簽的功能,布爾值
app:hintTextAppearance setHintTextAppearance(int) 設(shè)置提示文字的樣式(注意這里是運行了動畫效果之后的樣式)

這里我們通過一個簡單的Demo來了解以上這些屬性,簡單起見我們就做一個登錄界面,這個界面長這樣:

Demo

先上布局文件:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 
xmlns:app="http://schemas.android.com/apk/res-auto" 
xmlns:tools="http://schemas.android.com/tools" 
android:layout_width="match_parent" 
android:layout_height="match_parent" 
android:paddingLeft="@dimen/activity_horizontal_margin" 
android:paddingRight="@dimen/activity_horizontal_margin" 
tools:context="com.test.textinputlayoutdemo.MainActivity"> 
<LinearLayout android:layout_width="match_parent" 
android:layout_height="match_parent" 
android:layout_marginTop="65dp" android:orientation="vertical"> 
<android.support.design.widget.TextInputLayout 
android:id="@+id/layout_name" 
android:layout_width="match_parent" 
android:layout_height="wrap_content" app:counterEnabled="true" 
app:counterMaxLength="5" 
app:counterOverflowTextAppearance="@style/MyOverflowText" 
app:errorTextAppearance="@style/MyErrorStyle"> <EditText 
android:id="@+id/input_name" 
android:layout_width="match_parent" 
android:layout_height="wrap_content" 
android:hint="@string/EnterName" android:singleLine="true" /> 
</android.support.design.widget.TextInputLayout> 
<android.support.design.widget.TextInputLayout 
android:id="@+id/layout_password" 
android:layout_width="match_parent" 
android:layout_height="wrap_content" app:counterEnabled="true" 
app:counterMaxLength="11" 
app:counterOverflowTextAppearance="@style/MyOverflowText" 
app:errorTextAppearance="@style/MyErrorStyle"> <EditText 
android:id="@+id/input_password" 
android:layout_width="match_parent" 
android:layout_height="wrap_content" 
android:hint="@string/EnterPassWord" 
android:inputType="textPassword" android:singleLine="true" /> 
</android.support.design.widget.TextInputLayout> 
<android.support.design.widget.TextInputLayout 
android:id="@+id/layout_email" 
android:layout_width="match_parent" 
android:layout_height="wrap_content" 
app:counterOverflowTextAppearance="@style/MyOverflowText" 
app:errorTextAppearance="@style/MyErrorStyle"> <EditText 
android:id="@+id/input_email" 
android:layout_width="match_parent" 
android:layout_height="wrap_content" 
android:hint="@string/EnterEmail" 
android:inputType="textEmailAddress" /> 
</android.support.design.widget.TextInputLayout> <Button 
android:id="@+id/login" android:layout_width="match_parent" 
android:layout_height="wrap_content" 
android:layout_marginTop="50dp" 
android:background="@color/colorPrimary" 
android:text="@string/login" android:textColor="#ffffff" 
android:textSize="20sp" android:textStyle="bold" /> 
</LinearLayout></RelativeLayout>

代碼如下:

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
            private EditText input_name, input_password, input_email;
            private TextInputLayout layout_name, layout_password, layout_email;    
            private Button btn_login;
            @Override    
            protected void onCreate(Bundle savedInstanceState) {  
                super.onCreate(savedInstanceState);        
                setContentView(R.layout.activity_main);
                initWidget();
            }    
            private void initWidget() {
                input_name = (EditText) findViewById(R.id.input_name);
                input_password = (EditText) findViewById(R.id.input_password);
                input_email = (EditText) findViewById(R.id.input_email);        
                layout_name = (TextInputLayout) findViewById(R.id.layout_name);        
                layout_password = (TextInputLayout) findViewById(R.id.layout_password);
                layout_email = (TextInputLayout) findViewById(R.id.layout_email);
                btn_login = (Button) findViewById(R.id.login);
                btn_login.setOnClickListener(this);        //添加監(jiān)聽        
                input_name.addTextChangedListener(new MyTextWatcher(input_name));
                input_password.addTextChangedListener(new MyTextWatcher(input_password));        
                input_email.addTextChangedListener(new MyTextWatcher(input_email));
                  }
                 @Override    public void onClick(View v) {
                                      switch (v.getId()) {
                                            case R.id.login:
                                                    canLogin();
                                                            break;
                                                          default:
                                                            break;
                                                  }
                                          }    
                            /**     * 判斷是否可以登錄的方法     */    
             private void canLogin() {
    if (!isNameValid()) {
        Toast.makeText(this, getString(R.string.check), Toast.LENGTH_SHORT).show();
        return;
    }
    if (!isPasswordValid()) {
        Toast.makeText(this, getString(R.string.check), Toast.LENGTH_SHORT).show();
        return;    }
    if (!isEmailValid()) {
        Toast.makeText(this, getString(R.string.check), Toast.LENGTH_SHORT).show();
        return;    }
    Toast.makeText(this, getString(R.string.login_success), Toast.LENGTH_SHORT).show();}

public boolean isNameValid() {
    if (input_name.getText().toString().trim().equals("") ||
 input_name.getText().toString().trim().isEmpty()) {
        layout_name.setError(getString(R.string.error_name));
        input_name.requestFocus();
        return false;
    }
    layout_name.setErrorEnabled(false);
    return true;}public boolean isPasswordValid() {
    if (input_password.getText().toString().trim().equals("") ||
 input_password.getText().toString().trim().isEmpty()) {
        layout_password.setErrorEnabled(true);
        layout_password.setError(getResources().getString(R.string.error_password));
        input_password.requestFocus();
        return false;
    }
    layout_password.setErrorEnabled(false);
    return true;}public boolean isEmailValid() {
    String email = input_email.getText().toString().trim();
    if (TextUtils.isEmpty(email) || !android.util.Patterns.EMAIL_ADDRESS.matcher(email).matches()) { 
       layout_email.setErrorEnabled(true); 
       layout_email.setError(getString(R.string.error_email));        
layout_email.requestFocus();
        return false;
    }
    layout_email.setErrorEnabled(false);
    return true;
}
   //動態(tài)監(jiān)聽輸入過程
    private class MyTextWatcher implements TextWatcher {
        private View view;
        private MyTextWatcher(View view) {
            this.view = view;
        }
        @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) {
            switch (view.getId()) {
                case R.id.input_name:
                    isNameValid();
                    break;
                case R.id.input_password:
                    isPasswordValid();
                    break;
                case R.id.input_email:
                    isEmailValid();
                    break;
            } 
       }
    }
}

先來看一下最終的實現(xiàn)效果:

最終效果

可以很明顯的看到,當(dāng)我們同時設(shè)置了app:counterEnableapp:counterMaxLength屬性時,我們輸入的EditText右下角會出現(xiàn)一個計數(shù)器還有一個最大輸入字符數(shù)的數(shù)字顯示,我們在輸入名字這一欄設(shè)置最大輸入為5個字符,所以當(dāng)超過了5個字符的時候,EditText的整個樣式的顏色都會改變以示警告,如果我們只設(shè)置了app:counterEnabled屬性的話EditText右下角一開始會出現(xiàn)一個0,隨著輸入字符的增多而逐步進(jìn)行計數(shù),注意如果設(shè)置了整個屬性我們EditText布局的高度會有一定的增大,具體的可以自己實踐一下。
另外,我們在代碼中設(shè)置了不同的餓輸入類型,如果輸入類型錯誤,我們就可以通過設(shè)置app:errorEnabled來開啟錯誤顯示,此時需要通過在代碼中調(diào)用setError(string)方法來設(shè)置顯示的錯誤提示文字,當(dāng)不需要的時候記得設(shè)置app:errorEnabled(false)來取消錯誤提示,不然錯誤提示會一直存在。
注意: 當(dāng)我們使用app:counterMaxLength這個屬性的時候,一定要設(shè)置 app:counterOverflowTextAppearance屬性,不然的話程序運行會報錯,這個屬性是設(shè)置當(dāng)我們輸入字符超過限定的個數(shù)時候EditText控件整體顯示的樣式,需要在style.xml文件里面定義一個自己的style,注意我們自定義的style的parent是TextAppearance.AppCompat.Small,拿我上面的程序舉例:

 <style name="MyOverflowText" parent="TextAppearance.AppCompat.Small"> 
 <item name="android:textColor">#f3672b</item> </style>

這樣定義好后再在app:counterOverflowTextAppearance里面設(shè)置這個style就行

關(guān)于自定義樣式

有些人可能不喜歡官方提供的默認(rèn)樣式想要自己定義,下面說一下自定義幾種樣式的方法:

  • 如果你想更改下劃線的顏色,只要在style.xml文件里面找到AppTheme:
 <style name="AppTheme"parent="Theme.AppCompat.Light.DarkActionBar"> 
<!-- Customize your theme here. -->
 <item name="colorPrimary">@color/colorPrimary</item>
 <item name="colorPrimaryDark">@color/colorPrimaryDark</item> 
<item name="colorAccent">@color/colorAccent</item> </style>

更改里面的colorAccent屬性就行了.

  • 如果你想更改錯誤提示的樣式的話,也是在style.xml文件里面,自定義一個style,同樣拿上面的程序舉例:
<style name="MyErrorStyle"> 
<item name="android:textColor">#ec4722</item> </style>

然后在xml文件TextInputLayout控件里面這么設(shè)置一下就行了:

app:errorTextAppearance="@style/MyErrorStyle"

包括前面提到的設(shè)置當(dāng)輸入字符大于我們限定個數(shù)字符時的樣式,基本上我們可以很好地自定義出自己想要的style了,以上兩種不提供演示,都很簡單,可以自己去嘗試。

最后

下一次準(zhǔn)備分析SnackBar控件,很多東西說簡單也簡單,說不簡單也不簡單,就像做這個Demo我之前光看官方文檔根本沒有告訴有app:counterOverflowTextAppearance
這個屬性的存在,也是一直查資料,還是要親自去嘗試一下才好,下面上源碼(注意是AS文件)
參考:Android Material Design Floating Labels for EditText
Demo地址
項目GitHub地址
最后來個小提示,當(dāng)我們在Android Studio中導(dǎo)入support design開發(fā)包的時候,版本號最好和v7包的版本號一致,不然有些時候會出現(xiàn)莫名其妙的錯誤:

compile fileTree(dir: 'libs', include: ['*.jar']) compile 
'com.android.support:appcompat-v7:23.0.1' compile 
'com.android.support:design:23.0.1'

有任何問題歡迎留言探討~

對了,誰能告訴我在簡書插入代碼塊為何不會自動換行,我手動的好辛苦,從編輯器或者從我的CSDN博客剪貼過來都不會自動換行~~~求告知

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容