转载请注明出处:http://blog.csdn.net/demokui/article/details/54693904
本篇文章出自【姜奎的博客】
本篇文章是一篇系列篇内容,会一步步实现出一款完美的Material Design app,所以一切从基础开始,后面遇到相关技术点会持续完善,本人也是从零开始学习,望高手轻喷,谢谢!
图中红框的信息对我们学习TextInputLayout很重要。大概意思是TextInputLayout是LinearLayout的子类,子控件是一个EditText控件或者子类控件,可以随时开启错误提示以及设置提示信息。
首先我们从名字上可以看出来TextInputLayout应该是一个布局类型,类似于RelativeLayout和LinearLayout这样的布局文件,相当于一个控件容器,但是TextInputLayout和RelativeLayout与LinearLayout不一样的地方是,TextInputLayout只能放一个控件,而且必须是EditText控件,这样我们监听EditText时如果输入有误可以给出完美的错误提示并带有很活波的动画效果,提高用户体验。
通过文档得知TextInputEditText是EditText的子类控件,那么毫无疑问它可以使用EditText的所有属性和方法。
Snackbar类似我们常用的Toast提示,众所周知Toast只是给用户一个简单的提示信息,隔一会就立马消失掉了,用户无法和其进行交互,但有些场景用户是需要与其交互才会达到更高的用户体现,那么谷歌这次就推出了Snackbar这个工具类,他不但UI风格完美,最主要的是可交互的Toast。
FloatingActionButton首先是一个Button,其次是一个ImageButton,Google为了使UI界面的所有控件完美结合从而达到Material Design,推出了这款带有阴影浮动效果的Button。使用场景如果项目中的一个界面需要一个回到顶部的悬浮按钮就完全可以将该控件加入到项目中去。
文档明确指出CoordinatorLayout是一种布局类型,是Framlayout布局的升级版,也就是说CoordinatorLayout是顶层布局控件,还有最强大之处就是他可以调节子view的行为。例如下文示例中处理Snackbar遮挡FloatingActionButton的问题用的就是这种布局。
先给出今天目标效果图:
好了今天我们就用文章开始时列举的5个控件来实现上面效果。
首先在使用前先添加相关依赖:
compile 'com.android.support:appcompat-v7:24.2.0' // 6.0系统自带v7 兼容低版本
compile 'com.android.support:design:24.2.0' // Design
compile 'net.qiujuer.genius:blur:2.0.0' // 图片高斯模糊(项目中为了美观要用到)
其次注意:因为本篇学习design里面的控件,google自定义了控件的属性,那么我们在布局的时候要在最顶层添加命名空间:
xmlns:app="http://schemas.android.com/apk/res-auto"
终于可以开始实战了,先亮出今天最重要的布局文件:
"1.0" encoding="utf-8"?>
.support.design.widget.CoordinatorLayout 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:orientation="vertical">
"@+id/id_main_rl"
android:layout_width="match_parent"
android:layout_height="match_parent">
"@+id/id_bg"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="@drawable/bg" />
"wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_marginTop="90dp"
android:text="Hello world"
android:textColor="@color/colorAccent"
android:textSize="36sp"
android:textStyle="bold" />
.support.design.widget.TextInputLayout
android:id="@+id/id_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="200dp"
android:paddingLeft="24dp"
android:paddingRight="24dp"
android:textColorHint="@color/colorhint">
.support.design.widget.TextInputEditText
android:id="@+id/id_user"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="请输入你的账户名"
android:inputType="phone" />
.support.design.widget.TextInputLayout>
.support.design.widget.TextInputLayout
android:id="@+id/id_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/id_name"
android:layout_marginTop="4dp"
android:paddingLeft="24dp"
android:paddingRight="24dp"
android:textColorHint="@color/colorhint">
.support.design.widget.TextInputEditText
android:id="@+id/id_pass"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="请输入你的账户密码"
android:inputType="textPassword" />
.support.design.widget.TextInputLayout>
.support.design.widget.FloatingActionButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:src="@android:drawable/ic_dialog_email"
app:elevation="5dp"
app:fabSize="normal"
app:layout_anchor="@id/id_main_rl"
app:layout_anchorGravity="bottom|right"
app:rippleColor="#00ff00" />
.support.design.widget.CoordinatorLayout>
我本来打算一点点往出贴代码的,一下没忍住直接Ctrl+A、C、V完了,那就这样吧;我们分析一下我们的布局结构:
...
最外层是CoordinatorLayout添加了命名空间,有两个子控件,之前说过这个布局是Framlayout的加强版。所以为了方便我在里面嵌套了一层RelativeLayout,这个相对布局就不过多解释了,这里先说一下
FloatingActionButton控件,我用了一些很少见到的属性,这就是我们添加的命名空间里的属性,这里解释一下这几个属性:
app:elevation="5dp" // 按钮投影 值越大阴影面积越大颜色越淡
app:fabSize="normal" // 按钮显示大小(共三个值可选) normal ->56dp mini ->40dp auto.
app:layout_anchor="@id/id_main_rl" // 锚点,可以将此按钮锚点在某个控件上,这里定义在相对布局上
app:layout_anchorGravity="bottom|right" // 锚点后设置该按钮在此控件上的位置
app:rippleColor="#00ff00" // 阴影颜色
小坑:大家可以试着将CoordinatorLayout改为FramLayout试一下,会出现一个bug,感兴趣的同学可以自行尝试。(要想bug重现必须在布局底部给出元素)
TextInputLayout这个没什么要说的,只是在EditText外面包了一层而已,需要注意的是设置hite颜色,是在外层设置。
这是style文件:
-- Base application theme. -->
下来给出MainActivity.java文件(关键之处给了详细注释,其余没什么可说的):
package com.damonjiang.designdemo;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.support.design.widget.TextInputEditText;
import android.support.design.widget.TextInputLayout;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.Editable;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.text.style.ForegroundColorSpan;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.Toast;
import net.qiujuer.genius.blur.StackBlur;
import interfaceUtils.SnacbarListener;
import utils.SnacbarUtils;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private ImageView mImageView;
private TextInputLayout mTIETName;
private TextInputLayout mTIETPassword;
private View mLogin;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 状态栏空间利用(沉浸式)
setStatusColor();
initView();
// 登录界面背景毛玻璃处理(第三方,值越大模糊效果越重,值越小越轻,0直接白板,一般设置8就好了)
Bitmap mBitmap = BitmapFactory.decodeResource(this.getResources(), R.drawable.bg);
Bitmap newBitmap = StackBlur.blurNatively(mBitmap, 8, false);
mImageView.setImageBitmap(newBitmap);
}
private void initView() {
mImageView = (ImageView) findViewById(R.id.id_bg);
mTIETName = (TextInputLayout) findViewById(R.id.id_name);
mTIETPassword = (TextInputLayout) findViewById(R.id.id_password);
final TextInputEditText editTextP = (TextInputEditText) findViewById(R.id.id_pass);
//必须先将错误提示开启
mTIETPassword.setErrorEnabled(true);
mLogin = findViewById(R.id.id_login);
mLogin.setOnClickListener(this);
editTextP.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
if (!validatePassword(charSequence.toString())) {
// 将错误提示文字改为红色
ForegroundColorSpan fgcspan = new ForegroundColorSpan(Color.RED);
SpannableStringBuilder ssbuilder = new SpannableStringBuilder("密码格式有误!!!");
ssbuilder.setSpan(fgcspan, 0, "密码格式有误!!!".length(), 0);
//设置错误提示信息内容
mTIETPassword.setError(ssbuilder);
} else {
//隐藏错误提示信息
mTIETPassword.setErrorEnabled(false);
}
}
@Override
public void afterTextChanged(Editable editable) {
}
});
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.id_login:
//这里说一下EditText上的数据可以根据TextInputLayout对象直接获取
String name = mTIETName.getEditText().getText().toString().trim();
String password = mTIETPassword.getEditText().getText().toString().trim();
if (TextUtils.isEmpty(name) || TextUtils.isEmpty(password)) {
SnacbarUtils.setSnckBar(mLogin, "用户名或密码错误", "重新输入", new SnacbarListener() {
@Override
public void OnAction() {
Toast.makeText(MainActivity.this, "请重新输入", Toast.LENGTH_SHORT).show();
}
});
} else if (validatePassword(password)) {
SnacbarUtils.setSnckBar(mLogin, "登录成功,点击右边按钮可直接进入个人中心", "立即进入", new SnacbarListener() {
@Override
public void OnAction() {
Toast.makeText(MainActivity.this, "欢迎回来", Toast.LENGTH_SHORT).show();
}
});
mTIETPassword.setErrorEnabled(false);
}
break;
}
}
// 密码验证
public boolean validatePassword(String password) {
return password.length() > 5;
}
private void setStatusColor() {
Window window = this.getWindow();
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS
| WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.setStatusBarColor(Color.TRANSPARENT);
}
}
状态栏文章可以看我之前总结的【Android 4.4之后状态栏和导航栏细节美化(沉浸式状态栏)】
Snackbar的工具类:SnacbarUtils
package utils;
import android.support.design.widget.Snackbar;
import android.view.View;
import interfaceUtils.SnacbarListener;
/**
* Created by ykit00001 on 2017/1/21.
*/
public class SnacbarUtils {
public static void setSnckBar(View view, String msg, String action, final SnacbarListener sl) {
//用法和Toast很类似,
Snackbar.make(view, msg, Snackbar.LENGTH_LONG)
.setAction(action, new View.OnClickListener() {
@Override
public void onClick(View view) {
sl.OnAction();
}
})
.show();
}
}
这里着重说一下 Snackbar的make方法参数
第一个参数view,这个view只要是当前界面的任何一个子view就可以,然后Snackbar内部会自动检索出父布局,然后将Snackbar UI添加到父布局上。
第二个参数msg,这里设置用户提示信息。
第三个参数Snackbar.LENGTH_LONG,显示时长。
Snackbar的setAction方法参数
第一个参数action,String类型,按钮提示名称,
第二个参数OnClickListener,按钮点击的监听回调接口。
这里我封装的时候又定义了一个回调接口:
public interface SnacbarListener {
void OnAction(); // 处理点击回调逻辑
}
好了,写到这里基本效果都已经实现了,其实基础功能很简单,大家可以试试,本篇最重要的就是学习控件以及布局的使用。如有问题欢迎大家指正,觉得不错请点个赞,谢谢!!!(高手请轻喷)