Material Design for Android

来源:互联网 发布:js 数组 sort 编辑:程序博客网 时间:2024/05/19 00:13

Material 主题

想要使用Material Design,就需要先设置主题,系统自带主题
- @android:style/Theme.Material(深色版本)
- @android:style/Theme.Material.Light(浅色版本)
- @android:style/Theme.Material.Light.DarkActionBar

ThemeColors.png

常用的样式属性:

 <style name="RedTheme" parent="android:Theme.Material">    <!-- 状态栏颜色,会被statusBarColor效果覆盖-->    <item name="android:colorPrimaryDark">@color/status_red</item>    <!-- 状态栏颜色,继承自colorPrimaryDark -->    <item name="android:statusBarColor">@color/status_red</item>    <!-- actionBar颜色 -->    <item name="android:colorPrimary">@color/action_red</item>    <!-- 背景颜色 -->    <item name="android:windowBackground">@color/window_bg_red</item>    <!-- 底部栏颜色 -->    <item name="android:navigationBarColor">@color/navigation_red</item>    <!-- ListView的分割线颜色,switch滑动区域色-->    <item name="android:colorForeground">@color/fg_red</item>    <!-- popMenu的背景色 -->    <item name="android:colorBackground">@color/bg_red</item>    <!-- 控件默认颜色 ,效果会被colorControlActivated取代  -->    <item name="android:colorAccent">@color/control_activated_red</item>    <!-- 控件默认时颜色  -->    <item name="android:colorControlNormal">@color/control_normal_red</item>    <!-- 控件按压时颜色,会影响水波纹效果,继承自colorAccent  -->    <item name="android:colorControlHighlight">@color/control_highlight_red</item>    <!-- 控件选中时颜色 -->    <item name="android:colorControlActivated">@color/control_activated_red</item>    <!-- Button的默认背景 -->    <item name="android:colorButtonNormal">@color/button_normal_red</item>    <!-- Button,textView的文字颜色  -->    <item name="android:textColor">@color/white_text</item>    <!-- RadioButton checkbox等控件的文字 -->    <item name="android:textColorPrimaryDisableOnly">@color/white_text</item>    <!-- actionBar的标题文字颜色 -->    <item name="android:textColorPrimary">@color/white_text</item></style>

定义阴影与裁剪视图

Z轴属性

视图的 Z 值包含两个组件:
- 高度:静态组件。
- 转换:用于动画的动态组件。
Z = elevation + translationZ

android:elevation   //静态Z轴的高度。相对于父控件的高度android:translationZ    //Z轴偏移的高度。

Z轴的大小会决定视图的遮盖关系,Z值大的View会优先显示于UI中.
你可以在代码中使用 View.setElevation()View.setTranslationZ()来设置Z的大小,在属性动画中也提供了关于Z轴的方法, ViewPropertyAnimator.z()ViewPropertyAnimator.translationZ()

裁剪视图

将可裁剪的view裁剪成各种形状

 holder.profileImageView.setOutlineProvider(new ViewOutlineProvider() {                @Override                public void getOutline(View view, Outline outline) {                    int r = Math.min(view.getHeight(), view.getWidth());                    outline.setRoundRect(0, 0, r, r, r / 2);                }            });            holder.profileImageView.setClipToOutline(true);

上面这句话就可以很简单地设置imageview为圆形,但是这种方式的绘制成本是很高的,所以这里只是演示作用

调色板Palette

 // Synchronous Palette p = Palette.from(bitmap).generate(); // Asynchronous Palette.from(bitmap).generate(new PaletteAsyncListener() {     public void onGenerated(Palette p) {         // Use generated instance     } });

根据图片的颜色分配来设置statusBar的颜色值:

// Set the background and text colors of a toolbar given a// bitmap image to matchpublic void setToolbarColor(Bitmap bitmap) {  // Generate the palette and get the vibrant swatch  // See the createPaletteSync() and checkVibrantSwatch() methods  // from the code snippets above  Palette p = createPaletteSync(bitmap);  Palette.Swatch vibrantSwatch = checkVibrantSwatch(p);  // Set the toolbar background and text colors  Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);  toolbar.setBackgroundColor(vibrantSwatch.getRgb());  toolbar.setTitleTextColor(vibrantSwatch.getTitleTextColor());}

触摸反馈

  • ?android:attr/selectableItemBackground 指定有界的波纹。
  • ?android:attr/selectableItemBackgroundBorderless 指定越过视图边界的波纹。 也就是波纹动画会涉及到父容器.

如果要改变默认触摸反馈颜色,请使用主题的 android:colorControlHighlight 属性

自定义波纹颜色:

<?xml version="1.0" encoding="utf-8"?><ripple xmlns:android="http://schemas.android.com/apk/res/android"        android:color="#ffff0000"></ripple>

揭露效果

效果:
reveal

void enterReveal(final View targetView) {        //获取view的中心点        int cx = targetView.getMeasuredWidth() / 2;        int cy = targetView.getMeasuredHeight() / 2;        int finalRadius = Math.max(targetView.getWidth(), targetView.getHeight()) / 2;        Animator anim =                ViewAnimationUtils.createCircularReveal(targetView, cx, cy, 0, finalRadius);        targetView.setVisibility(View.VISIBLE);        anim.start();    }    void exitReveal(final View targetView) {        int cx = targetView.getMeasuredWidth() / 2;        int cy = targetView.getMeasuredHeight() / 2;        int initialRadius = targetView.getWidth() / 2;        Animator anim =                ViewAnimationUtils.createCircularReveal(targetView, cx, cy, initialRadius, 0);        anim.addListener(new AnimatorListenerAdapter() {            @Override            public void onAnimationEnd(Animator animation) {                super.onAnimationEnd(animation);                targetView.setVisibility(View.INVISIBLE);            }        });        anim.start();    }

定制动画

指定定制转换

开启指定定制转换两种方式
- 在主题文件中设置<item name="android:windowContentTransitions">true</item>
- 在代码中设置getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);

以及其他效果同样可以在theme中定义

<style name="BaseAppTheme" parent="android:Theme.Material">  <!-- enable window content transitions -->  <item name="android:windowContentTransitions">true</item>  <!-- specify enter and exit transitions -->  <item name="android:windowEnterTransition">@transition/explode</item>  <item name="android:windowExitTransition">@transition/explode</item>  <!-- specify shared element transitions -->  <item name="android:windowSharedElementEnterTransition">    @transition/change_image_transform</item>  <item name="android:windowSharedElementExitTransition">    @transition/change_image_transform</item></style>

change_image_transform 转换定义如下:

<!-- res/transition/change_image_transform.xml --><!-- (see also Shared Transitions below) --><transitionSet xmlns:android="http://schemas.android.com/apk/res/android">  <changeImageTransform/></transitionSet>

代码中开启:

// inside your activity (if you did not enable transitions in your theme)getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);// set an exit transitiongetWindow().setExitTransition(new Explode());

并在startActivity时设置:

    //没有共享元素    ActivityOptions options=ActivityOptions.makeSceneTransitionAnimation(this);    //1个共享元素    ActivityOptions options = ActivityOptions    .makeSceneTransitionAnimation(this, sharedElementView, "sharedElementName");    //多个共享元素    ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(this,    Pair.create(sharedElementView1, "sharedElementName1"),    Pair.create(sharedElementView2, "sharedElementName2"));    // 启动Activity    startActivity(intent,ActivityOptionsCompat.                            makeSceneTransitionAnimation(MainActivity.this).toBundle());

Android默认提供了3中场景转换:
- Explode
- Fade
- Slide
这里演示Slide效果,这是默认new Slide()的效果:

slide1.gif

可以看到toolbar都一起下滑了,这时候需要自定义.
在res下新建transition文件夹,然后建立slide.xml:

<?xml version="1.0" encoding="utf-8"?><slide xmlns:android="http://schemas.android.com/apk/res/android"       android:slideEdge="right"       android:duration="1000">    <targets>        <!-- if using a custom Toolbar container, specify the ID of the AppBarLayout -->        <target android:excludeId="@id/app_bar_layout" />        <target android:excludeId="@android:id/statusBarBackground"/>        <target android:excludeId="@android:id/navigationBarBackground"/>    </targets></slide>

或者设置组合动画:

<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">    <fade        android:duration="300"        android:fadingMode="fade_in_out">        <targets>            <!-- if using a custom Toolbar container, specify the ID of the AppBarLayout -->            <target android:excludeId="@android:id/statusBarBackground"/>            <target android:excludeId="@android:id/navigationBarBackground"/>        </targets>    </fade>    <slide xmlns:android="http://schemas.android.com/apk/res/android"           android:duration="1000"           android:slideEdge="bottom">        <targets>            <!-- if using a custom Toolbar container, specify the ID of the AppBarLayout -->            <target android:excludeId="@id/app_bar_layout"/>            <target android:excludeId="@android:id/statusBarBackground"/>            <target android:excludeId="@android:id/navigationBarBackground"/>        </targets>    </slide></transitionSet>

代码中设置:

getWindow().setExitTransition(TransitionInflater.from(this).                inflateTransition(R.transition.slide));

中android:excludeId的作用就是排除这些ID,让他们不会有这个转场效果

自定义效果:

slide2.gif

共享元素

也就是上面图中图片的转场效果
1.在两个acitvity的布局中设置`android:transitionName=”profile”,要保证名字一样
2.

Intent intent = new Intent(MainActivity.this, TransitionActivity.class);                    TransitionActivity.imageUrls = imageUrls[position];                    Pair<View, String> p1 = Pair.create((View) holder.profileImageView, "profile");                    //如果是多个效果则再设置Pair                    ActivityOptionsCompat options = ActivityOptionsCompat.                            makeSceneTransitionAnimation(MainActivity.this, p1);                    startActivity(intent, options.toBundle());

其次你需要在activity结束的时候设置结束动画:

@Overridepublic boolean onOptionsItemSelected(MenuItem item) {    switch (item.getItemId()) {        // Respond to the action bar's Up/Home button        case android.R.id.home:            supportFinishAfterTransition();            return true;    }    return super.onOptionsItemSelected(item);}

在fragment中使用共享元素

// Inflate transitions to apply    Transition changeTransform = TransitionInflater.from(this).          inflateTransition(R.transition.change_image_transform);    Transition explodeTransform = TransitionInflater.from(this).          inflateTransition(android.R.transition.explode);    // Setup exit transition on first fragment    fragmentOne.setSharedElementReturnTransition(changeTransform);    fragmentOne.setExitTransition(explodeTransform);    // Setup enter transition on second fragment    fragmentTwo.setSharedElementEnterTransition(changeTransform);    fragmentTwo.setEnterTransition(explodeTransform);    // Find the shared element (in Fragment A)    ImageView ivProfile = (ImageView) findViewById(R.id.ivProfile);    // Add second fragment by replacing first     FragmentTransaction ft = getFragmentManager().beginTransaction()            .replace(R.id.container, fragmentTwo)            .addToBackStack("transaction")            .addSharedElement(ivProfile, "profile");    // Apply the transaction    ft.commit();

为视图状态改变添加动画

StateListAnimator 类别让您能够定义视图状态改变时运行的动画

在xml中定义:

<?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="@android:integer/config_shortAnimTime"                            android:propertyName="translationY"                            android:valueTo="50dp"                            android:valueType="floatType"/>        </set>    </item>    <item          android:state_pressed="false">        <set>            <objectAnimator android:duration="@android:integer/config_shortAnimTime"                            android:propertyName="translationY"                            android:valueTo="0dp"                            android:valueType="floatType"/>        </set>    </item></selector>

在view中设置 android:stateListAnimator="@drawable/statelist"
注意在imageView中要设置 android:clickable="true"
我这里是设置点击的时候将view的Y轴移动50dp,松手的时候回归原位.效果图如下:
stateList.gif

代码中使用AnimationInflater.loadStateListAnimator()方法,并以 View.setStateListAnimator() 方法将设置动画.在Material Design主题中,Button默认会有一个Z轴动画,便是我们看到的点击出现阴影,如果你想取消该效果,则设置android:stateListAnimator属性为@null

视图层级动画

这里顺便提一下场景,早在4.4的时候,我们可以通过场景的动画做很多特效,使用TransitionManager.beginDelayedTransition方法

DelayedTransition0.gif

DelayedTransition.gif

代码如下:

int selected = gridLayout.indexOfChild(v);        if(selected > 0) {            View cur = gridLayout.getChildAt(0);            int currentId = cur.getId();            int selectedId = v.getId();            TransitionManager.beginDelayedTransition(gridLayout,                    new ChangeBounds());            gridLayout.removeView(v);            gridLayout.addView(v, 0);            GridLayout.LayoutParams params =                    (GridLayout.LayoutParams)cur.getLayoutParams();            cur.setLayoutParams(v.getLayoutParams());            v.setLayoutParams(params);            if(currentId == R.id.item_2c ||                    (currentId == R.id.item_2b &&                            selectedId == R.id.item_2c)) {                gridLayout.removeView(cur);                gridLayout.addView(cur);            }        }

TransitionManager的基本流程:
- 捕获启动状态
- 进行布局的变化
- 捕获结束状态
- 运行动画

可以自定义的类型:

  • slide (entering/exiting from an edge)
  • changeBounds (resizing or moving)
  • fade (changing visibility)
  • changeClipBounds (adjusting boundary where view is cut off)
  • changeTransform (adjusting scale/rotation)
  • changeImageTransform (adjusting size, shape, scaleType of an image)
  • pathMotion (allows movement along a path)
  • explode (slide in some direction depending on epicenter)
  • recolor (change background or next color)

详情可参考 https://guides.codepath.com/android/View-Hierarchy-Animations#overview

Material Design控件

ToolBar

主题中一些常用属性:

<style name="ToolbarTheme" parent="AppTheme">        <!-- 设置 toolbar 溢出菜单的文字的颜色 -->        <item name="android:textColor">@color/colorAccent</item>        <!-- 设置toolbar 溢出菜单的字体大小-->        <item name="android:textSize">18sp</item>        <!-- 设置 显示在tooBar上菜单文字的颜色 -->        <item name="actionMenuTextColor">@color/colorAccent</item>        <!-- 设置 toolBar溢出菜单颜色 (三个点那个图标) -->        <item name="android:textColorSecondary">@color/md_white_1000</item>        <!--<item name="actionOverflowButtonStyle">@style/over_flow</item>-->        <!--<item name="actionOverflowMenuStyle">@style/over_flow_popList</item>-->    </style>    <!--修改溢出菜单图标 -->    <style name="over_flow" parent="Base.Widget.AppCompat.ActionButton.Overflow">        <item name="srcCompat">@mipmap/ic_launcher</item>        <item name="android:background">?attr/actionBarItemBackground</item>        <item name="android:contentDescription">@string/abc_action_menu_overflow_description</item>        <item name="android:minWidth">@dimen/abc_action_button_min_width_overflow_material</item>        <item name="android:minHeight">@dimen/abc_action_button_min_height_material</item>    </style>    <!--溢出菜单样式 -->    <style name="over_flow_popList" parent="Base.Widget.AppCompat.ListPopupWindow">        <item name="overlapAnchor">true</item>        <item name="android:dropDownSelector">?attr/listChoiceBackgroundIndicator</item>        <item name="android:popupBackground">@drawable/abc_popup_background_mtrl_mult</item>        <item name="android:dropDownVerticalOffset">0dip</item>        <item name="android:dropDownHorizontalOffset">0dip</item>        <item name="android:dropDownWidth">wrap_content</item>    </style>

代码中设置溢出菜单:

@Override    public boolean onCreateOptionsMenu(Menu menu) {        getMenuInflater().inflate(R.menu.home_menu, menu);        return true;    }    @Override    public boolean onOptionsItemSelected(MenuItem item) {        switch (item.getItemId()) {            case R.id.github:                return true;            case R.id.me:                return true;        }        return super.onOptionsItemSelected(item);    }fragment:@Overridepublic void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {    inflater.inflate(R.menu.menu_sample, menu);    super.onCreateOptionsMenu(menu,inflater);}

menu文件:

<?xml version="1.0" encoding="utf-8"?><menu xmlns:android="http://schemas.android.com/apk/res/android"      xmlns:app="http://schemas.android.com/apk/res-auto">    <!--     ifRoom 表示 如果Toolbar 上有显示空间就显示在Toolbar 上,如果没有空间就展示在溢出菜单里     never 表是总是显示在溢出菜单里     always 表示总是显示在Toolbar 上    -->    <item        android:id="@+id/search"        android:icon="@drawable/search"        android:title="search"        app:showAsAction="ifRoom"        />    <item        android:id="@+id/github"        android:icon="@drawable/github"        android:title="github"        app:showAsAction="never"        />    <item        android:id="@+id/me"        android:icon="@drawable/me"        android:title="me"        app:showAsAction="never"        /></menu>

代码中设置属性:

       // 设置 toolbar 背景色        toolbar.setBackgroundColor(getResources().getColor(R.color.colorPrimary));        // 设置 Title        toolbar.setTitle(R.string.toolbar_title);        //  设置Toolbar title文字颜色        toolbar.setTitleTextColor(getResources().getColor(R.color.white));        // 设置Toolbar subTitle        toolbar.setSubtitle(R.string.sub_title);        toolbar.setSubtitleTextColor(getResources().getColor(R.color.white));        // 设置logo        toolbar.setLogo(R.mipmap.ic_launcher);        // 设置 NavigationIcon 点击事件        toolbar.setNavigationOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                finish();            }        });        //设置 Toolbar menu        toolbar.inflateMenu(R.menu.home_menu);        // 设置溢出菜单的图标        toolbar.setOverflowIcon(getResources().getDrawable(R.drawable.toolbar));

CoordinatorLayout

CoordinatorLayout is a super-powered FrameLayout
用于协调子view,子view可以通过behavior 来做出相应的响应动作.CoordinatorLayout的使用核心就是Behavior

AppbarLayout

在使用CoordinatorLayout时,我们最常用的组合就是AppbarLayout,AppbarLayout 严重依赖于CoordinatorLayout,必须用于CoordinatorLayout 的直接子View
当某个View滑动的时候,AppbarLayout 可以通过app:layout_scrollFlags来指定动作,以便让AppbarLayout 中的子view做出响应.
layout_scrollFlags有5种动作:
- scroll
- enterAlways
- enterAlwaysCollapsed
- exitUntilCollapsed
- snap

scroll

表示可滚动view滚动时,设置了该属性的view也会随之滚动
app:layout_scrollFlags="scroll"

enterAlways

拥有scroll的效果,并且可滚动view向下滑时,设置了该属性的view也会向下滑
app:layout_scrollFlags="scroll|enterAlways"

enterAlwaysCollapsed

下滑时,展开折叠(当可滚动view滑动到最顶部的时候)
app:layout_scrollFlags="scroll|enterAlways|enterAlwaysCollapsed"

exitUntilCollapsed

下滑时展开折叠,上滑到最顶部时,如果没有设置最小高度以及没有toolbar则会将整个内容向上移动
app:layout_scrollFlags="scroll|exitUntilCollapsed"

如果没有设置最小高度有toolBar则会以toolBar的高度为最小高度

snap

在滑动时手松开了,它自己会根据位置来回弹位置
app:layout_scrollFlags="scroll|snap"

注意: 你想使用这种动画必须先设置scroll属性,没有这个属性是触发滑动的

AppbarLayout 的几个重要方法
  • addOnOffsetChangedListener 当AppbarLayout里的子view偏移发生改变的时候回调。

  • getTotalScrollRange 返回AppbarLayout 所有子View的滑动范围

  • removeOnOffsetChangedListener 移除监听器

  • setExpanded (boolean expanded, boolean animate)设置AppbarLayout 是展开状态还是折叠状态,animate 参数控制切换到新的状态时是否需要动画

  • setExpanded (boolean expanded) 设置AppbarLayout 是展开状态还是折叠状态,默认有动画

CollapsingToolbarLayout

当我们想要在toolbar展开折叠时放入一下其他的view比如图片,那么就使用CollapsingToolbarLayout 来包装toolBar。
之前设置标题的时候都是设置toolBar的标题,现在则可以根据CollapsingToolbarLayout来设置

 CollapsingToolbarLayout collapsingToolbar =              (CollapsingToolbarLayout) findViewById(R.id.collapsing_toolbar); collapsingToolbar.setTitle("Title");

当使用CollapsingToolbarLayout 的时候,status bar 应该设置半透明 (API 19)或者透明 (API 21)

<!-- res/values-v19/styles.xml --><style name="AppTheme" parent="Base.AppTheme">    <item name="android:windowTranslucentStatus">true</item></style><!-- res/values-v21/styles.xml --><style name="AppTheme" parent="Base.AppTheme">    <item name="android:windowDrawsSystemBarBackgrounds">true</item>    <item name="android:statusBarColor">@android:color/transparent</item></style>

当你设置了透明属性,请保证你的跟布局设置android:fitsSystemWindow="true"来达到好的视觉效果.

其他常用属性:
- contentScrim:app:contentScrim="@color/colorPrimary"设置内容背景
- layout_collapseMode:app:layout_collapseMode="parallax"视觉差效果
- layout_collapseMod:app:layout_collapseMode="pin"固定子view

0 0