Android Design Support Library--TextInputLayout的使用

引言

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

继承结构.png

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

1.png

我们看到下面那里输入框已经很大了,然后你点击输入框进行输入,会发现这个现象:

2.png

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

3.png

看到左上角的文字了嘛,这是我们在之前设置的android:hint属性的值,这么一看这两者的区别的就一目了然了,但是说实话TextInputEditText
用到的地方还是很有限的,所以日常开发我们还是使用TextInputLayout
去包裹EditText来实现浮动标签的功能。

以上图片出自 感谢万能的stackoverflow

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

因为它是继承自LinearLayout的所以理论上LinearLayout有的属性它全都有,这里我们只看有关它本身的属性:

属性名 相关方法 描述
app:counterEnabled setCounterEnabled(boolean) 设置是否显示一个计数器,布尔值
app:counterMaxLength setCounterMaxLength(int) 设置计数器的最大计数数值,整型
app:errorEnabled setErrorEnabled(boolean) 设置是否显示一个错误信息,布尔值
app:hintAnimationEnabled setHintAnimationEnabled(boolean) 设置是否要显示输入状态时候的动画效果,布尔值
app:hintEnabled setHintEnabled(boolean) 设置是否要用这个浮动标签的功能,布尔值
app:hintTextAppearance setHintTextAppearance(int) 设置提示文字的样式(注意这里是运行了动画效果之后的样式)

这里我们通过一个简单的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);        //添加监听        
                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;
            } 
       }
    }
}

先来看一下最终的实现效果:

最终效果

可以很明显的看到,当我们同时设置了app:counterEnableapp: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'

有任何问题欢迎留言探讨~

对了,谁能告诉我在简书插入代码块为何不会自动换行,我手动的好辛苦,从编辑器或者从我的CSDN博客剪贴过来都不会自动换行~~~求告知

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,319评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,801评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,567评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,156评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,019评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,090评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,500评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,192评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,474评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,566评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,338评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,212评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,572评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,890评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,169评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,478评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,661评论 2 335

推荐阅读更多精彩内容