TextInput 详解 · Material Design Part 1 · 简单心理技术团队

Material Design 文本输入详解 这是一个系列文章,在这个系列里,我会按打造一个 Material Design App 的路线介绍所有应当掌握和值

Material Design 文本输入详解

这是一个系列文章,在这个系列里,我会按打造一个 Material Design App 的路线介绍所有应当掌握和值得掌握的系统组件。 你会在这些文章里了解到这些组件的使用和内部实现原理,以及它们背后所反映的 Material Design 的设计思想,希望你会喜欢。

Tips

我的每一篇博客都会提供详尽的API介绍,如果你想快速查阅某个功能的API或如何实现,建议Ctrl+F(Commad+F)在本页面搜索关键字查找。

Thanks for reading~!

目录

  1. 基本介绍
  2. TextInputLayout详解
  3. 代码杂烩
  4. 登录页面实战

基本介绍

文本输入框通常都有提示语,告知用户这里需要输入什么。提示语通常有两种形式, 位于输入框内,输入文字后即消失固定浮于输入框上方或左侧

将提示置于输入框内能够节省空间,但有个致命的缺点就是容易导致用户失去上下文。想象一下,当我们有十个文本框需要输入的时候,如果无法始终看到提示语,很容易忘记这里该填入什么样的数据。

提示语固定于输入框上方或左侧,让人能够非常明确的知道这里该填入的内容,但在动画效果和焦点区分上却不是十分出色。

结合这两种类型的输入框优点,Material Design推出了一种优秀的文本输入框形式—— 当用户点击空内容的文本输入框时,原本位于输入框内的提示语会经由一个动画浮动至输入框上方,文本颜色同时变成强调色,明确显示此时该组件获得焦点

为了让开发者更加方便地实现这种效果,Google推出了 TextInputLayout 组件。向该组件中放入一个 EditText 组件(或其扩展类也可,比如 TextInputEditText ),便可轻松实现Material Design文档中所要求的效果。

其中,要提一下的是TextInputEditText。相比于其他的EditText类,使用该类作为子View放入TextInputLayout可以在 全屏模式时依然在编辑器里展示hint

接下来就让我们一起来详细地整理学习一下TextInputLayout。

TextInputLayout详解

xml属性&常用方法

  • counterEnabled:false or true,用于设置字符计数器的开启或关闭。

    对应方法: setCounterEnabled(boolean)

  • counterMaxLength:设置字符计数器的最大长度。 (仅用于设置计数器最大值,并不影响文本实际能输入的最大长度)

    对应方法: setCounterMaxLength(int)

  • errorEnabled:false or true,用于设置错误提示是否开启。

    对应方法: setErrorEnabled(boolean)

  • hint:设置输入框的提示语。

    对应方法: setHint(CharSequence)

  • hintAnimationEnabled:true or false,开启或关闭hint浮动成标签的动画效果。

    对应方法: setHintAnimationEnabled(boolean)

  • hintEnabled:true or false,开启或关闭hint浮动的功能,设为false的话就和之前的EditText一样,在输入文字后,提示语就消失了。

    对应方法: setHintEnabled(boolean)

  • hintTextAppearance:设置hint的style,字体颜色,字体大小等,可引用系统自带的也可以自定义。 若要使用请统一使用,以保证APP体验的统一性。

    对应方法: setHintTextAppearance(int)

当文本输入类型为密码时,系统提供了一个开关来控制密码是否可见(默认为眼睛��)。此为design包24.2.0新提供的功能。

  • passwordToggleEnabled: 控制密码可见开关是否启用。 设为false则该功能不启用,密码输入框右侧也没有控制密码可见与否的开关。

    对应方法: setPasswordVisibilityToggleEnabled(boolean)

  • passwordToggleDrawable: 设置密码可见开关的图标。通常我们会在不同的情况下设定不同的图标,可通过自定义一个selector,根据“state_checked”属性来控制图标的切换。后面代码实践里会有示范。

    对应方法: setPasswordVisibilityToggleDrawable(Drawable)

  • passwordToggleTint: 控制密码可见开关图标的颜色。在开启或关闭的状态下我们可以设定不同的颜色,可通过自定义一个color的selector,根据“state_checked”和“state_selected”属性来控制颜色的切换。后面代码实践里会有示范。

    对应方法: setPasswordVisibilityToggleTintList(ColorStateList)

  • passwordToggleTintMode:控制密码可见开关图标的背景颜色混合模式。这个地方我不是很能理解,暂作标记,希望有人可以指教。不过可以肯定的是正常需求都用不到这个属性。

    分别是:

    1. multiply[Sa*Da, Sc*Dc]
    2. screen[Sa+Da-Sa*Da, Sc+Dc-Sc*Dc]
    3. src_atop[Da, Sc*Da+(1-Sa)*Dc]
    4. src_in[Sa*Da, Sc*Da]
    5. src_over[Sa+(1-Sa) Da, Rc=Sc+(1-Sa) Dc]

    对应方法: setPasswordVisibilityToggleTintMode(PorterDuff.Mode)

    注:关于这方面的知识有兴趣请参考 Xfermode in android 其中有关于这方面概念的解释。

  • passwordToggleContentDescription:该功能是为Talkback或其他无障碍功能提供的。TalkBack会去读contentDescription的值,告诉用户这个操作是什么。

对应方法: setPasswordVisibilityToggleContentDescription(int)

代码杂烩

效果图 TextInput 详解 · Material Design Part 1 · 简单心理技术团队

效果视频→ 点这里

Layout布局

<LinearLayout 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" tools:context="com.jiandanxinli.smileback.materiallogin.LoginActivity"> <ScrollView android:id="@+id/login_form" android:layout_width="match_parent" android:layout_height="match_parent" android:scrollbars="none"> <LinearLayout android:id="@+id/email_login_form" android:layout_width="match_parent" android:layout_height="wrap_content" android:focusableInTouchMode="true" android:orientation="vertical" android:paddingEnd="16dp" android:paddingStart="16dp"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="18dp" android:orientation="horizontal"> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_marginEnd="12dp" android:src="@drawable/ic_perm_identity_black_24dp" /> <android.support.design.widget.TextInputLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="@string/hotchpotch_name_tv"> <android.support.design.widget.TextInputEditText android:id="@+id/edit_name" android:layout_width="match_parent" android:layout_height="wrap_content" android:inputType="text" /> </android.support.design.widget.TextInputLayout> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="8dp" android:orientation="horizontal"> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_marginEnd="12dp" android:src="@drawable/ic_phone_black_24dp" /> <android.support.design.widget.TextInputLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="@string/hotchpotch_phone_tv" app:counterEnabled="true" app:counterMaxLength="11"> <android.support.design.widget.TextInputEditText android:id="@+id/edit_phone" android:layout_width="match_parent" android:layout_height="wrap_content" android:inputType="phone" android:maxLength="11" /> </android.support.design.widget.TextInputLayout> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_marginEnd="12dp" android:src="@drawable/ic_mail_black_24dp" /> <android.support.design.widget.TextInputLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="@string/hotchpotch_mail_tv"> <android.support.design.widget.TextInputEditText android:id="@+id/edit_email" android:layout_width="match_parent" android:layout_height="wrap_content" android:inputType="textEmailAddress" /> </android.support.design.widget.TextInputLayout> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="8dp" android:orientation="horizontal"> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_marginEnd="12dp" android:src="@drawable/ic_security_black_24dp" /> <android.support.design.widget.TextInputLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="@string/hotchpotch_password_tv" app:passwordToggleDrawable="@drawable/visibility_selector" app:passwordToggleEnabled="true" app:passwordToggleTint="@color/visibility_selector"> <android.support.design.widget.TextInputEditText android:id="@+id/edit_password" android:layout_width="match_parent" android:layout_height="wrap_content" android:inputType="textPassword" /> </android.support.design.widget.TextInputLayout> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_marginEnd="12dp" android:src="@drawable/ic_feedback_black_24dp" /> <android.support.design.widget.TextInputLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="@string/hotchpotch_feedback_tv" app:counterEnabled="true" app:counterMaxLength="10" app:hintTextAppearance="@style/FloatingStyle"> <android.support.design.widget.TextInputEditText android:id="@+id/edit_feedback" android:layout_width="match_parent" android:layout_height="wrap_content" android:inputType="text" /> </android.support.design.widget.TextInputLayout> </LinearLayout> <Button android:id="@+id/submit_btn" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginEnd="16dp" android:layout_marginStart="16dp" android:layout_marginTop="30dp" android:background="@drawable/btn_selector" android:text="@string/hotchpotch_submit_btn" android:textColor="@color/colorWhite" android:textSize="20sp" /> </LinearLayout> </ScrollView></LinearLayout>

FloatingStyle

<style name="FloatingStyle" parent="@android:style/TextAppearance"> <item name="android:textColor">#FFEB3B</item> <item name="android:textSize">20sp</item></style>

btn_selector

<selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:drawable="@color/colorPrimaryDark" android:state_pressed="true" /> <item android:drawable="@color/colorPrimary" /></selector>

drawable/visibility_selector

<selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:drawable="@drawable/ic_visibility_black_24dp" android:state_checked="false" android:state_pressed="true" /> <item android:drawable="@drawable/ic_visibility_off_black_24dp" android:state_checked="true" android:state_pressed="true" /> <item android:drawable="@drawable/ic_visibility_black_24dp" android:state_checked="true" android:state_pressed="false" /> <item android:drawable="@drawable/ic_visibility_off_black_24dp" android:state_checked="false" android:state_pressed="false" /></selector>

color/visibility_selector

<selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:color="@color/colorAccent" android:state_checked="false" android:state_pressed="true" /> <item android:color="@color/colorDivider" android:state_checked="true" android:state_pressed="true" /> <item android:color="@color/colorAccent" android:state_checked="true" android:state_pressed="false" /> <item android:color="@color/colorDivider" android:state_checked="false" android:state_pressed="false" /></selector>

HotchpotchActivity.java

public class HotchpotchActivity extends AppCompatActivity { private TextInputEditText mNameEditx; private TextInputEditText mPhoneEditx; private TextInputEditText mEmailEditx; private TextInputEditText mPasswordEditx; private TextInputEditText mFeedbackEditx; private String mContentName; private String mContentPhone; private String mContentEmail; private String mContentPassword; private String mContentFeedback; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_hotchpotch); initView(); } private void initView() { mNameEditx = (TextInputEditText) findViewById(R.id.edit_name); mPhoneEditx = (TextInputEditText) findViewById(R.id.edit_phone); mEmailEditx = (TextInputEditText) findViewById(R.id.edit_email); mPasswordEditx = (TextInputEditText) findViewById(R.id.edit_password); mFeedbackEditx = (TextInputEditText) findViewById(R.id.edit_feedback); Button submitBtn = (Button) findViewById(R.id.submit_btn); submitBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { controlAction(); } }); } private void getContent() { mContentName = mNameEditx.getText().toString(); mContentPhone = mPhoneEditx.getText().toString(); mContentEmail = mEmailEditx.getText().toString(); mContentPassword = mPasswordEditx.getText().toString(); mContentFeedback = mFeedbackEditx.getText().toString(); } private void controlAction() { getContent(); if (mContentName.length() == 0) { showToast("姓名不能为空!"); } else if (mContentPhone.length() != 11) { showToast("请正确填写11位手机号码!"); } else if (mContentEmail.length() == 0 || !android.util.Patterns.EMAIL_ADDRESS.matcher(mContentEmail).matches()) { showToast("请正确填写邮箱地址!"); } else if (mContentPassword.length() == 0) { showToast("密码不能为空"); } else if (mContentFeedback.length() == 0) { showToast("反馈意见不能为空!"); } else if (mContentFeedback.length() > 10) { showToast("反馈意见长度字数不能大于10!"); } else { doSubmission(); } } private void doSubmission() { showToast("恭喜您,数据正确!"); } private void showToast(String message) { Toast.makeText(HotchpotchActivity.this, message, Toast.LENGTH_SHORT).show(); }}

登录页面实战

就像文章开头说的,这是一个系列文章,路线是一个APP打开的路线。那么这第一篇就用来写登录页面吧,代码如下:

Layout布局

<LinearLayout 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:background="@color/colorPrimary" android:gravity="center_horizontal" android:orientation="vertical" android:paddingEnd="32dp" android:paddingStart="32dp" tools:context="com.jiandanxinli.smileback.materiallogin.LoginActivity"> <!-- Login progress --> <ProgressBar android:id="@+id/login_progress" style="?android:attr/progressBarStyleLarge" android:layout_width="wrap_content" android:layout_height="wrap_content" android:visibility="gone" /> <ScrollView android:id="@+id/login_form" android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:id="@+id/email_login_form" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_marginBottom="24dp" android:focusableInTouchMode="true" android:orientation="vertical"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:layout_marginBottom="48dp" android:fontFamily="cursive" android:text="@string/app_name" android:textSize="48sp" android:textStyle="bold" /> <android.support.design.widget.TextInputLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="24dp"> <AutoCompleteTextView android:id="@+id/email" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="@string/prompt_email" android:inputType="textEmailAddress" android:maxLines="1" /> </android.support.design.widget.TextInputLayout> <android.support.design.widget.TextInputLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="40dp" app:passwordToggleDrawable="@drawable/visibility_selector"> <android.support.design.widget.TextInputEditText android:id="@+id/password" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="@string/prompt_password" android:imeActionId="@+id/login" android:imeActionLabel="@string/action_sign_in_short" android:imeOptions="actionUnspecified" android:inputType="textPassword" android:maxLines="1" /> </android.support.design.widget.TextInputLayout> <Button android:id="@+id/email_sign_in_button" style="?android:textAppearanceSmall" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@color/colorPrimaryDark" android:text="@string/action_sign_in" android:textSize="18sp" android:textStyle="bold" /> </LinearLayout> </ScrollView></LinearLayout>

LoginActivity.java

/** * 登录页面 * * @author bugdev */public class LoginActivity extends AppCompatActivity { private AutoCompleteTextView mEmailView; private EditText mPasswordView; private ProgressDialog mProgressDialog; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_login); mEmailView = (AutoCompleteTextView) findViewById(R.id.email); mPasswordView = (EditText) findViewById(R.id.password); mPasswordView.setOnEditorActionListener(new TextView.OnEditorActionListener() { @Override public boolean onEditorAction(TextView textView, int id, KeyEvent keyEvent) { if (id == R.id.login || id == EditorInfo.IME_NULL) { //尝试登录 attemptLogin(); return true; } return false; } }); Button emailSignInButton = (Button) findViewById(R.id.email_sign_in_button); emailSignInButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { //尝试登录 attemptLogin(); } }); } /** * 说明:获取用户输入的内容并调用相应的验证方法 */ private void attemptLogin() { String email = mEmailView.getText().toString(); String password = mPasswordView.getText().toString(); if (isEmailValid(email) && isPasswordValid(password)) { login(); } } /** * 说明:验证邮箱格式 * * @param email 邮箱地址 * @return 邮箱地址格式正确或错误 */ private boolean isEmailValid(String email) { if (email.length() == 0) { showToast("请输入邮箱!"); return false; } else if (!android.util.Patterns.EMAIL_ADDRESS.matcher(email).matches()) { showToast("邮箱格式不正确!"); return false; } else { return true; } } /** * 说明:验证密码格式 * * @param password 密码 * @return 密码格式正确或错误 */ private boolean isPasswordValid(String password) { if (password.length() == 0) { showToast("请输入密码!"); return false; } else if (password.length() < 6) { showToast("请输入至少6位密码!"); return false; } else { return true; } } /** * 说明:显示Toast的方法 * * @param message 要显示的消息 */ private void showToast(String message) { Toast.makeText(getApplicationContext(), message, Toast.LENGTH_SHORT).show(); } /** * 说明:登录 * <p> * 这里为了展示对话框,将对话框固定显示了3秒 */ private void login() { showDialog(); Timer timer = new Timer(); TimerTask timerTask = new TimerTask() { @Override public void run() { mProgressDialog.dismiss(); Intent intent = new Intent(LoginActivity.this, HomeActivity.class); startActivity(intent); finish(); } }; timer.schedule(timerTask, 3000); } /** * 说明:show一个Dialog */ private void showDialog() { mProgressDialog = new ProgressDialog(this, R.style.AppTheme_Dark_Dialog); mProgressDialog.setIndeterminate(true); mProgressDialog.setMessage("正在验证..."); mProgressDialog.show(); }}

Dialog Style

<style name="AppTheme.Dark.Dialog" parent="Theme.AppCompat.Dialog"> <item name="colorAccent">@color/colorAccent</item> <item name="android:textColorPrimary">@color/textPrimary</item> <item name="android:background">@color/colorPrimary</item></style>

效果图

TextInput 详解 · Material Design Part 1 · 简单心理技术团队

效果视频→ 点这里

未登录用户
全部评论0
到底啦