Android L Material Design新UI元素/动画

版权声明:本文为博主原创文章,转载请注明出处。 https://blog.csdn.net/dahaohan/article/details/52087603

Ripple Drawable

//在控件使用默认的有界限/无界水波纹效果
android:background="?android:attr/selectableItemBackground"
android:background="?android:attr/selectableItemBackgroundBorderless"
//实质对应的分别为以下两个drawable文件
<ripple xmlns:android="http://schemas.android.com/apk/res/android"  
    android:color="?attr/colorControlHighlight">  
    <item android:id="@id/mask">  
        <color android:color="@color/white" />  
    </item>  
</ripple>  

<ripple xmlns:android="http://schemas.android.com/apk/res/android"  
    android:color="?attr/colorControlHighlight" />  

其核心就是提供了一个RippleDrawable对象,官方的介绍连接:
https://developer.android.com/reference/android/graphics/drawable/RippleDrawable.html

自定义水波纹效果的drawable文件:

<?xml version="1.0" encoding="utf-8"?>

<ripple xmlns:android="http://schemas.android.com/apk/res/android"
    android:color="#00ff00"
    android:radius="50dp">

    <item
        android:id="@android:id/mask"
        android:drawable="@android:color/white"></item>

    <item>
        <selector>
            <item android:state_pressed="true">
                <shape android:shape="oval">
                    <solid android:color="@color/colorPrimary"></solid>
                </shape>
            </item>

            <item>
                <shape android:shape="oval">
                    <solid android:color="@color/colorAccent"></solid>
                </shape>
            </item>
        </selector>
    </item>

</ripple>
  • Ripple Drawable Mask

    A touch feedback drawable may contain multiple child layers, including a special mask layer that is not drawn to the screen. A single layer may be set as the mask from XML by specifying its android:id value as mask. At run time, a single layer may be set as the mask using setId(…, android.R.id.mask) or an existing mask layer may be replaced using setDrawableByLayerId(android.R.id.mask, …).

    If a mask layer is set, the ripple effect will be masked against that layer before it is drawn over the composite of the remaining child layers.
    If no mask layer is set, the ripple effect is masked against the composite of the child layers.

    上述是官方对ripple drawable layer与mask layer的一些说明,可见作为一个反馈效果drawable可以包含很多“图层”像上述的有多个item标签含有一个mask layer和一个根据状态改变的shape layer。
    而mask layer是不会在屏幕上绘制的,也可以简单粗暴的理解为mask layer就是声明了ripple drawable的绘制边界,也就是水波纹效果的显示区域由mask layer决定;当木有主动指明mask layer时,mask layer区域则为其它子layer的区域组合而成的并集区域。

    If no child layers or mask is specified and the ripple is set as a View background, the ripple will be drawn atop the first available parent background within the View’s hierarchy. In this case, the drawing region may extend outside of the Drawable bounds.
    当然木有子layer也木有声明mask layer的情况下,则会取该控件的上一级的父控件作为绘制区域,即为无边界的水波纹效果。

<ripple xmlns:android="http://schemas.android.com/apk/res/android"  
    android:color="#00ff00" />  

Circular Reveal

这其实与其它的Animator动画类似,Circular Reveal就是新增的一个圆形扩散动画;

ViewAnimationUtils#createCircularReveal(View view, int centerX, int centerY, float startRadius, float endRadius)
  • view - view to reveal
  • centerX - start X coordinate of reveal
  • centerY - start Y coordinate of reveal
  • startRadius - start radius. In most cases - 0
  • endRadius - end radius - depends on your view’s bounds
        final View image = findViewById(R.id.image_view);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Animator animator = ViewAnimationUtils.createCircularReveal(image,
                        image.getWidth(),
                        image.getHeight(),
                        0,
                        image.getHeight() * 2);
                animator.setInterpolator(new AccelerateDecelerateInterpolator());
                animator.setDuration(2000);
                animator.start();

            }
        });

跟其它Animator类似,一样可以设置监听器/加减速度效果/周期等。

Transition Animation

应用内会有多个activity页面或者说场景,在各个activity之间加入酷炫统一的切换动画更能吸引用户。
在KitKat引入Scene和Transition之前一般会使用overridePendingTransition函数或者主题中指定过渡动画效果:

        //在主题内指定特定的过渡动画
        //当我们从 A1 启动 A2 时,A1 从屏幕上消失,这个动画叫做 android:activityOpenExitAnimation
        //当我们从 A1 启动 A2 时,A2 出现在屏幕上,这个动画叫做 android:activityOpenEnterAnimation
        //当我们从 A2 退出回到 A1 时,A2 从屏幕上消失,这个叫做 android:activityCloseExitAnimation
        //当我们从 A2 退出回到 A1 时,A1 出现在屏幕上,这个叫做 android:activityCloseEnterAnimation
        <item name="android:activityOpenEnterAnimation">@android:anim/slide_in_left</item>
        <item name="android:activityOpenExitAnimation">@android:anim/slide_out_right</item>
        <item name="android:activityCloseEnterAnimation">@android:anim/fade_in</item>
        <item name="android:activityCloseExitAnimation">@android:anim/fade_out</item>

         <!--窗体进入动画-->
        <item name="android:windowEnterAnimation">@anim/left_in</item>
        <!--窗体退出动画-->
        <item name="android:windowExitAnimation">@anim/right_out</item>
        //或者代码设置
        overridePendingTransition(R.anim.left_in, R.anim.right_out);

详细可参考博文:http://my.oschina.net/ososchina/blog/355239

Material Design在Transition基础上增强了Activity之间的场景转换动画,可以使用android:windowContentTransitions 启用窗口内容转换,可在您的风格style中指定进入、退出以及共享元素转换,就可以像使用transition框架做动画一样来设置更加丰富activity间的过场动画,特别是transition的共享元素的动画使得页面切换更加生动自然:

Step 1:

<!-- 在主题style内开启并设置对应的transition动画 -->
<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>

//或者在代码内activity内使用如下函数开启/设置对应的动画
// inside your activity (if you did not enable transitions in your theme)
getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);

// set an exit transition
getWindow().setExitTransition(new Explode());
//getWindow().setEnterTransition()
//getWindow().setExitTransition()
//Window.setSharedElementEnterTransition()
//Window.setSharedElementExitTransition()

Step 2:

    //在activity跳转时使用ActivityOptions启用已经设定好的transition
    //下面一句,即可启用activity的EnterTransition
    startActivity(intent,
                  ActivityOptions.makeSceneTransitionAnimation(this).toBundle());


    //启用更酷炫SharedElementEnterTransition,需要稍微多一点步骤

    //所谓的共享元素transition即表示,退出和将启动前后两个activity有一个或多个“共享的Ui视图元素”通常
    //是ImageView等;就是在两个布局xml layout文件内使用android:transitionName将两个“共享”的ui控件设置
    //为同样的名字,告诉transition framework哪些控件需要作为“共享元素”完成动画。

        //main activity xml
        <?xml version="1.0" encoding="utf-8"?>
        <RelativeLayout 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"
            tools:context="com.example.jokerlee.androidlsample.MainActivity"
            android:background="@color/colorPrimary"
            android:fitsSystemWindows="true">

            <!-- 该imageView作为共享元素,设置transitionName为share -->
            <ImageView
                android:id="@+id/share_ui"
                android:layout_width="99dp"
                android:layout_height="45dp"
                android:layout_centerInParent="true"
                android:background="#00ff00"
                android:transitionName="share" />

            <Button
                android:id="@+id/button"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Test button" 
               android:layout_centerHorizontal="true"
              android:layout_alignParentBottom="true"
                android:layout_marginBottom="30dp"
                />
        </RelativeLayout>


//activity 1 xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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:background="@color/colorAccent">

<!-- 该imageView作为共享元素,同样设置transitionName为share -->
    <ImageView
        android:layout_width="66dp"
        android:layout_height="66dp"
        android:layout_centerInParent="true"
        android:background="#00ffff"
        android:transitionName="share" />
</RelativeLayout>

//在代码内也可以通过View.setTransitionName() 
//设置统一的transitionName
//shareUi.setTransitionName("share");
//in this activity
final View shareUi = findViewById(R.id.share_ui);
assert button != null;
button.setOnClickListener(new View.OnClickListener() {
       @Override
       public void onClick(View v) {                                                                  
           Intent intent = new      
           Intent(MainActivity.this,Activity1.class);
            //需要通过ActivityOptions告诉
            //需要共享的ui view的实例以及
            //共享ui的transitionName
            ActivityOptions options = ActivityOptions
            .makeSceneTransitionAnimation(
            MainActivity.this,shareUi, "share");
            startActivity(intent, options.toBundle());
       }
  });

//若有多个共享元素,可以使用
ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(this,
        Pair.create(view1, "agreedName1"),
        Pair.create(view2, "agreedName2"));

Step 3:

启用了EnterTransition,进入了新的activity1;
那backTo原来的mainActivity的反向动画仅仅需要
使用Activity.finishAfterTransition() 方法取代原来的 Activity.finish()

关于Activity Transition的一些直观效果可以参见:http://blog.csdn.net/ljx19900116/article/details/41806889

google官方Sample:https://github.com/googlesamples/android-ActivitySceneTransitionBasic

Transition是什么鬼

Maybe一些童鞋并不了解transition framework,transition动画是什么鬼?
通俗点说呢,之前的动画Animation只作用在单个view上的,比如一个ImageView的平移/缩放/旋转;不论同时执行几个动画,其动画执行target仅仅是一个单独的View。

而Transition framework则是一个Group-level animations,就是用于同时作用于多个view的动画集合,其动画是作用于一个layout内的所有views.
The framework animates each view by changing one or more of its property values over time between the initial or starting view hierarchy and the final or ending view hierarchy.

如下图所示,layout内就只有四个圆行的View,不同场景就是位置/大小变换;使用以前的动画方式则得自己控制给每个view加一个animation并且控制每一个animation执行的时机,动画的长短等等;
使用transition framework则可以使用内置的一些transition轻松完成这种多View同时动画的生动效果。

这里写图片描述

具体使用可参考:http://www.tuicool.com/articles/zAZNVn

官方Transitions Framework文档:https://developer.android.com/training/transitions/overview.html

Google官方Transition Sample:https://github.com/googlesamples/android-BasicTransition

Curved Motion

    //曲线动画主要由新增的基于贝塞尔曲线或 Path 对象的全新插入器PathInterpolator以及
    //属性动画ObjectAnimator新增的可以传入path作为运动轨迹的函数体现。
        final View shareUi = findViewById(R.id.share_ui);

        Path path = new Path();
        path.lineTo(300,300);
        path.lineTo(600,400);

        //ObjectAnimator.ofFloat(view, View.X, View.Y, path);构造函数能够给定一个path为移动轨迹
        final ObjectAnimator animator = ObjectAnimator.ofFloat(shareUi,View.X,View.Y,path);
        animator.setDuration(2000);

        //PathInterpolator插入器在一个 1x1 的正方形内指定一个运动曲线,定位点位于 (0,0) 以及 (1,1),
        //而控制点则使用构造函数参数指定,内置几个基本曲线资源
        //@interpolator/fast_out_linear_in.xml
        //@interpolator/fast_out_slow_in.xml
        //@interpolator/linear_out_slow_in.xml

        Interpolator pathInterpolator = AnimationUtils.loadInterpolator(this,android.R.interpolator.fast_out_slow_in);
        animator.setInterpolator(pathInterpolator);

        shareUi.setTransitionName("share");

        assert button != null;
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                animator.start();
            }
        });

Animating View State Changes

新增的StateListAnimator可以给视图状态改变增加动画效果,也就是在之前的selector drawable基础上加入了动画的tag

该state_list_drawable.xml示例,在按下时缩小view,松开恢复原本大小

?xml version="1.0" encoding="utf-8"?>
<!-- animate the translationZ property of a view when pressed -->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_pressed="true">
        <set>
            <objectAnimator android:propertyName="scaleX"
                android:duration="@android:integer/config_shortAnimTime"
                android:valueTo="0.6"
                android:valueType="floatType"/>
            <!-- you could have other objectAnimator elements
                 here for "x" and "y", or other properties -->
        </set>
    </item>
    <item >
        <set>
            <objectAnimator android:propertyName="scaleX"
                android:duration="100"
                android:valueTo="1"
                android:valueType="floatType"/>
        </set>
    </item>
</selector>

注意给控件设置StateListAnimator动画需要使用的为android:stateListAnimator或者代码函数:

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Test button"
        android:layout_centerHorizontal="true"
        android:layout_alignParentBottom="true"
        android:stateListAnimator="@drawable/state_list_drawable"
        android:layout_marginBottom="30dp"
        />

    //使用函数在代码内加载
    StateListAnimator stateListAnimator = AnimatorInflater.loadStateListAnimator(this,R.drawable.state_list_drawable);
    button.setStateListAnimator(stateListAnimator);

新增的AnimatedStateListDrawable 更是可以在View按下状态变化时实现类似帧动画的效果:

<animated-selector
    xmlns:android="http://schemas.android.com/apk/res/android">

    <!-- provide a different drawable for each state-->
    <item android:id="@+id/pressed" android:drawable="@drawable/drawableP"
        android:state_pressed="true"/>
    <item android:id="@+id/focused" android:drawable="@drawable/drawableF"
        android:state_focused="true"/>
    <item android:id="@id/default"
        android:drawable="@drawable/drawableD"/>

    <!-- specify a transition -->
    <!-- 可以指定两个状态间的过渡的图片 -->
    <transition android:fromId="@+id/default" android:toId="@+id/pressed">
        <animation-list>
            <item android:duration="15" android:drawable="@drawable/dt1"/>
            <item android:duration="15" android:drawable="@drawable/dt2"/>
            ...
        </animation-list>
    </transition>
    ...
</animated-selector>

猜你喜欢

转载自blog.csdn.net/dahaohan/article/details/52087603