引言
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):

可以很清晰的看到我們的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:

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

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

看到左上角的文字了嘛,這是我們在之前設(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來了解以上這些屬性,簡單起見我們就做一個登錄界面,這個界面長這樣:

先上布局文件:
<?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:counterEnable和app: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博客剪貼過來都不會自動換行~~~求告知