目前代码已经上传到GitHub,里面有博客里面Material Design系列的所有代码
GitHub Material Design
1、什么是TextInputLayout?
TextInputLayout是22.2.0新添加的控件, 要和EditText(或EditText的子类)结合使用,并且只能包含一个EditText(或EditText的子类),这一点尤其重要,在做用户注册的时候遇到了TextInputLayout里面有获取验证码的按钮,布局的时候出现问题了,具体的解决方法详见下文,TextInputLayout的继承关系如下图所示。
2、为什么要用TextInputLayout?
遵循Material Design的设计,很多APP在用户登录、注册的页面中都使用了TextInputLayout的控件,在一些iOS的APP中也能发现这种想过,通过一些细微的动画效果以及改变颜色,能达到很炫酷的效果,相比传统的EditText,在用户输入信息的时,hint(提示)会自动上移,并且输入框的下划线会高亮,并能够把输入校验的错误能够在控件下面展示,不用Toast弹出提示,并且自带设置密码框的显示与隐藏,效果如下图所示。
3、如何使用TextInputLayout?
3.1 首先在build.gradle 引入design的库
implementation 'com.android.support:appcompat-v7:26.1.0'
implementation 'com.android.support:design:26.1.0'
3.2 其次在xml文件中添加一下代码
<?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"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white">
<LinearLayout
android:layout_marginTop="40dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<include layout="@layout/custom_toolbar"/>
<android.support.design.widget.TextInputLayout
android:id="@+id/text_input_layout_phone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="15dp"
android:paddingRight="15dp"
android:scrollbarAlwaysDrawHorizontalTrack="true"
android:textColorHint="@color/colorHint"
app:counterOverflowTextAppearance="@style/TextOverCount"
app:errorTextAppearance="@style/text_input_login_error_style">
<android.support.design.widget.TextInputEditText
android:id="@+id/et_phone"
style="@style/et_login_text"
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_centerVertical="true"
android:hint="@string/phone_hint"
android:inputType="number"
android:maxLength="11"
android:textColor="@color/black"
android:textColorHint="@color/font_dark_color"
android:textSize="@dimen/font_middle_size" />
</android.support.design.widget.TextInputLayout>
<android.support.design.widget.TextInputLayout
android:id="@+id/text_input_layout_pwd"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="15dp"
android:paddingRight="15dp"
android:textColorHint="@color/colorHint"
app:errorTextAppearance="@style/text_input_login_error_style"
app:passwordToggleDrawable="@drawable/login_pwd_eye_selector"
app:passwordToggleEnabled="true"
app:passwordToggleTint="@color/colorHint">
<android.support.design.widget.TextInputEditText
android:id="@+id/et_pwd"
style="@style/et_login_text"
android:layout_width="match_parent"
android:layout_height="48dp"
android:hint="@string/pwd_hint"
android:inputType="textPassword"
android:maxLength="16"
android:singleLine="true"
android:textColor="@color/black"
android:textSize="@dimen/font_middle_size" />
</android.support.design.widget.TextInputLayout>
<Button
android:id="@+id/btn_login"
android:layout_width="match_parent"
android:layout_height="38dp"
android:layout_marginLeft="80dp"
android:layout_marginRight="80dp"
android:layout_marginTop="60dp"
android:layout_weight="1"
android:background="@drawable/btn_login_bg_gradient"
android:text="登录"
android:textColor="@color/white"
android:textSize="@dimen/font_small_size" />
</LinearLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_gravity="center_horizontal"
android:layout_marginBottom="18dp">
<View
android:id="@+id/view_center"
android:layout_width="0.5dp"
android:layout_height="16dp"
android:layout_centerInParent="true"
android:background="@color/line_default_color" />
<TextView
android:id="@+id/tv_modify_pwd"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="8dp"
android:layout_toLeftOf="@id/view_center"
android:text="忘记密码?"
android:textColor="#ff5c5f5f"
android:textSize="12dp" />
<TextView
android:id="@+id/tv_phone_register"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_toRightOf="@id/view_center"
android:text="注册"
android:textColor="#ff5c5f5f"
android:textSize="12dp" />
</RelativeLayout>
</RelativeLayout>
3.3在activity中的设置
package com.codingsnail.materialdesigndemo.view
import CommonUtils
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.text.Editable
import android.text.TextWatcher
import android.widget.Toast
import com.codingsnail.materialdesigndemo.R
import kotlinx.android.synthetic.main.activity_text_input_login.*
import org.jetbrains.anko.sdk25.coroutines.onClick
/**
* Created by Snail on 3/1/2018 11:31 AM
* Contact with slowsnail0223@gmail.com
*/
class TextInputLoginActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_text_input_login)
initView()
}
private fun initView() {
et_phone.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
text_input_layout_phone.isErrorEnabled = false
}
override fun afterTextChanged(s: Editable) {
}
})
et_pwd.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
text_input_layout_pwd.isErrorEnabled = false
}
override fun afterTextChanged(s: Editable) {
}
})
btn_login.onClick {
invalidLogin()
}
}
private fun invalidLogin() {
if (!CommonUtils.verifyMobile(et_phone!!.text.toString().trim())) {
text_input_layout_phone.error = getString(R.string.error_field_phone)
return
}
if (!CommonUtils.verifyLoginPwd(et_pwd!!.text.toString().trim())) {
text_input_layout_pwd.error = getString(R.string.error_incorrect_password)
return
}
Toast.makeText(this, "登录成功", Toast.LENGTH_SHORT).show()
}
companion object {
fun startActivity(context: Context) {
context.startActivity(Intent(context, TextInputLoginActivity::class.java))
}
}
}
这样就能完成如上git图所看到的所有功能啦!
4、TextInputLayout的重要属性讲解
4.1 达到上面效果我们需要做到的步骤
XML中配置的如上图所示,这样就可以基本完成TextInputLayout的功能了,但是想使用TextInputLayout自带的错误提示功能,在代码中还需要设置以下两点。
4.1.1 需要给TextInputEditText添加 addTextChangedListener监听,当文本有变化的时候需要动态的去设置,整个项目都是用Kotlin写的,Java的写法也是一样,只是语法不一样。
text_input_layout_pwd.isErrorEnabled = false
4.1.2 校验错误的时候去设置错误提示
可以看到完成1、2点以后就可以使用TextInputLayout的错误提示功能了
4.2 如何改变默认的颜色,能够适配自己APP颜色风格的UI
如果我们不进行颜色设置的话,那么我们字体的颜色以及下划线的颜色一定和colorPrimary、colorPrimaryDark、colorAccent颜色是一样的。
<!-- 代表 hint 的颜色 -->
<item name="android:textColorHint">#ff6f7272</item>
<!-- 代表 下划线没有获取焦点的颜色 的颜色 -->
<item name="colorControlNormal">#ffd8d8d8</item>
<!-- 代表 代表了获取焦点的时候 下划线 的颜色 -->
<item name="colorControlActivated">#ff3586fe</item>
<!-- 代表 代表了点击的时候 下划线 的颜色 -->
<item name="colorControlHighlight">#ff3586fe</item>
然后在我们的AndroidManifest.xml 里面的 application里面运用我们设置的主题就可以改变颜色了。
4.3 如何改变光标的颜色
如果我们不做配置的话,EditText的光标的颜色是和系统的colorAccent的颜色一样的,给editText设置光标颜色只需要设置一个属性即可。
<style name="et_login_text" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:textCursorDrawable">@drawable/cursor_input_et</item>
</style>
在XML中为这个editText运用这个style即可
4.4 如何设置最大数量限制?
这个只需要在TextInputLayout里面设置几个属性就可以了
<android.support.design.widget.TextInputLayout
android:id="@+id/text_input_layout_phone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:paddingLeft="15dp"
android:paddingRight="15dp"
app:counterMaxLength="11"
app:counterEnabled="true"
android:scrollbarAlwaysDrawHorizontalTrack="true"
android:textColorHint="@color/colorHint"
app:counterOverflowTextAppearance="@style/TextOverCount"
app:errorTextAppearance="@style/text_input_login_error_style">
counterMaxLength 设置最大长度限制
counterEnabled 是否开启超出限制后的提示
counterOverflowTextAppearance 超出限制后hint 的颜色,以及EditText 下划线的颜色 16/11 提示文字的颜色以及提示字体的大小都是在这个里面设置的
4.5 密码功能的小眼睛的显示与隐藏?
随着互联网的发展,很多用户的密码会很长,很麻烦,在输入的时候提示密码不对,这时候需要动态的可以看到密码的显示和隐藏密码,这种功能在TextInputLayout出来之前我们都是通过去标记一个初始状态,然后点击图片的时候去改变状态,然后设置editText的内容为可见,有了TextInputLayout以后,我们只需要去设置不同状态下显示的图片就可以了,省去了显示眼睛不同状态下图片的复杂的业务逻辑,具体的代码如下:
login_pwd_eye_selector的代码如下:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@mipmap/close_eyes" android:state_checked="false" />
<item android:drawable="@mipmap/open_eyes" android:state_checked="true" />
</selector>
下面为密码开关的一些属性。
app:passwordToggleEnabled:设置 password 开关是否可用。
app:passwordToggleTint:设置 password 开关图标的 tint 着色。
app:passwordToggleTintMode:设置 password tint 的模式。
app:passwordToggleDrawable:设置 password 开关图标。
setPasswordVisibilityToggleDrawable:设置 password 开关图标。
isPasswordVisibilityToggleEnabled:设置 password 开关图标 是否可见。
setPasswordVisibilityToggleTintList:设置 password 开关图标的 tint。
setPasswordVisibilityToggleTintMode:设置 password 开关图标的 tint 模式。
getPasswordVisibilityToggleDrawable:设置 password 开关图标。
4.6 使用过程中的请求验证码显示错位的问题
在上图中可以看到TextInputLayout中有一个获取验证码的按钮,因为TextInputLayout里面只能有EditText以及EditText的子类,所以我们想把获取验证码的按钮放在里面是不可能的。
最开始的时候的解决方案是TextInputLayout 外面套一层 RelativeLayout,然后把获取验证码这个Button 在RelativeLayout里面居中,最后发现这样写确实可以,但是当这个输入框有错误提示的时候,这个RelativeLayout的高度会变高,那么这个时候按钮的位置会在输入框的线上,所以最后把这个高度居中改为了直接距离上部20dp,这样问题就解决了。