Material Design 设计风格进阶篇 <一>

转载请注明出处:http://blog.csdn.net/demokui/article/details/54693904

本篇文章出自【姜奎的博客】

撰写缘由:

  1. 平时只是用Material Design的某些控件或功能,从来没融合在一块写一个app,想看看自己动手的效果到底如何;
  2. 现在市面上的好多app界面风格无法统一并且没有一款很合眼;
  3. android UI风格一直在IOS面前有点逊色,这次为了在IOS面前昂首挺胸的做人.

本篇文章是一篇系列篇内容,会一步步实现出一款完美的Material Design app,所以一切从基础开始,后面遇到相关技术点会持续完善,本人也是从零开始学习,望高手轻喷,谢谢!

一、本篇目标控件

  1. TextInputLayout
  2. TextInputEditText
  3. Snackbar
  4. FloatingActionButton
  5. CoordinatorLayout

二、控件详解

1. TextInputLayout:

给出官网介绍截图:

图中红框的信息对我们学习TextInputLayout很重要。大概意思是TextInputLayout是LinearLayout的子类,子控件是一个EditText控件或者子类控件,可以随时开启错误提示以及设置提示信息。

首先我们从名字上可以看出来TextInputLayout应该是一个布局类型,类似于RelativeLayout和LinearLayout这样的布局文件,相当于一个控件容器,但是TextInputLayout和RelativeLayout与LinearLayout不一样的地方是,TextInputLayout只能放一个控件,而且必须是EditText控件,这样我们监听EditText时如果输入有误可以给出完美的错误提示并带有很活波的动画效果,提高用户体验。

2. TextInputEditText:

给出官网介绍截图:

通过文档得知TextInputEditText是EditText的子类控件,那么毫无疑问它可以使用EditText的所有属性和方法。

3. Snackbar:

给出官网介绍截图:

Snackbar类似我们常用的Toast提示,众所周知Toast只是给用户一个简单的提示信息,隔一会就立马消失掉了,用户无法和其进行交互,但有些场景用户是需要与其交互才会达到更高的用户体现,那么谷歌这次就推出了Snackbar这个工具类,他不但UI风格完美,最主要的是可交互的Toast。

4. FloatingActionButton:

给出官网介绍截图:

FloatingActionButton首先是一个Button,其次是一个ImageButton,Google为了使UI界面的所有控件完美结合从而达到Material Design,推出了这款带有阴影浮动效果的Button。使用场景如果项目中的一个界面需要一个回到顶部的悬浮按钮就完全可以将该控件加入到项目中去。

5. CoordinatorLayout:

给出官网介绍截图:

文档明确指出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"

终于可以开始实战了,先亮出今天最重要的布局文件:

<?xml version="1.0" encoding="utf-8"?>
<android.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">

    <RelativeLayout
        android:id="@+id/id_main_rl"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <ImageView
            android:id="@+id/id_bg"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:scaleType="centerCrop"
            android:src="@drawable/bg" />

        <TextView
            android:layout_width="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" />

        <android.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">

            <android.support.design.widget.TextInputEditText
                android:id="@+id/id_user"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:hint="请输入你的账户名"
                android:inputType="phone" />

        </android.support.design.widget.TextInputLayout>

        <android.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">

            <android.support.design.widget.TextInputEditText
                android:id="@+id/id_pass"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:hint="请输入你的账户密码"
                android:inputType="textPassword" />
        </android.support.design.widget.TextInputLayout>


        <Button
            android:id="@+id/id_login"
            android:layout_width="234dp"
            android:layout_height="46dp"
            android:layout_below="@id/id_password"
            android:layout_centerHorizontal="true"
            android:layout_marginTop="45dp"
            android:background="@drawable/shape_pink"
            android:text="登录"
            android:textColor="#fff"
            android:textSize="16sp" />
    </RelativeLayout>

    <android.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" />
</android.support.design.widget.CoordinatorLayout>

我本来打算一点点往出贴代码的,一下没忍住直接Ctrl+A、C、V完了,那就这样吧;我们分析一下我们的布局结构:

<CoordinatorLayout>
    <RelativeLayout>
    ...
        <TextInputLayout>
            <TextInputEditText/>
        </TextInputLayout>

     </RelativeLayout>

    <FloatingActionButton/>
</CoordinatorLayout>

最外层是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文件:

<resources>
    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
        <!-- EditText默认状态状态设置底线颜色 -->
        <item name="colorControlNormal">@color/colorhint</item>
        <!-- EditText选择的底线颜色 -->
        <item name="colorControlActivated">@color/colorAccent</item>
    </style>
</resources>

下来给出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(); // 处理点击回调逻辑
}

好了,写到这里基本效果都已经实现了,其实基础功能很简单,大家可以试试,本篇最重要的就是学习控件以及布局的使用。如有问题欢迎大家指正,觉得不错请点个赞,谢谢!!!(高手请轻喷)

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值