在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());
...省略无数代码
上面我贴上了关键的代码ヾ(=・ω・=)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,给一个放大的效果然后进行转场动画的。
先看效果图:
首先呢,我们用到了共享元素,然后进行转场动画。所以先在XML中定义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>
Transition
的Slide
,所以我们分别创建进入与 退出的Transition
。simple_activity_enter_transition.xml
与simple_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>
<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来处理。
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)
。
天气转暖了,终于不用穿羽绒衣上班了。真爽(◕ᴗ◕✿)(◕ᴗ◕✿)