Android Design Support Library--TextInputLayout的使用

丈夫志四海,万里犹比邻。这篇文章主要讲述Android Design Support Library--TextInputLayout的使用相关的知识,希望能为你提供帮助。

本篇文章已授权微信公众号 guolin_blog ( 郭霖) 独家发布
引言
Google在2015的IO大会上, 给我们带来了更加详细的Material Design设计规范, 同时, 也给我们带来了全新的android Design Support Library, Android Design Support Library的兼容性更广, 直接可以向下兼容到Android 2.2, 我准备从最简单的控件开始, 逐渐延伸, 把新控件都给熟悉一遍。
先从看起来最简单的控件开始, 也就是TextInputLayout, 说实话TextInputLayout 我所见到的平常用的并不多, 它的大体作用是在我们正常的EditText左上角显示出一个浮动标签, 这个标签的内容就是我们设置的android:hint 属性的值。
先来看一下它的继承结构:
Android Design Support Library--TextInputLayout的使用

文章图片

可以很清晰的看到我们的TextInputLayout 继承于LinearLayout , 那么很明显这是一个布局, 需要配合它的子控件来显示出想要的效果, 这里谷歌把它专门设计用来包裹EditText(或者EditText的子类), 然后当用户进行输入动作的时候我们设置的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 的, 为什么这里出现了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界面很大, 大的不能与你自己的应用程序的UI结合起来, 这时候就可以切换到全屏模式来输入, 这么说可能不太明白, 上图:
比如说, 下面这种情况使用的是EditText
Android Design Support Library--TextInputLayout的使用

文章图片

我们看到下面那里输入框已经很大了, 然后你点击输入框进行输入, 会发现这个现象:
Android Design Support Library--TextInputLayout的使用

文章图片

你进入到了全屏模式输入, 但是界面上空空如也, 对比一下使用TextInputEditText 的情况:
Android Design Support Library--TextInputLayout的使用

文章图片

看到左上角的文字了嘛, 这是我们在之前设置的android:hint 属性的值, 这么一看这两者的区别的就一目了然了, 但是说实话TextInputEditText 用到的地方还是很有限的, 所以日常开发我们还是使用TextInputLayout 去包裹EditText 来实现浮动标签的功能。
以上图片出自 感谢万能的stackoverflow
接下来看看TextInputLayout里面有什么方法
因为它是继承自LinearLayout的所以理论上LinearLayout 有的属性它全都有, 这里我们只看有关它本身的属性:
属性名相关方法描述
app:counterEnabledsetCounterEnabled(boolean)设置是否显示一个计数器, 布尔值
app:counterMaxLengthsetCounterMaxLength(int)设置计数器的最大计数数值, 整型
app:errorEnabledsetErrorEnabled(boolean)设置是否显示一个错误信息, 布尔值
app:hintAnimationEnabledsetHintAnimationEnabled(boolean)设置是否要显示输入状态时候的动画效果, 布尔值
app:hintEnabledsetHintEnabled(boolean)设置是否要用这个浮动标签的功能, 布尔值
app:hintTextAppearancesetHintTextAppearance(int)设置提示文字的样式(注意这里是运行了动画效果之后的样式)
这里我们通过一个简单的Demo来了解以上这些属性, 简单起见我们就做一个登录界面, 这个界面长这样:
Android Design Support Library--TextInputLayout的使用

文章图片

先上布局文件:
< ?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); //添加监听 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; }//动态监听输入过程 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; }} } }

先来看一下最终的实现效果:
Android Design Support Library--TextInputLayout的使用

文章图片

可以很明显的看到, 当我们同时设置了app:counterEnabledapp:counterMaxLength 属性时, 我们输入的EditText 右下角会出现一个计数器还有一个最大输入字符数的数字显示, 我们在输入名字这一栏设置最大输入为5个字符, 所以当超过了5个字符的时候, EditText 的整个样式的颜色都会改变以示警告, 如果我们只设置了app:counterEnabled 属性的话EditText 右下角一开始会出现一个0, 随着输入字符的增多而逐步进行计数, 注意如果设置了整个属性我们EditText 布局的高度会有一定的增大, 具体的可以自己实践一下。
另外, 我们在代码中设置了不同的饿输入类型, 如果输入类型错误, 我们就可以通过设置app:errorEnabled 来开启错误显示, 此时需要通过在代码中调用 setError(string) 方法来设置显示的错误提示文字, 当不需要的时候记得设置app:errorEnabled(false) 来取消错误提示, 不然错误提示会一直存在。
注意: 当我们使用app:counterMaxLength 这个属性的时候, 一定要设置 app:counterOverflowTextAppearance 属性, 不然的话程序运行会报错, 这个属性是设置当我们输入字符超过限定的个数时候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 里面设置这个style就行
关于自定义样式
有些人可能不喜欢官方提供的默认样式想要自己定义, 下面说一下自定义几种样式的方法:
  • 如果你想更改下划线的颜色, 只要在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控件里面这么设置一下就行了:
app:errorTextAppearance= " @ style/MyErrorStyle"

包括前面提到的设置当输入字符大于我们限定个数字符时的样式, 基本上我们可以很好地自定义出自己想要的style了, 以上两种不提供演示, 都很简单, 可以自己去尝试。
最后
下一次准备分析SnackBar控件, 很多东西说简单也简单, 说不简单也不简单, 就像做这个Demo我之前光看官方文档根本没有告诉有app:counterOverflowTextAppearance 这个属性的存在, 也是一直查资料, 还是要亲自去尝试一下才好, 下面上源码(注意是AS文件)
参考: Android Material Design Floating Labels for EditText
Demo地址
项目GitHub地址
最后来个小提示, 当我们在Android Studio中导入support design开发包的时候, 版本号最好和v7包的版本号一致, 不然有些时候会出现莫名其妙的错误:
compile fileTree(dir: ' libs' , include: [' *.jar' ]) compile ' com.android.support:appcompat-v7:23.0.1' compile ' com.android.support:design:23.0.1'

有任何问题欢迎留言探讨~
【Android Design Support Library--TextInputLayout的使用】
Android Design Support Library--TextInputLayout的使用

文章图片


    推荐阅读