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());...省略无数代码
上面我贴上了关键的代码ヾ(=・ω・=)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>
- 跳转页面的时候我们看到页面是从上与从顶部分别进入的。这是因为定义
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>
- 看一下跳转的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(安卓)JNI之OpenSSL
- Android(安卓)N代码分析:requestLayout
- Android的Toolbar(含溢出菜单设置[弹出菜单的使用])的使用PopMen
- android蓝牙耳机录音程序主要代码
- SystemProperties與Settings.System
- 2018最新 Android(安卓)面试题总结(二)
- 如何开始分析一份开源软件代码
- Notification的功能与用法
- ndk初体验