Android Transition——提高一点点篇

来源:互联网 发布:什么叫seo营销 编辑:程序博客网 时间:2024/05/19 05:33

在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">true</item>

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">true</item>

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">true</item>

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

  getWindow().setSharedElementsUseOverlay(true);

xml写在stytle中:

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

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());...省略无数代码

这里写图片描述

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

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

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


先看效果图:

这里写图片描述

首先呢,我们用到了共享元素,然后进行转场动画。所以先在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>

标签<targets>里面可以指定目标ID,可以选择ID指定动画<target android:targetId="@id/..." />,也可以除去目标ID,这个ID不执行动画<target android:excludeId="@id/..."/>

  • Transition最后需要在style中适配,然后应用在Activity的主题中。
<style name="SecondActivityTheme" parent="AppTheme">   <item name="android:windowEnterTransition">@transition/simple_activity_enter_transition</item>   <item name="android:windowExitTransition">@transition/simple_activity_exit_transition</item>   <item name="android:windowSharedElementEnterTransition">@transition/change_image_transform</item>  </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<View, String> imagePair = Pair.create((View)ivImage, "profileImage");        Pair<View, String> 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)

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