Android Transition——提高一点点篇

在Android5.0版本之前,我们的Activity进入退出动画都比较生硬,通常都是调用overridePendingTransition(int enterAnim, int exitAnim)来展示Activty的进出动画,当然了也可以设置overridePendingTransition(0, 0)来取消Activty的切换动画。到了5.1这个版本之后,Activity的进出动画就已经很生动了。根本原因是Transition这个类的出现,它为我们提供了两种Activity transition :内容transition与共享元素的transition。

现在我们需要了解的API:

1 .FEATURE_ACTIVITY_TRANSITIONS(added in API level 21)

ActivityOptions 调用makeSceneTransitionAnimation(android.app.Activity, android.util.Pair[]) 方法时,允许活动的窗口接收或发送Transition。[○・`Д´・ ○]

代码实现:

 @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getWindow().requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS);
        setContentView(R.layout.activity_sample);
        }

xml实现需要写在style中(默认就是true)

<item name="android:windowActivityTransitions">trueitem>

2 .FEATURE_CONTENT_TRANSITIONS(added in API level 21)

使用TransitionManager来管理过渡动画时,设置窗口内容更改的标志。

 @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getWindow().requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS);
        setContentView(R.layout.activity_sample);
        }

xml实现需要写在style中(默认就是true)

<item name="android:windowContentTransitions">trueitem>

3 . setAllowEnterTransitionOverlap
当设置了setEnterTransition(android.transition.Transition)之后,Transition动画一般会比跳转Activity的速度慢一些,所以会有执行了一半Transition动画之后,然后就跳转到了另一个Activity。设置true,Transition开始的时候调用Activity,设置false,那就等待Transition完成,然后再调用Activity,默认是true。

代码中:

 getWindow().setAllowEnterTransitionOverlap(true);

xml写在stytle中:

<item name="android:windowAllowEnterTransitionOverlap">trueitem>

4 . setSharedElementsUseOverlay
设置转场动画期间使用Overlay。true表示共享元素应该用Overlay转换,false表示在正常的View层次结构中转换。默认值是true。(个人建议尽量不要设置false)
代码中:

  getWindow().setSharedElementsUseOverlay(true);

xml写在stytle中:

<item name="android:windowSharedElementsUseOverlay">trueitem>

ViewOverlay是Android 4.3新增的一个类,它位于view最上面,是一个透明层,因为它是View绘制之后才绘制的,所以我们可以在这个层之上添加内容而不会影响到整个布局结构。

5 .postponeEnterTransition(added in API level 21)
在设置了makeSceneTransitionAnimation(Activity, android.util.Pair[])之后,推迟转场动画。这个方法会延迟启动进入Activity直到加载完所有的数据。在此之前,活动不会进入新的窗口,让窗口保持透明。所以必须调startPostponedEnterTransition()以允许Activity开始转换。如果Activity没有使用 makeSceneTransitionAnimation(Activity, android.util.Pair[]),那么设置这个方法设置了也无效。

6 . startPostponedEnterTransition (added in API level 21)
postponeEnterTransition()被调用后开始推迟的转换。如果调用postponeEnterTransition(),则必须调用startPostponedEnterTransition()以使您的活动开始绘制。


简单的看了几个API,后面我们会用到的。那我们来看两个简单的例子:

...省略无数代码
ActivityOptionsCompat options = ActivityOptionsCompat.makeScaleUpAnimation(ivImage, view.getWidth(),view.getHeight(), 0, 0);
mContext.startActivity(intent, options.toBundle());
...省略无数代码

Android Transition——提高一点点篇_第1张图片

上面我贴上了关键的代码ヾ(=・ω・=)o

ActivityOptionsCompat makeScaleUpAnimation (View source, 
                int startX, 
                int startY, 
                int startWidth, 
                int startHeight)
source:目标View
startX:根据目标View从X坐标开始动画
startY:根据目标View从Y坐标开始动画
startWidth:新的Activity从什么宽度开始动画
startHeight:新的Activity从什么高度开始动画

这个方法是根据目标View,给一个放大的效果然后进行转场动画的。


先看效果图:

Android Transition——提高一点点篇_第2张图片

首先呢,我们用到了共享元素,然后进行转场动画。所以先在XML中定义Transition

  • 建立transition文件夹,然后创建change_image_transform.xml
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
    <changeBounds
        android:duration="500"
        android:interpolator="@android:interpolator/linear_out_slow_in"/>
    <changeImageTransform
        android:duration="500"
        android:interpolator="@android:interpolator/linear_out_slow_in"/>
transitionSet>
  • 跳转页面的时候我们看到页面是从上与从顶部分别进入的。这是因为定义TransitionSlide,所以我们分别创建进入与 退出的Transitionsimple_activity_enter_transition.xmlsimple_activity_exit_transition.xml两个写法正好相反,所以只贴出进入的方式。
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">

    <slide
        android:duration="500"
        android:slideEdge="top">
        <targets>
            <target android:targetId="@id/relative_top"/>
        targets>
    slide>

    <slide android:duration="500" android:slideEdge="bottom" >
        <targets>
            <target android:targetId="@id/relative_bottom"/>
        targets>
    slide>

transitionSet>

标签里面可以指定目标ID,可以选择ID指定动画,也可以除去目标ID,这个ID不执行动画

  • Transition最后需要在style中适配,然后应用在Activity的主题中。
<style name="SecondActivityTheme" parent="AppTheme">
   <item name="android:windowEnterTransition">@transition/simple_activity_enter_transition
   "android:windowExitTransition">@transition/simple_activity_exit_transition
   "android:windowSharedElementEnterTransition">@transition/change_image_transform
  style>
  • 看一下跳转的Activity布局
<RelativeLayout 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.support.design.widget.AppBarLayout
        android:id="@+id/appbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:contentInsetEnd="16dp" />

    android.support.design.widget.AppBarLayout>


    <RelativeLayout
        android:id="@+id/relative_top"
        android:layout_width="match_parent"
        android:layout_height="200dp"
        android:layout_below="@id/appbar"
        android:transitionGroup="true">

        <ImageView
            android:id="@+id/full_image"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

        <ImageView
            android:id="@+id/full_pic"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:transitionName="profileImage" />

        <TextView
            android:id="@+id/content_tv"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignBottom="@id/full_pic"
            android:layout_centerInParent="true"
            android:layout_marginTop="10dp"
            android:textColor="@android:color/holo_orange_dark"
            android:textSize="20sp"
            android:transitionName="profileText" />
    RelativeLayout>

    <RelativeLayout
        android:id="@+id/relative_bottom"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_below="@id/relative_top"
        android:background="@android:color/darker_gray"
        android:transitionGroup="true">

        <TextView
            android:id="@+id/tv_des"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:padding="10dp"
            android:textColor="@android:color/holo_orange_light"
            android:textSize="21sp" />
    RelativeLayout>

RelativeLayout>
...省略布局

    <ImageView
        android:id="@+id/list_picture"
        android:layout_width="@dimen/picture_width"
        android:layout_height="@dimen/picture_height"
        android:transitionName="profileImage" />

    <TextView
        android:id="@+id/tv_author"
        android:transitionName="profileText"
        android:textSize="21sp" />
...省略布局

重点关注android:transitionName="..."android:transitionGroup="true"这两个属性值。
其中第一个:在布局文件中对于要共享View添加的Flag。需要共享View的ID可以不同,但是设定的这个name需要一致。
第二个:应该将这个ViewGroup视为单个实体,也就是当成一个Transition来处理。

  • 代码里面比较简单,点击Item的时候,跳转到另一个界面。
    Intent intent = new Intent(mContext, SecondActivity.class);
        intent.putExtra(SecondActivity.EXTRA_AUTHOR, item.getAuthor());
        intent.putExtra(SecondActivity.EXTRA_CONTACT, item.getQuote());
        Pair imagePair = Pair.create((View)ivImage, "profileImage");
        Pair textPair = Pair.create((View)tvAuthor, "profileText");
        ActivityOptionsCompat options = ActivityOptionsCompat.
                  makeSceneTransitionAnimation((Activity) mContext, imagePair, textPair);
        mContext.startActivity(intent, options.toBundle());

ActivityOptionsCompat makeSceneTransitionAnimation (Activity activity, Pair…

...省略代码
 Picasso.with(this)
                .load(R.drawable.k)
                .into(imageView, new Callback() {
                    @Override
                    public void onSuccess() {
                        scheduleStartPostponedTransition(imageView);
                    }

                    @Override
                    public void onError() {

                    }
                });

        postponeEnterTransition();

        fullImage.setAlpha(0f);
        //添加共享元素监听
        getWindow().getSharedElementEnterTransition().addListener(new Transition.TransitionListener() {

            //完成元素共享动画之后,显示一张模糊图片
            @Override
            public void onTransitionEnd(Transition transition) {
                Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.l);
                showBlur(bitmap,fullImage);
            }

        });
    }

   private void showBlur(Bitmap bg, View view) {
        //Radius out of range (0 < r <= 25).
        float radius = 10;
        Bitmap overlay = Bitmap.createBitmap((int)(view.getMeasuredWidth()), (int)(view.getMeasuredHeight()), Bitmap.Config.RGB_565);
        Canvas canvas = new Canvas(overlay);
        canvas.translate(-view.getLeft(), -view.getTop());
        canvas.drawBitmap(bg, 0, 0, null);
        //初始化RenderScript
        RenderScript rs = RenderScript.create(SecondActivity.this);
        //创建一个Allocation,为数据提供存储功能,Allocation允许将数组从Java代码传递到RenderScript代码
        Allocation overlayAlloc = Allocation.createFromBitmap(rs, overlay);
        //使用kernels中的RenderScript,在其子类中 ScriptIntrinsic.实现了部分效果(高斯模糊)
        ScriptIntrinsicBlur blur = ScriptIntrinsicBlur.create(rs, overlayAlloc.getElement());
        blur.setInput(overlayAlloc);
        blur.setRadius(radius);
        blur.forEach(overlayAlloc);
        overlayAlloc.copyTo(overlay);
        view.setBackground(new BitmapDrawable(getResources(), overlay));
        view.animate().setDuration(1000).alpha(1f);
        rs.destroy();

    }

    private void scheduleStartPostponedTransition(final View sharedElement) {
        sharedElement.getViewTreeObserver().addOnPreDrawListener(
                new ViewTreeObserver.OnPreDrawListener() {
                    @Override
                    public boolean onPreDraw() {
                        sharedElement.getViewTreeObserver().removeOnPreDrawListener(this);
                        startPostponedEnterTransition();
                        return true;
                    }
                });
    }

    @Override
    public void onBackPressed() {
//        super.onBackPressed();
        finishAfterTransition();
    }
 ...省略代码

关键的代码就是这些,我顺便加了一个高斯模糊的效果(RenderScript ),然后退出的界面的时候调用finishAfterTransition()。在Picasso中写了一个监听,为的是如果加载的是网络图片,数据加载需要一定的时间,配合startPostponedEnterTransition()完全加载出图片之后再显示共享动画效果。

OK,结合着上篇的内容,共享动画效果我们就懂了一些了。
这种玩意如果想向下兼容到4.4之前,很不容易,尤其是在加上statusBar这种东西,向后兼容那就更费劲了。不过写代码的时候还是一定要加上if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)

天气转暖了,终于不用穿羽绒衣上班了。真爽(◕ᴗ◕✿)(◕ᴗ◕✿)

你可能感兴趣的:(Android)