TextInput 详解 · Material Design Part 1 · 简单心理技术团队 - 24小时 - 剑客 -「科技引领未来」

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
到底啦