引言
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):
可以很清晰的看到我們的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
:
我們看到下面那里輸入框已經(jīng)很大了骗露,然后你點(diǎn)擊輸入框進(jìn)行輸入岭佳,會(huì)發(fā)現(xiàn)這個(gè)現(xiàn)象:
你進(jìn)入到了全屏模式輸入,但是界面上空空如也萧锉,對(duì)比一下使用
TextInputEditText
的情況:
看到左上角的文字了嘛珊随,這是我們?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)這樣:
先上布局文件:
<?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:counterEnable
和app: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)換行~~~求告知