Android群英传笔记——第十二章:Android5.X 新特性详解,Material Design UI的新体验

Android群英传笔记——第十二章:Android5.X 新特性详解,Material Design UI的新体验


第十一章为什么不写,因为我很早之前就已经写过了,有需要的可以去看

Android高效率编码-第三方SDK详解系列(二)——Bmob后端云开发,实现登录注册,更改资料,修改密码,邮箱验证,上传,下载,推送消息,缩略图加载等功能

这一章很多,但是很有趣,也是这书的最后一章知识点了,我现在还在考虑要不要写这个拼图和2048的案例,在此之前,我们先来玩玩Android5.X的新特性吧!

  • Android 5.X UI设计初步
  • Android 5.X 新增特性分析

一. Android 5.X UI设计初步

Android 5.X开始使用新的设计风格Material Design来统一整个Android系统设计风格,与之前的设计不同,这次的Material Design设计将Android带来一片全新的高度,同时Google在官网推出了新的设计指南,全面的讲解了Material Design的整个实现规范

1.材料的形态模拟

材料的形态模拟是Material Design中最核心也是改变最大的设计,Google通过模拟自然界纸墨的形态变化,光线和阴影,纸和纸之间的空间层次关系,带来一种真实的感觉

2.更加真实的动画

好的动画效果可以非常的有效的指引用户,暗示用户并给用户带来愉悦的使用体验,Android5.X中大量加入了各种新的动画效果,让整个设计风格更加自然,和谐,而各种新的转场动画,能更加有效的指引用户的视觉焦点,不至于因为复杂布局的界面重排而对整体效果产生影响,让使用者达到一个视觉连贯性

3.大色块的使用

Material Design中用了大量高饱和度,适中亮度的大色块来突出界面的主次,并一扫Android4.X系列Holo主题的沉重感,让界面更加富有时尚感和视觉冲击力

此外,还有更多的设计风格,比如悬浮按钮,聚焦大图,无框按钮,波纹效果等新特性,这里就不一一列举了,Google在其Material Design网站上也有

2.Material Design主题

我们先来看看如何使用主题,MD一共有三种默认的主题可以设置

@android:style/Theme.Material (dark version)
@android:style/Theme.Material.Light (light version)
@android:style/Theme.Material.Light.DarkActionBar   

效果如图

同时,Android5.X提出来Color Palette的概念,让开发者可以设定系统区域的颜色,使得整个APP的颜色使得APP的颜色统一

通过如下所示的代码,可以通过自定义style的方式来创建自己的Color palette颜色主题,从而实现颜色的不同风格

<!-- inherit from the material theme-->
    <style name="AppTheme" parent="android:Theme.Material">
        <!-- Main theme color-->
        <!-- your app branding color for the app bar-->
        <item name="colorPrimary">#BEBEBE</item>
        <!-- derker variant for thr status bar and contextual app bars-->
        <item name="colorPrimaryDark">#FF5AEBFF</item>
        <!--theme ui controls like checkBoxs and text fields-->
        <item name="colorAccent">#FFFF4130</item>

    </style>

看效果

3.Palette

在Android的版本发展中,UI越来越成为Google的发展中心,这次的Android5.X创新的使用了Palette来提取颜色,从而让主题能够动态适应当前页面的色调,使得整个app的颜色基本和谐统一

Android内置了几种提取颜色的种类

  • Vibrant(充满活力的)
  • Vibrant dark(充满活力的黑)
  • Vibrant light(充满活力的白)
  • Muted(柔和的)
  • Muted dark(柔和的黑)
  • Muted light(柔和的白)

使用Palette的API,能够让我们从Bitmap中获取对应的色调,修改当前的主题色调,使用Palette首先需要在Android studio引用相关的依赖

compile ‘com.android.support:palette-v7:21.0.+‘

可以通过传递一个Bitmap对象给Palette,并调用它的Palette.generate()静态方法或者Palette.generateAsync()方法创建一个Palette,接下来,就可以使用getter方法来检索相应的色调,这些色调就是我们在上面列表所列出的色调

我们这里写一个小例子,演示如何通过加载的图片的柔和色调来改变状态栏和actionbar的色调,代码如下

package com.lgl.materialdesign;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.graphics.Palette;
import android.view.Window;
import android.widget.ImageView;

public class MainActivity extends AppCompatActivity {

    private ImageView iv_palette;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        setPalette();
    }

    /**
     * Palette获取颜色
     */
    private void setPalette() {
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher);
        //创建Palette对象
        Palette.generateAsync(bitmap, new Palette.PaletteAsyncListener() {
            @Override
            public void onGenerated(Palette palette) {
                //通过Palette来获取对应的色调
                Palette.Swatch vibrant = palette.getDarkVibrantSwatch();
                //将颜色设置给相应的组件
                getSupportActionBar().setBackgroundDrawable(new ColorDrawable(vibrant.getRgb()));
                Window window = getWindow();
                window.setStatusBarColor(vibrant.getRgb());
            }
        });
    }
}

而且,我们可以使用不同的方法获取不同的色调颜色

palette.getVibrantSwatch();
palette.getDarkMutedSwatch();
palette.getLightMutedSwatch();
palette.getMutedSwatch();
palette.getDarkVibrantSwatch();
palette.getLightVibrantSwatch();

看效果

4.视图与阴影

Material Design的一个很重要的特点就是拟物扁平化,如果说IOS的扁平化设计太过于超前,让很多人来不及从拟物转变成扁平,那么Material Design则是把IOS往回拉了一点,通过展现生活中的材料效果,恰当的使用阴影和光线,配合完美的动画效果,模拟出一个动感十足又美丽大胆的视觉效果

-1.阴影效果

以往的Android View通常有两个属性——X和Y,而在Android5.X中,Google为其增加了一个新的属性——Z,对应垂直方向上的高度变化,看图,我们更加了解

在Android 5.X中,View的Z值由两部分组成,elevation和translationZ(他们都是Android5.X新引入的属性),elevation是静态的成员,translationZ可以在代码中使用来实现动画效果,他们的关系

Z = elevation + translationZ;

通过下面的代码,演示了不同视图高度所显示的效果,xml代码

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <TextView
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:layout_margin="10dp"
        android:background="@mipmap/ic_launcher" />

    <TextView
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:layout_margin="10dp"
        android:background="@mipmap/ic_launcher"
        android:elevation="2dp" />

    <TextView
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:layout_margin="10dp"
        android:background="@mipmap/ic_launcher"
        android:elevation="10dp" />

</LinearLayout>

在程序中,我们也可以使用代码改变视图高度

 view.setTranslationZ(xxx);

通常也会使用属性动画来为视图高度改变的时候增加动画效果

    if(flag){
            view.animate().translationZ(100);
            flag = false;
        }else {
            view.animate().translationZ(0);
            flag = true;
        }

5.Tinting 和 Clipping

Android5.X在对图像的操作有了更多的功能,下面来看看Android5.X的两个对操作图像的新功能——Tinting(着色) 和 Clipping(裁剪)

-1.Tinting(着色)

Tinting的使用非常的简单,只要在XML中配置好tint和tintMode就可以了,对于配置组合效果,只需要大家实际操作一下,就能非常清晰的理解处理效果,在下面的代码中,设置了几种不同的tint和tintMode效果,XML代码如下

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <ImageView
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:layout_gravity="center"
        android:elevation="5dp"
        android:src="@mipmap/ic_launcher" />

    <ImageView
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:layout_gravity="center"
        android:elevation="5dp"
        android:src="@mipmap/ic_launcher"
        android:tint="@android:color/holo_blue_bright" />

    <ImageView
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:layout_gravity="center"
        android:elevation="5dp"
        android:src="@mipmap/ic_launcher"
        android:tint="@android:color/holo_blue_bright"
        android:tintMode="add" />

    <ImageView
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:layout_gravity="center"
        android:elevation="5dp"
        android:src="@mipmap/ic_launcher"
        android:tint="@android:color/holo_blue_bright"
        android:tintMode="multiply" />

</LinearLayout>

效果如下

Tint通过修改图像的Alpha遮罩来修改图像的颜色,从而达到重新着色的目的,这一功能在一些图片处理的APP使用起来还是十分的方便的

-2. Clipping(裁剪)

Clipping可以让我们改变一个视图的外形,要使用Clipping,首先需要使用ViewOutlineProvider来修改outline作用给视图

下面这个例子,将一个正方形的textview通过Clipping裁剪成一个圆形的正方形和一个圆,XML代码如下

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical">

    <TextView
        android:id="@+id/tv_rect"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:layout_marginTop="20dp"
        android:elevation="1dp"
        android:gravity="center" />

    <TextView
        android:id="@+id/tv_circle"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:layout_marginTop="20dp"
        android:elevation="1dp"
        android:gravity="center" />

</LinearLayout>

逻辑代码很简单

 /**
     * Clipping裁剪
     */
    private void setClipping() {
        final View v1 = findViewById(R.id.tv_rect);
        View v2 = findViewById(R.id.tv_circle);

        //获取Outline
        ViewOutlineProvider vlp1 = new ViewOutlineProvider() {
            @Override
            public void getOutline(View view, Outline outline) {
                //修改outline为特定形状
                outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), 30);
            }
        };

        //获取Outline
        ViewOutlineProvider vlp2 = new ViewOutlineProvider() {
            @Override
            public void getOutline(View view, Outline outline) {
                //修改outline为特定形状
                outline.setOval(0, 0, view.getWidth(), view.getHeight());
            }
        };
        //重新设置形状
        v1.setOutlineProvider(vlp1);
        v2.setOutlineProvider(vlp2);
    }

我们看下效果

6.列表和卡片

-1.RecyclerView

在Android5.X中将使用了很久的ListView做了升级,增加了一个使用方便,效率更高的控件——RecyclerView,RecyclerView是support-v7中的一个新的组件,是一个强大的滑动控件,ViewHolder的封装实现,用户只要实现自己的viewholder就可以了,该组件会自动帮你回收服用的每一个item

要使用RecyclerView,首先需要在项目中引用依赖

compile ‘com.android.support:recyclerview-v7:21.0.+‘

在布局中使用RecyclerView与使用ListView基本类似,同样需要一个类似List itemd的布局,在Material Design中,通常与CardView配合使用,后面我们再讲CardView的使用

使用RecyclerView的重点和使用和ListView一样,需要使用一个合适的数据适配器来加载数据,RecyclerView中需要重写的很多方法都似曾相识,不过RecyclerView更加先进的是,它已经封装好了ViewHolder,只要实现功能就可以,,先看Adapter

package com.lgl.materialdesign;

import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.TextView;

import java.util.List;

/**
 * RecyclerView的适配器
 * Created by LGL on 2016/4/30.
 */
public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerAdapter.ViewHolder> {

    private List<String> mData;

    public RecyclerAdapter(List<String> mData) {
        this.mData = mData;
    }

    public AdapterView.OnItemClickListener itemClickListener;

    public void setOnItemClickListener(AdapterView.OnItemClickListener itemClickListener) {
        this.itemClickListener = itemClickListener;
    }

    public interface OnItemClickListener {
        void onItemClic(View view, int position);
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        //将布局转化为View并传递给RecyclerView封装好的ViewHolder
        View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.rc_item, parent, false);
        return new ViewHolder(v);
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        //建立起ViewHolder中视图和数据的关联
        holder.textView.setText(mData.get(position) + position);
    }

    @Override
    public int getItemCount() {
        return mData.size();
    }

    public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {

        private TextView textView;

        public ViewHolder(View itemView) {
            super(itemView);
            textView = (TextView) itemView;
            textView.setOnClickListener(this);
        }

        //通过接口回调来实现RecyclerView的点击事件
        @Override
        public void onClick(View v) {
            if (itemClickListener != null) {
                itemClickListener.onItemClick(null, v, getPosition(), R.id.tv_item);
            }
        }
    }
}

上面就是一个非常简单却典型的RecyclerView,通过onCreateViewHolder将List item的布局转换成View,并传递给RecyclerView封装好的ViewHolder,就可以将数据和视图关联起来了,但是有一点要注意的是,Android并没有给RecyclerView增进点击事件,所以我们需要自己写接口回调,代码如图

 public AdapterView.OnItemClickListener itemClickListener;

    public void setOnItemClickListener(AdapterView.OnItemClickListener itemClickListener) {
        this.itemClickListener = itemClickListener;
    }

    public interface OnItemClickListener {
        void onItemClic(View view, int position);
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        //将布局转化为View并传递给RecyclerView封装好的ViewHolder
        View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.rc_item, parent, false);
        return new ViewHolder(v);
    }

类似ListView的List item视图

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#bebebe"
    android:textSize="40sp"
    android:layout_margin="3dp"
    android:gravity="center"
    android:id="@+id/tv_item"
    android:orientation="vertical">

</TextView>

当然,仅仅是性能也是不够的,让开发者能够更加方便的使用才是非常重要的,Google在RecyclerView中定义了LayoutManager来帮助开发者更加方便的创建不同但的布局,下面的例子就演示如何创建水平和垂直布局,当然,你也可以通过自定义LayoutManager来创建自己的布局,核心代码如下:

   mRcList.setLayoutManager(new LinearLayoutManager(this));
   mRcList.setLayoutManager(new GridLayoutManager(this));

完整代码:

package com.lgl.materialdesign;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.AdapterView;
import android.widget.Spinner;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {

    private RecyclerView mRcList;
    private RecyclerAdapter mAdapter;
    private RecyclerView.LayoutManager mLayoutManager;
    private Spinner mSpinner;
    private List<String>mData = new ArrayList<String>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mRcList = (RecyclerView) findViewById(R.id.mRcList);
        mLayoutManager = new LinearLayoutManager(this);
        mRcList.setLayoutManager(mLayoutManager);
        mRcList.setHasFixedSize(true);
        //设置显示动画
        mRcList.setItemAnimator(new DefaultItemAnimator());

        mSpinner = (Spinner) findViewById(R.id.spinner);
        mSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
            @Override
            public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
                if(position == 0){
                    //设置为线性布局
                    mRcList.setLayoutManager(new LinearLayoutManager(MainActivity.this));
                }else if(position == 1){
                    //设置为表格布局
                    mRcList.setLayoutManager(new GridLayoutManager(MainActivity.this,3));
                }else if(position == 2 ){

                }
            }

            @Override
            public void onNothingSelected(AdapterView<?> parent) {

            }
        });
        //增加测试数据
        mData.add("Android Test1");
        mData.add("Android Test2");
        mData.add("Android Test3");

        mAdapter = new RecyclerAdapter(mData);
        mRcList.setAdapter(mAdapter);

        mAdapter.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(final AdapterView<?> parent, View view, int position, long id) {
                //设置点击动画
                parent.animate().translationZ(15f).setDuration(300).setListener(new AnimatorListenerAdapter() {
                    @Override
                    public void onAnimationEnd(Animator animation) {
                        super.onAnimationEnd(animation);
                        parent.animate().translationZ(1f).setDuration(500).start();
                    }
                }).start();
            }
        });
    }

    public void addRecycler(View view){
        int position = mData.size();
        if(position>0){
            mAdapter.notifyDataSetChanged();
        }
    }

    public void delRecycler(View view){
        int position = mData.size();
        if(position>0){
            mData.remove(position-1);
            mAdapter.notifyDataSetChanged();
        }
    }
}

运行结果

-2.CardView

CardView曾经开始流行在Google+,后来越来越多的APP也引入了Card这样的布局方式,说到底,CardView也是一个容器布局,只是他提供了一种卡片的形式,Google所幸提供了一个CardView控件,方便大家使用这种布局,开发者可以设置大小和视图高度,圆角的角度等,在此之前,我们要添加依赖

 compile ‘com.android.support:cardview-v7:21.0.0+‘

同时要添加命名空间

xmlns:app="http://schemas.android.com/apk/res-auto"

我们举个例子

 <android.support.v7.widget.CardView xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/cardview"
        android:layout_width="match_parent"
        android:layout_height="80dp"
        android:layout_margin="8dp"
        app:cardBackgroundColor="@color/colorAccent"
        app:cardCornerRadius="8dp">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:text="TextView in CardView"
            android:textColor="@android:color/white"
            android:textSize="26sp" />
    </android.support.v7.widget.CardView>

运行的效果

7.Activity过渡动画

曾经的Android在activity进行跳转的时候,只是很生硬的切换,即使通过 overridePendingTransition( int inId, int outId)这个方法来给Activity增加一些切换动画,效果也只是差强人意,而在Android5.X中,Google对动画效果进行了更深一步的诠释,为Activity的转场效果设计了更加丰富的动画效果

Android5.X提供了三种Transition类型

  • 进入:一个进入的过渡动画决定Activity中的所有视图怎么进入屏幕
  • 退出:一个退出的过渡动画决定Activity中的所有视图怎么退出屏幕
  • 共享元素:一个共享元素过渡动画决定两个Activity之间的过渡,怎么共享他们的视图

其中,进人和退出效果包括

  • explode(分解)一一从屏幕中间进或出,移动视图
  • slide(滑动)——从屏幕边缘进或出,移动视图
  • fade(淡出) 一一通过改变屏幕上视图的不透明度达到添加或者移除视图

共享元素包括

  • changeBounds——改变目标视图的布局边界
  • changeCliBounds——裁剪目标视图边界
  • changeTransfrom——改变目标视图的缩放比例和旋转角度
  • changeImageTransfrom——改变目标图片的大小和缩放比例

可以发现,在Android5.X上,动画效果的种类变得更加丰富了

首先来看看普通的三种Activity过渡动画, 要使用这些动画非常简单,例如从ActivityA转到ActivityB,只需要在ActivityA中将基本的startActivity(intent)方法改为如下代码即可

startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(MainActivity.this).toBundle());

而在AchvityB中,只需要设置下如下所示代码

 getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);

或者在样式文件中设置如下所示代码

 <item name="android:windowContentTransitions">true</item>

那么接下来就可以设置进人/退出ActivityB的具体的动画效果了, 代码如下所示

getWindow().setEnterTransition(new Explode());
getWindow().setEnterTransition(new Slide());
getWindow().setEnterTransition(new Fade());

而对于共享元素的动画效果.可以借用开发者网上的一张图

要想在程序中使用共享元素的动画效果也很简单,首先需要在他的activity1布局中设置共享元素,增加元素代码

android:transitionName="XXX"

同时在activity2中,也增加一个相应的共享元素属性,如果只要一个共享元素,那么在activity1中可以这样写

startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(this,view,"share").toBundle());

使用的参数就是前面普通动画的基础上增加了共享的的view和前面取的名字,如果由多个共享元素,那么我们可以通过

 Pair.create()

来创建多个共享元素

startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(this, Pair.create(view,"share"),Pair.create(fab,"fab")).toBundle());

下面我们通过一个具体的实例来演示一下过渡动画,我们从BActivity跳转到CActivity

BActivity的XML

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <Button
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:onClick="explode"
        android:text="explode" />

    <Button
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:onClick="slide"
        android:text="slide" />

    <Button
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:onClick="fade"
        android:text="fade" />

    <Button
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:onClick="share"
        android:text="share"
        android:transitionName="share" />

    <Button
        android:id="@+id/fab_button"
        android:layout_width="56dp"
        android:layout_height="56dp"
        android:background="@mipmap/ic_launcher"
        android:elevation="5dp"
        android:onClick="explode"
        android:transitionName="fab" />

</LinearLayout>

实现逻辑

package com.lgl.materialdesign;

import android.app.ActivityOptions;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.util.Pair;
import android.view.View;

/**
 * BActivity
 * Created by lgl on 2016/5/1.
 */
public class BActivity extends AppCompatActivity {

    private Intent intent;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_b);

    }

    //设置不同的动画效果
    public void explode(View view) {
        intent = new Intent(this, CActivity.class);
        intent.putExtra("flag", 0);
        startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(this).toBundle());
    }

    //设置不同的动画效果
    public void slide(View view) {
        intent = new Intent(this, CActivity.class);
        intent.putExtra("flag", 1);
        startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(this).toBundle());
    }

    //设置不同的动画效果
    public void fade(View view) {
        intent = new Intent(this, CActivity.class);
        intent.putExtra("flag", 2);
        startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(this).toBundle());
    }

    //设置不同的动画效果
    public void share(View view) {
        View fab = findViewById(R.id.fab_button);
        intent = new Intent(this, CActivity.class);
        intent.putExtra("flag", 3);
        //创建单个共享元素
        startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(this, view, "share").toBundle());
        //创建多个共享单元
        startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(this, Pair.create(view, "share"), Pair.create(fab, "fab")).toBundle());
    }

}

再看CActivity的XML

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="15dp">

    <View
        android:id="@+id/holder_view"
        android:layout_width="match_parent"
        android:layout_height="300dp"
        android:background="?android:colorPrimary"
        android:transitionName="share" />

    <Button xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/fab_button"
        android:layout_width="56dp"
        android:layout_height="56dp"
        android:layout_alignParentEnd="true"
        android:layout_below="@id/holder_view"
        android:layout_marginRight="15dp"
        android:layout_marginTop="-26dp"
        android:background="@mipmap/ic_launcher"
        android:elevation="5dp"
        android:transitionName="fab" />

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_below="@id/holder_view"
        android:paddingTop="10dp">

        <Button
            android:id="@+id/button1"
            android:layout_width="match_parent"
            android:layout_height="60dp"
            android:layout_alignParentStart="true"
            android:layout_marginTop="10dp" />

        <Button
            android:id="@+id/button"
            android:layout_width="match_parent"
            android:layout_height="60dp"
            android:layout_below="@id/button1"
            android:layout_marginTop="10dp" />
    </RelativeLayout>

</RelativeLayout>

实现逻辑就更简单了

package com.lgl.materialdesign;

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.transition.Explode;
import android.transition.Fade;
import android.transition.Slide;
import android.view.Window;

/**
 * CActivity
 * Created by LGL on 2016/5/2.
 */
public class CActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);
        int flag = getIntent().getExtras().getInt("flag");
        switch (flag) {
            case 0:
                getWindow().setEnterTransition(new Explode());
                break;
            case 1:
                getWindow().setEnterTransition(new Slide());
                break;
            case 2:
                getWindow().setEnterTransition(new Fade());
                getWindow().setExitTransition(new Fade());
                break;
        }
        setContentView(R.layout.activity_c);
    }
}

我们运行一下就可以看到自己想要的效果了

八.Material Design动画效果

动画已经成了UI设计中一个非常重要的一个组成部分,在Android5.X的Material Design中,更是使用了大量的动画效果,同时Google官方文档也有详细的说明

1.Ripple效果

在Android5.X中,Material Design大量的使用了Ripple动画,即点就博文效果,可以通过如下代码设置波纹的背景

//波纹有边界
 android:background="?android:attr/selectableItemBackground"
 //波纹无边界
 android:background="?android:attr/selectableItemBackgroundBorderless"

波纹有边界是指波纹被限制在控件的边界中,而波纹超出边界,则是不会受到控件的限制,以圆形发散出去,我们做一个演示

XML

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/holo_blue_light"
    android:gravity="center"
    android:orientation="vertical">

    <Button
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:background="?android:attr/selectableItemBackground"
        android:text="有界波纹"
        android:textColor="@android:color/white" />

    <Button
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:background="?android:attr/selectableItemBackgroundBorderless"
        android:text="无界波纹"
        android:textColor="@android:color/white" />

</LinearLayout>

运行的效果

同样的,我们可以写一个xml文件来实现Ripple效果

<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
    android:color="@android:color/holo_blue_dark">
    <item>
        <shape android:shape="oval">
            <solid android:color="@color/colorPrimary" />
        </shape>
    </item>
</ripple>

使用方法直接设置背景就可以了,运行效果

2.Circular Reveal

这个动画效果是在Google I/O 大会上演示了好多次的,具体变现为一个View以圆形的形式展开,揭示出来,通过ViewAnimationUtils.createCircularReveal()来创建动画,代码如下:

public static Animator createCircularReveal(View view, int centerX, int centerY, float startRadius, float endRadius) {

        return new RevealAnimator(view,centerX,centerY,startRadius,endRadius);
    }

RevealAnimator的使用特别简单,主要就是几个关键的坐标点

  • centerX 动画开始的中心点X
  • centerY 动画开始的中心点Y
  • startRadius 动画开始半径
  • endRadius 动画结束半径

我们还是通过一个实例去演示一下

XML

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:gravity="center">

    <ImageView
        android:id="@+id/oval"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:background="@mipmap/ic_launcher" />

    <ImageView
        android:id="@+id/rect"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:background="@mipmap/ic_launcher" />

</LinearLayout>

逻辑代码

package com.lgl.materialdesign;

import android.animation.Animator;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.ViewAnimationUtils;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.AccelerateInterpolator;

/**
 * Circular Reveal
 * Created by LGL on 2016/5/2.
 */
public class CirActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_cir);

        final View oval = findViewById(R.id.oval);
        oval.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Animator animator = ViewAnimationUtils.createCircularReveal(oval, oval.getWidth() / 2, oval.getHeight() / 2, oval.getWidth(), 0);
                animator.setInterpolator(new AccelerateDecelerateInterpolator());
                animator.setDuration(2000);
                animator.start();
            }
        });

        final View rect = findViewById(R.id.rect);
        rect.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Animator animator = ViewAnimationUtils.createCircularReveal(rect, 0, 0, 0, (float) Math.hypot(rect.getWidth(), rect.getHeight()));
                animator.setInterpolator(new AccelerateInterpolator());
                animator.setDuration(2000);
                animator.start();
            }
        });
    }
}

这样我们就可以去实现了

3. View state changes Animation

在Android5.X中,系统提供了视图与状态改变来设置一个视图状态的切换

  • StaetListAnimator

StaetListAnimator作为视图改变时的动画效果,通常会使用Seletor来进行设置,但是以前我们设置Seletor的时候,通常是修改他的背景来达到反馈的效果,但是再现在Android5.X中就不需要这样了,可以使用动画来实现,我们用小例子来具体看看怎么实现的吧

在XML中定义一个StaetListAnimator

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_pressed="true">
        <set>
            <objectAnimator android:duration="2000" android:property="rotationX" android:valueTo="360" android:valuyeType="floatType" />
        </set>
    </item>

    <item android:state_pressed="false">
        <set>
            <objectAnimator android:duration="2000" android:property="rotationX" android:valueTo="0" android:valuyeType="floatType" />
        </set>
    </item>
</selector>

然后直接在布局中设置即可

 <Button
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:background="@drawable/animatorstate" />
  • animated-selector

animated-selector同样是一个改变动画效果的动画,

这个效果不讲,没素材

九.Toolbar

Toolbar和actionBar最大的区别就是Toolbar更加的自由,可控,这也是Google在逐渐使用Toolbar来取代actionBar的原因,要使用Toolbar就必须引用依赖

compile ‘com.android.support:appcompat-v7:23.3.0‘

同样的,我们要设置主题

Theme.AppCompat.Light.NoActionBar

然后在XML中

<android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/colorPrimaryDark"/>

在代码中获取

         mToolbar = (Toolbar) findViewById(R.id.toolbar);
        mToolbar.setLogo(R.mipmap.ic_launcher);
        mToolbar.setTitle("主标题");
        mToolbar.setSubtitle("副标题");
        setSupportActionBar(mToolbar);

而menu都是一样的,就不写了,这样一个title就写出来了

我们再来具体实现以下,我们加入一个侧滑的效果,XML代码

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/colorPrimaryDark" />

    <android.support.v4.widget.DrawerLayout
        android:id="@+id/drawer"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <!--主页内容-->
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="@android:color/holo_blue_bright"
            android:orientation="vertical">

            <Button
                android:layout_width="100dp"
                android:layout_height="match_parent"
                android:text="内容界面" />

        </LinearLayout>

        <!--侧滑菜单内容,必须制定水平重力-->
        <LinearLayout
            android:id="@+id/drawer_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="start"
            android:orientation="vertical">

            <Button
                android:layout_width="100dp"
                android:layout_height="match_parent"
                android:text="菜单界面" />

        </LinearLayout>

    </android.support.v4.widget.DrawerLayout>

</LinearLayout>

然后我们设置一下

 mToolbar = (Toolbar) findViewById(R.id.toolbar);
        mToolbar.setLogo(R.mipmap.ic_launcher);
        mToolbar.setTitle("主标题");
        mToolbar.setSubtitle("副标题");
        setSupportActionBar(mToolbar);

        getSupportActionBar().setDisplayHomeAsUpEnabled(true);
        mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer);
        mDrawerToggle = new ActionBarDrawerToggle(this,mDrawerLayout,mToolbar,0,0);
        mDrawerToggle.syncState();
        mDrawerLayout.setDrawerListener(mDrawerToggle);

运行的效果

十.Notification

Notification作为一个时间触发性的交互提示接口,让我们获得消息的时候,在状态栏,锁屏界面显示相应的消息

Google在Android5.X中,又进一步的改进了通知栏,优化了Notification,在5.X设备上一个标准的通知是这样的,长按是下图

长按会显示消息的来源

Notification会有一个从白色到灰色的动画切换效果,最终显示发出这个通知的调用者,而在我们的锁屏界面,也可以看到

我们分四重境界来讲解在Android5.X下使用Notification

1.基本的Notification

通过Notification.Builder创建一个Notification的builder,代码如下

 Notification.Builder builder = new Notification.Builder(this);

这个与AlertDialog的使用方法非常的相似,接下来,要点击Notification执行一个intent,由于这个intent的不是立即执行,而是用户触发的,所以用pendingintent来完成这个延时操作,代码如下:

Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.baidu.com"));
//构造pendingdintent
PendingIntent pendingIntent = PendingIntent.getActivities(this,0,intent,0);

这样点击PendingIntent 之后就会触发时间了,我们也可以给他增加属性

        builder.setSmallIcon(R.mipmap.ic_launcher);
        builder.setContentIntent(pendingIntent);
        builder.setAutoCancel(true);
        builder.setLargeIcon(BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher));
        builder.setContentText("Title");
        builder.setContentText("内容");
        builder.setSubText("text");

通过下面的代码发布通知栏

 //通过NotificationManager来发出
        NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        notificationManager.notify(0,builder.build());

这样我们很轻松的就创建了一个通知栏

2.折叠式Notification

折叠式Notification也是一种自定义视图的Notification,常常用于显示文本,他拥有两个视图状态,我们可以用RemoteView来帮助我们创建一个Notification视图,代码如下:

        //通过RemoteViews创建自定义视图
        RemoteViews contentView = new RemoteViews(getPackageName(),R.layout.notification);
        contentView.setTextViewText(R.id.textView,"通知栏");

其中notification的布局是这样的

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center_horizontal"
    android:orientation="vertical">

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:textColor="#ff43aebe" />

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@mipmap/ic_launcher" />

</LinearLayout>

通过如下代码,可以讲一个视图指定为Notification正常状态下的视图

  notification.contentView = contentView;

另一个展开的代码

notification.bigContentView = expandedView;

奉献完整代码

package com.lgl.materialdesign;

import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Intent;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.widget.RemoteViews;

/**
 * Notification
 * Created by LGL on 2016/5/2.
 */
public class NotificationActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_notification);

        Notification.Builder builder = new Notification.Builder(this);

        Intent intents = new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.baidu.com"));
        //构造pendingdintent
        PendingIntent pendingIntent = PendingIntent.getActivities(this, 0, new Intent[]{intents}, 0);

        builder.setSmallIcon(R.mipmap.ic_launcher);
        builder.setContentIntent(pendingIntent);
        builder.setAutoCancel(true);
        builder.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher));

        //通过RemoteViews创建自定义视图
        RemoteViews contentView = new RemoteViews(getPackageName(), R.layout.notification);
        contentView.setTextViewText(R.id.textView, "通知栏");

        Notification notification = builder.build();
        //指定视图
        notification.contentView = contentView;

        RemoteViews expandedView = new RemoteViews(getPackageName(), R.layout.notification_expanded);

        notification.bigContentView = expandedView;

        //通过NotificationManager来发出
        NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        notificationManager.notify(0, builder.build());

    }
}

3.悬挂式Notification

悬挂式Notification是Android5.X新增加的方式,Google希望通过这个方式来给用户带来更好的体验,这种被称为Headsup的Notification方式,可以在屏幕的上方产生Notification而不打扰用户的操作

在Android Sample中,Google给我们展示了这个项目,代码如下:

Notification.Builder builder = new Notification.Builder(this).setSmallIcon(R.mipmap.ic_launcher).setPriority(Notification.PRIORITY_DEFAULT).setCategory(Notification.CATEGORY_MESSAGE).setContentTitle("Headsup Notification").setContentText("I am Headsup Notification");
        Intent push = new Intent();
        push.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        push.setClass(this,MainActivity.class);
        PendingIntent pendingIntent = PendingIntent.getActivities(this,0, new Intent[]{push},PendingIntent.FLAG_CANCEL_CURRENT);
        builder.setContentText("Android 5.X Headsup Notification").setFullScreenIntent(pendingIntent,true);
        NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        nm.notify(0,builder.build());

效果

4.显示等级的Notification

最后一重境界也就是新加入的一种模式,显示登记,具体分三级

  • VISIBILITY_PRIVATE -没有锁屏的时候显示
  • VISIBILITY_PUBLIC - 表明在任何情况下都会显示
  • VISIBILITY_SECRET - 表明在pin,password等安全锁和没有锁屏的情况下显示

这一章自我感觉没啥写的,就一页,跳过吧,很少的知识点

群英传到这里,也就没有知识点可以将了,最后十三章,是两个小项目,本来不准备讲的,不过看到这个拼图还是可以讲一讲的,就准备写了,2048我很早之前就写过,这里就不重复了

笔记下载:正在上传

Demo下载:正在上传

时间: 2024-10-12 04:47:22

Android群英传笔记——第十二章:Android5.X 新特性详解,Material Design UI的新体验的相关文章

SQL必知必会 笔记 第二十二章 了解高级SQL特性

22.1约束 为正确地进行关系数据库设计,需要一种方法来保证只在表中插入合法的数据.例如,如果Orders表存储订单信息,OrderItems表存储订单详细内容,应该保证Orderitems中引用的任何订单ID存在于Orders中.类似地,在Orders表中引用的任意用户必须存在于Customers表中. 虽然可以在插入新行时进行检查,但最好不要这样做,原因如下: (1)如果在客户机层面上实施数据库完整性规则,则每个客户机都被迫要实施这些规则,但很可能会有一些客户机不实施这些规则. (2)在执行

Android群英传笔记——第四章:ListView使用技巧

Android群英传笔记--第四章:ListView使用技巧 近期也是比較迷茫.可是有一点点还是要坚持的,就是学习了.近期离职了,今天也是继续温习第四章ListView,也拖了事实上也挺久的了,listview可谓是老牌大将了,非常多的应用场景都要使用它,他也是我们用得最多的控件之中的一个了,尽管如今出来了一个RecyclerView,可是ListView的地位一时半会儿还是撼动不了的.这就促使我们更加应该去把他掌握了 一.Listview经常使用优化技巧 我们一步步来把ListView学习好

Android群英传笔记——第九章:Android系统信息和安全机制

Android群英传笔记--第九章:Android系统信息和安全机制 本书也正式的进入尾声了,在android的世界了,不同的软件,硬件信息就像一个国家的经济水平,军事水平,不同的配置参数,代表着一个android帝国的强弱,所以厂商喜欢打配置战,本节就要是讲 Android系统信息的获取 PackageManager的使用 ActivityManager的使用 Android安全机制 一. Android系统信息的获取 由于android手机的开源性,手机的配置各种各样,那些优化大师之类的东西

Android群英传笔记——第七章:Android动画机制和使用技巧

Android群英传笔记--第七章:Android动画机制和使用技巧 想来,最近忙的不可开交,都把看书给冷落了,还有好几本没有看完呢,速度得加快了 今天看了第七章,Android动画效果一直是人家中十分重要的一部分,从早期的Android版本中,由于动画机制和绘图机制的不健全,Android的人机交互备受诟病,Android从4.X开始,特别是5.X,动画越来越完善了,Google也开始重视这一方面了,我们本章学习的主要内容有 Android视图动画' Android属性动画 Android动画

Android群英传笔记——摘要,概述,新的出发点,温故而知新,能够为师矣!

Android群英传笔记--摘要.概述,新的出发点,温故而知新.能够为师矣! 当工作的越久,就越感到力不从心了,基础和理解才是最重要的,所以买了两本书,医生的<Android群英传>和主席的<Android开发艺术探索>.主要是再全面点的把自己所学的知识消化,这样也就不枉自己天天熬夜学习了,如今群英传快看完了.准备又一次再看一遍,同一时候把笔记以博客的形式记录下来,这样或许更加的深刻,然后再消磨一下主席的那本书,这本书有视频解说,更加好,所以估计以后应该非常少再写其它的博客,只是工

Android群英传笔记——第八章:Activity与Activity调用栈分析

Android群英传笔记--第八章:Activity与Activity调用栈分析 开篇,我们陈述一下Activity,Activity是整个应用用户交互的核心组件,了解Activity的工作模式,生命周期和管理方式,是了解Android的基础,本节主讲 Activity的生命周期与工作模式 Activity调用栈管理 一.Activity Activity作为四大组建出现平率最高的组件,我们在哪里都能看到他,就让我们一起先来了解一下他的生命周期 1.起源 Activity是用户交互的第一接口,他

“全栈2019”Java第六十四章:接口与静态方法详解

难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第六十四章:接口与静态方法详解 下一章 "全栈2019"Java第六十五章:接口与默认方法详解 学习小组 加入同步学习小组,共同交流与进步. 方式一:关注头条号Gorhaf,私信"Java学习小组". 方式二:关注公众号Gorhaf,回复"Java学习小组&qu

“全栈2019”Java第九十六章:抽象局部内部类详解

难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第九十六章:抽象局部内部类详解 下一章 "全栈2019"Java第九十七章:在方法中访问局部内部类成员详解 学习小组 加入同步学习小组,共同交流与进步. 方式一:关注头条号Gorhaf,私信"Java学习小组". 方式二:关注公众号Gorhaf,回复"Java学

4.0【OSPF】NP十二班第六天OSPF其它LSA详解&域间汇总及过滤-2

OSPF其它LSA详解&域间汇总及过滤 域间汇总 在OSPF里面有两种角色可以做汇总: 1.ABR:可以生成3类,针对3类域间路由做汇总 2.ASBR:生成5类,可以对外部路由做汇总 [实验配置]2:20 在R1上创建lo0口宣告进OSPF,ABR会针对这条链路信息通告一个3类LSA 在R1上看这条链路是否被宣告进OSPF: show  ip ospf database router self-originate 逻辑接口/物理接口但没有邻居关系,会被通告成stub network 末节网络 在