MaterialDesigner介绍
Material Design 是一种独一无二的底层系统,在这个系统的基础之上,构建跨平台和超越设备尺寸的统一体验。遵循基本的移动设计定则,同时支持触
摸、语音、鼠标、键盘等输入方式
特点
Material Design 有以下的特点:
1. 实体感的操作
2. 鲜明、形象的视觉效果
3. 有意义的动画效果
MaterialDigner样式
新增样式
@android:style/Theme.Material
@android:style/Theme.Material.Light
@android:style/Theme.Material.Light.DarkActionBar
自定义样式
xml中修改:
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
代码中修改样式:
//注意版本适配
Window window = getWindow();
window.setStatusBarColor(ContextCompat.getColor(this, R.color.colorBlue));
window.setNavigationBarColor(ContextCompat.getColor(this, R.color.colorGreen));
新增高度属性
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="版本21"
//新增的高度属性,可以通过该属性来控制显示的图层
android:translationZ="100dp"
//与高度相关的属性:阴影,注意只有在设置了背景以后才能有效果
android:elevation="10dp"
/>
新增波纹触摸反馈
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
//没有边界的波纹
android:background="?android:attr/selectableItemBackgroundBorderless"
//到控件边界自动消失的波纹
android:background="?android:attr/selectableItemBackgroundBorder"
android:text="版本21"/>
布局中@和?的区别
与往常不同,在引用波纹反馈时用到了?而不是@
那么?和@的区别是什么呢?
通常我们都是通过@去获取一个,这些属性有一个特点,都是写死的.
那如果我们想获取一个与手机主题相关的属性怎么办?
这就是?的作用,为了解决这个问题.
为了和当前的系统主题保持一致,我们常常需要获取当前应用主题与系统主题的一些属性,这些属性是会根据主题的变化发
生变化,所以我们不能使用@符号取,@符号是取某个固定的值。在这种情况下面,我们需要使用?,?符号代表在当前的主题下面去获取值。
实践
定义一个属性
//res/values/attrs.xml
<resources>
<declare-styleable name="show">
<attr name="myTextColor" format="color"/>
</declare-styleable>
</resources>
在主题中设置该属性
//res/values/styles
<resources>
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<!--设置该属性-->
<item name="myTextColor">@android:color/holo_red_light</item>
</style>
</resources>
在控件中引用该属性
<TextView
android:textColor="?attr/myTextColor"
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="TextView"/>
版本适配
静态版本适配
//5.0以后会优先从该目录读取layout文件
res/右键/Direct/layout-v21
动态版本适配
if (Build.VERSION.SDK_INT >= 21) {
getWindow().setNavigationBarColor(Color.RED);
getWindow().setStatusBarColor(Color.GREEN);
}
CardView控件
CardView 是MD提供的一种带圆角、阴影的容器。
引包
compile 'com.android.support:cardview-v7:25.+'
属性
<android.support.v7.widget.CardView
//背景颜色
app:cardBackgroundColor="@android:color/holo_purple"
//圆角半径大小
app:cardCornerRadius="13dp"
//阴影大小
app:cardMaxElevation="100dp" >
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/content"
android:textColor="@android:color/black"
android:textSize="22sp"
/>
</ScrollView>
</android.support.v7.widget.CardView>
TextInputLayout
提供更加友善的界面.TextInputLayout可以作
为EditText的父控件,能够将提示上浮,并自带文本字数控制的功能.
引包
compile 'com.android.support:design:25.3.1'
属性
<android.support.design.widget.TextInputLayout
android:id="@+id/text_input_layout"
//提示文本
android:hint="提示文本"
//开启提示文本的动画效果
app:hintAnimationEnabled="true"
//是否开启长度计数
app:counterEnabled="true"
//设置最大长度
app:counterMaxLength="10"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<EditText
android:id="@+id/et"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</android.support.design.widget.TextInputLayout>
错误提示
public class TextInputLayoutActivity extends AppCompatActivity {
@BindView(R.id.et)
EditText mEt;
@BindView(R.id.text_input_layout)
TextInputLayout mTextInputLayout;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_text_input_layout);
ButterKnife.bind(this);
mEt.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
//edittext 设置error
if (s.length()>10){
mEt.setError("请输入10以内的文本");
}else {
mEt.setError(null);
}
}
});
}
}
自定义错误提示样式
//res/values/style
<resources>
<style name="myError" parent="TextAppearance.Design.Error">
<item name="android:textSize">33sp</item>
<item name="android:textColor">@android:color/holo_purple</item>
</style>
</resources>
<android.support.design.widget.TextInputLayout
android:id="@+id/text_input_layout"
android:hint="提示文本"
app:hintAnimationEnabled="true"
app:counterEnabled="true"
app:errorTextAppearance="@style/myError">
</android.support.design.widget.TextInputLayout>
NestedScrollView
在一个列表控件(ListView RecyerView)中,除了能够上下滚动外,也可以对内部的某个滚动(如下图二)。
在以前,我们要实现这样的功能需要重写触摸事件的传递。现在Material Design提供了一个进阶版本的ScrollView给我们使用。
引包
compile 'com.android.support:cardview-v7:25.+'
1.在ScrollView中嵌套使用
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#8888">
<LinearLayout
android:background="#fff"
android:layout_width="match_parent"
android:layout_height="800dp"
android:orientation="vertical">
//会优先滑动NestedScrollView,当NestedScrollView不能再滑动的时候滑动外部的ScrollView
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="400dp">
<TextView
android:background="#00f"
android:text="@string/content"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</android.support.v4.widget.NestedScrollView>
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="400dp">
<TextView
android:background="#0ff"
android:text="@string/content"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</android.support.v4.widget.NestedScrollView>
</LinearLayout>
</ScrollView>
2.在ListView的item中嵌套使用
FloatActionButton
android 在 Material Designer 中提供了一种漂浮的按钮
引包
compile 'com.android.support:cardview-v7:25.+'
属性
<android.support.design.widget.FloatingActionButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
//中间的图标的
android:src="@mipmap/ic_launcher_round"
//背景颜色
app:backgroundTint="#F00"
//中间的图标的大小
app:fabSize="normal"
//应该设置为0,否则在4.1版本显示为正方形,5.0以后没有阴影效果
app:borderWidth="0dp"
//配合coordinator_layout使用,稍后讲解
app:layout_anchor="@id/coordinator_layout"
app:layout_anchorGravity="bottom|right"
app:elevation="30dp"
android:translationZ="3dp"/>
ToolBar
google在旧版本推出过一个控件ActionBar,但是ActionBarzhi只能固定在顶部,不能灵活使用,所以google推出了一个ToolBar来替换ActionBar,我们可以配合一些新的控件实现很多酷炫的效果。
引包
compile 'com.android.support:cardview-v7:25.+'
ToolBar的内容讲解
使用
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?android:attr/actionBarSize"
android:background="#F00"
/>
//参考开头的Toolbar内容介绍
mToolbar.setTitle("你好啊");
mToolbar.setSubtitle("小小的你好啊");
mToolbar.setNavigationIcon(R.mipmap.ic_launcher_round);
mToolbar.setLogo(R.mipmap.ic_launcher);
mToolbar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
return false;
}
});
//将其设置为actionbar,从而实现对actionbar各种兼容
setSupportActionBar(mToolbar);
android.support.v7.app.ActionBar actionBar = getSupportActionBar();
SnackBar
Toast是我们在日常开发中经常使用的控件,简单易用,但是现在有这样的需求,需要与Toast交互,比如登陆失败了,弹出Toast后,点击Toast能够重新
登陆,但是就目前的Toast来说是无法解决这种问题的。因此google提供了一个新的控件给开发者,Snackbar。Snackbar除了有Toast的功能外,还具备
了Toast不具备的交互功能。
mFab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Snackbar
//第一个参数是view,是snackbar依附的控件
.make(mFab, "你好啊", 3000)
.setAction("点击交互", new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(getApplicationContext()
,"弹出一个吐司",Toast.LENGTH_SHORT).show();
}
})
.show();
}
});
波纹动画
MD动画中有大量波纹动画,我们来看看如何通过代码来实现。
注意下面的api是5.0以上的系统提供的,注意好版本判断。
//核心api
ViewAnimationUtils.createCircularReveal(View view,int centerX,int centerY,float startRadius, float endRadius)
使用
public class CircularRevealActivity extends AppCompatActivity {
@BindView(R.id.fab)
FloatingActionButton mFab;
@BindView(R.id.layout)
ConstraintLayout mLayout;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_circular_reveal);
ButterKnife.bind(this);
if (Build.VERSION.SDK_INT>=21){
mFab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//以指定view的背景作为动画的元素
Animator reveal = ViewAnimationUtils.createCircularReveal(mLayout, (int)mFab.getX()+mFab.getWidth()/2
, (int)mFab.getY()+mFab.getHeight()/2, 0, 1800);
reveal.setInterpolator(new LinearInterpolator());
// mLayout.setBackgroundColor(Color.parseColor("#0000ff"));
reveal.start();
}
});
}
}
}
监听共享动画
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (Build.VERSION.SDK_INT >= 21) {
ChangeBounds changeBounds = new ChangeBounds();
//开启transition
getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);
getWindow().setSharedElementEnterTransition(changeBounds);
//监听过场动画
changeBounds.addListener(new Transition.TransitionListener() {
@Override
public void onTransitionStart(Transition transition) {
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
public void onTransitionEnd(Transition transition) {
showAnimation();
}
@Override
public void onTransitionCancel(Transition transition) {
}
@Override
public void onTransitionPause(Transition transition) {
}
@Override
public void onTransitionResume(Transition transition) {
}
});
}
setContentView(R.layout.activity_round_toolbar_detail);
}
Palette
Material Design 建议使用大色块来进行显示需要加重的内容,我们常常需要UI人员进行配合才能实现相应的功能,但是在Material Design中,google提供
了一个工具类给我们,让我们可以根据图片获取相应的对应的颜色,能够动态的显示在UI上面。
引包
compile 'com.android.support:palette-v7:25.3.1'
核心api
创建取色器
//同步的⽅方式⽣生成⼀一个取⾊色器,会卡顿UI
Palette.from(BitMap).generate();
//异步的⽅方式⽣生成⼀一个取⾊色器
Palette.from(BitMap).generate(PaletteAsyncListener lis);
获取这个取色器内部的颜色,!!!这些颜色可能为空,使用前需要做非空判断
Vibrant (有活力)
Vibrant dark(有活力 暗色)
Vibrant light(有活力 亮色)
Muted (柔和)
Muted dark(柔和 暗色)
Muted light(柔和 亮色)
使用方法
创建一个子线程的Handler
//HandlerThread对Thread的looper进行了封装
woker = new HandlerThread("worker"); //开启了一直存在
woker.start();
//mHandler绑定到woker子线程的looper上面
mHandler = new Handler(woker.getLooper()) {
@Override
public void handleMessage(Message msg) {
if(msg.what==99){
title.setText("aaaaa");
}else{
int index = msg.arg1;
//一定是在子线程
changeColor(index);
}
}
};
取色器
Bitmap bm = BitmapFactory.decodeResource(getResources(), darwables[index]);
Palette.from(bm).generate(new Palette.PaletteAsyncListener() {
@Override
public void onGenerated(Palette palette) {
//取某种风格的颜色,注意需要做非空判断
if (palette.getDarkMutedSwatch() != null) {
//获取颜色
int color = palette.getDarkMutedSwatch().getRgb();
}else if(palette.getLightVibrantSwatch() != null){
int color = palette.getLightVibrantSwatch().getRgb();
Message message = uiHandler.obtainMessage();
message.arg1 = color;
uiHandler.sendMessage(message);
}
}
});
修改颜色
Window window = getWindow();
if(Build.VERSION.SDK_INT>=21){
window.setStatusBarColor(color);
window.setNavigationBarColor(color);
}