Android Design Support Library--TextInputLayout的使用

引言

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

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

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

<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>

有些人可能會(huì)奇怪,之前說(shuō)好的TextInputLayout 是用來(lái)包裹EditText 的盔几,為什么這里出現(xiàn)了TextInputEditText 晴弃,先別急,我們看一下谷歌官方對(duì)這個(gè)控件的描述:

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.

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

1.png

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

2.png

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

3.png

看到左上角的文字了嘛珊随,這是我們?cè)谥霸O(shè)置的android:hint屬性的值,這么一看這兩者的區(qū)別的就一目了然了柿隙,但是說(shuō)實(shí)話TextInputEditText
用到的地方還是很有限的叶洞,所以日常開發(fā)我們還是使用TextInputLayout
去包裹EditText來(lái)實(shí)現(xiàn)浮動(dòng)標(biāo)簽的功能。

以上圖片出自 感謝萬(wàn)能的stackoverflow

接下來(lái)看看TextInputLayout里面有什么方法

因?yàn)樗抢^承自LinearLayout的所以理論上LinearLayout有的屬性它全都有禀崖,這里我們只看有關(guān)它本身的屬性:

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

這里我們通過(guò)一個(gè)簡(jiǎn)單的Demo來(lái)了解以上這些屬性,簡(jiǎn)單起見我們就做一個(gè)登錄界面财饥,這個(gè)界面長(zhǎng)這樣:

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;
}
   //動(dòng)態(tài)監(jiān)聽輸入過(guò)程
    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;
            } 
       }
    }
}

先來(lái)看一下最終的實(shí)現(xiàn)效果:

最終效果

可以很明顯的看到换吧,當(dāng)我們同時(shí)設(shè)置了app:counterEnableapp:counterMaxLength屬性時(shí),我們輸入的EditText右下角會(huì)出現(xiàn)一個(gè)計(jì)數(shù)器還有一個(gè)最大輸入字符數(shù)的數(shù)字顯示钥星,我們?cè)谳斎朊诌@一欄設(shè)置最大輸入為5個(gè)字符,所以當(dāng)超過(guò)了5個(gè)字符的時(shí)候满着,EditText的整個(gè)樣式的顏色都會(huì)改變以示警告谦炒,如果我們只設(shè)置了app:counterEnabled屬性的話EditText右下角一開始會(huì)出現(xiàn)一個(gè)0,隨著輸入字符的增多而逐步進(jìn)行計(jì)數(shù)风喇,注意如果設(shè)置了整個(gè)屬性我們EditText布局的高度會(huì)有一定的增大宁改,具體的可以自己實(shí)踐一下。
另外魂莫,我們?cè)诖a中設(shè)置了不同的餓輸入類型还蹲,如果輸入類型錯(cuò)誤,我們就可以通過(guò)設(shè)置app:errorEnabled來(lái)開啟錯(cuò)誤顯示耙考,此時(shí)需要通過(guò)在代碼中調(diào)用setError(string)方法來(lái)設(shè)置顯示的錯(cuò)誤提示文字谜喊,當(dāng)不需要的時(shí)候記得設(shè)置app:errorEnabled(false)來(lái)取消錯(cuò)誤提示,不然錯(cuò)誤提示會(huì)一直存在倦始。
注意: 當(dāng)我們使用app:counterMaxLength這個(gè)屬性的時(shí)候斗遏,一定要設(shè)置 app:counterOverflowTextAppearance屬性,不然的話程序運(yùn)行會(huì)報(bào)錯(cuò)鞋邑,這個(gè)屬性是設(shè)置當(dāng)我們輸入字符超過(guò)限定的個(gè)數(shù)時(shí)候EditText控件整體顯示的樣式诵次,需要在style.xml文件里面定義一個(gè)自己的style,注意我們自定義的style的parent是TextAppearance.AppCompat.Small枚碗,拿我上面的程序舉例:

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

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

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

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

  • 如果你想更改下劃線的顏色,只要在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屬性就行了.

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

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

app:errorTextAppearance="@style/MyErrorStyle"

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

最后

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

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

有任何問(wèn)題歡迎留言探討~

對(duì)了捻撑,誰(shuí)能告訴我在簡(jiǎn)書插入代碼塊為何不會(huì)自動(dòng)換行,我手動(dòng)的好辛苦缤底,從編輯器或者從我的CSDN博客剪貼過(guò)來(lái)都不會(huì)自動(dòng)換行~~~求告知

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末顾患,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子个唧,更是在濱河造成了極大的恐慌江解,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件徙歼,死亡現(xiàn)場(chǎng)離奇詭異犁河,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)魄梯,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門桨螺,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人画恰,你說(shuō)我怎么就攤上這事彭谁。” “怎么了允扇?”我有些...
    開封第一講書人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵缠局,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我考润,道長(zhǎng)狭园,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任糊治,我火速辦了婚禮唱矛,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘井辜。我一直安慰自己绎谦,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開白布粥脚。 她就那樣靜靜地躺著窃肠,像睡著了一般。 火紅的嫁衣襯著肌膚如雪刷允。 梳的紋絲不亂的頭發(fā)上冤留,一...
    開封第一講書人閱讀 51,125評(píng)論 1 297
  • 那天碧囊,我揣著相機(jī)與錄音,去河邊找鬼纤怒。 笑死糯而,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的泊窘。 我是一名探鬼主播熄驼,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼州既!你這毒婦竟也來(lái)了谜洽?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤吴叶,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后序臂,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蚌卤,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年奥秆,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了逊彭。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡眯亦,死狀恐怖纬凤,靈堂內(nèi)的尸體忽然破棺而出嚎花,到底是詐尸還是另有隱情,我是刑警寧澤囊榜,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站亥宿,受9級(jí)特大地震影響卸勺,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜烫扼,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一曙求、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧映企,春花似錦悟狱、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至豆赏,卻和暖如春挣菲,著一層夾襖步出監(jiān)牢的瞬間富稻,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來(lái)泰國(guó)打工白胀, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留椭赋,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓或杠,卻偏偏與公主長(zhǎng)得像哪怔,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子向抢,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容