整篇文章代码和逻辑比较简单,实现效果相对理想,大神勿喷.老规矩先上效果图:
我们要实现的效果是:页面跳转的时候,A页面类似开门的效果,打开的过程中逐渐显示B页面.退回的时候预览进入的A页面关闭B页面.对于动画我的想法是两张图片拼凑起来实现一张图片的视觉效果
需求明确了现在差构思:要实现这种效果也不难,首先我们先思考一下怎么才能在动画过渡的时候可以预览到B页面的视图...相信很多人都猜到了我的小心思:"所有动画过程在B页面完成,不就可以预览到B页面了吗".先为你的机智点个赞,对!就是这个道理.在这之前我们需要把A页面跳到B页面的默认动画取消
startActivity(intent); overridePendingTransition(0,0);
可以看到第二句是实现的关键,他的作用是覆盖Activity原有的跳转动画,使用自定义动画.参数详解:1.Activity进入动画;2.Activity退场动画
慢着,在B页面开始动画之前我们还差一个东西,那就是A页面的视图,我这里选择的办法是:获取A页面根视图的截图,通过Bitmap传到B页面,好的我们来一步步的实现:
/** * 获取指定View的截图 * @param view */ public static Bitmap getImageOfView(View view) { int width1 = view.getMeasuredWidth(); int height1 = view.getMeasuredHeight(); Bitmap bmp1 = Bitmap.createBitmap(width1, height1, Bitmap.Config.ARGB_8888); view.draw(new Canvas(bmp1)); Canvas canvas = new Canvas(bmp1); Paint paint = new Paint(); paint.setColor(Color.TRANSPARENT); canvas.drawBitmap(bmp1, 0, 0, null); return bmp1; }
这里我们用到的是画笔来绘制传入视图的方法,注意:如果我们的根视图没有背景色,那么我们取到的图片是背景透明的,就达不到我们想要的效果了.如果想解决这个麻烦也可以设置根视图的背景色,或者修改上述代码中的paint.setcolor()方法中的默认颜色值.
接下来我们需要做的就是把获取到的Bitmap传入B页面,我们知道传统的使用intent bundle的方法来传递数据是有大小限制的,网上查了一下貌似是1M的上限,说法不一我也不做纠正了.但是别着急这里我用的是另一种方法:利用EventBus传递Bitmap给B页面,这里Eventbus的注册需要用到粘性注册,不然你在A页面发送事件的时候B页面还没注册监听,是拿不到Bitmap的.B页面注册事件监听代码:
setContentView(R.layout.activity_b); // 注意这里注册在初始化Bitmap之前调用 EventBus.getDefault().register(this); // 初始化视图 initial();
/** * 粘性事件(可以收到注册之前发送的消息) * @param busEvent */ @Subscribe(threadMode = ThreadMode.MAIN,sticky = true) public void onBitmapRecieveEvent(BusEvent busEvent){ if (busEvent.getCode() == 2){ bitmap = (Bitmap) busEvent.getData(); } }
A页面跳转之前我们要设置bitmap:
// 获取imageView加载的bitmap Bitmap bmp = CommonFunction.getImageOfView(openAContainer); EventBus.getDefault().postSticky(new BusEvent(2,bmp));// 发送粘性事件
openAContainer是我们A页面的布局;
好的A页面的事情做完了,我们进入B来实现开门动画.图片拿到了我们的开门动画离成功又进了一步,但是!敲黑板,这里是重点我这里使用的是两张图片拼凑起来实现一张图片的视觉效果.那么我现在要做的就是把传进来的Bitmap一分为二:
/** * 实现bitmap一分为二的效果 * @param bitmap * @return */ public static Bitmap[] getCropBitmaps(Bitmap bitmap){ if (bitmap == null) { return null; } int with = bitmap.getWidth(); // 得到图片的宽,高 int height = bitmap.getHeight(); int nw, nh, retX; nw = with / 2; nh = height; retX = with / 2; // 两张图片的容器 Bitmap[] bitmaps = new Bitmap[2]; // 第一张图片从X坐标0的地方开始截取一半的宽度 Bitmap bmp = Bitmap.createBitmap(bitmap, 0, 0, nw, nh, null, false); // 第二张图片从X坐标为width一半的的地方开始截取一半的宽度 Bitmap bmp1 = Bitmap.createBitmap(bitmap, retX, 0, nw, nh, null, false); bitmaps[0] = bmp;// 左边图片 bitmaps[1] = bmp1;// 右边图片 if (bitmap != null && !bitmap.equals(bmp) && !bitmap.isRecycled()) { bitmap.recycle(); } return bitmaps; }
工具有了我们来看看布局是怎么实现的,其实很简单,就是使用RelativeLayout或者FrameLayout都可以实现,我这里用的RelativeLayout:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".OpenBActivity" android:background="@color/color_000000"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:text="这是开门动画的第二个Activity" android:textSize="22sp" android:textColor="@color/color_FFD166"/> <LinearLayout android:id="@+id/coverImageContainer" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal"> <ImageView android:id="@+id/coverImage1" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:scaleType="fitXY"/> <ImageView android:id="@+id/coverImage2" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:scaleType="fitXY"/> </LinearLayout> </RelativeLayout>
然后在B页面中我们来实现两张图片分离的平移效果:
bitmaps = CommonFunction.getCropBitmaps(bitmap);// 这里的bitmap为A页面通过EventBus传进来的A页面视图 coverImage1.setImageBitmap(bitmaps[0]); coverImage2.setImageBitmap(bitmaps[1]); coverImage1.animate() .setDuration(1000) .translationX(-bitmaps[0].getWidth()) .start(); coverImage2.animate() .setDuration(1000) .translationX(bitmaps[1].getWidth()) .withEndAction(new Runnable() { @Override public void run() { coverImageContainer.setVisibility(View.GONE); } }) .start();
这里我们用的是补间动画,这种实现的方式优点就是代码简介,一行代码搞定一个动画.哈哈..好的动画结束之后别忘了把两张图片设置隐藏.至此为止我们的开门动画就讲解完了.而关门动画就相对Easy了,在Activity中拦截返回键:
@Override public void onBackPressed() { coverImageContainer.setVisibility(View.VISIBLE); coverImage1.animate() .setDuration(1000) .translationX(0) .start(); coverImage2.animate() .setDuration(1000) .translationX(0) .withEndAction(new Runnable() { @Override public void run() { finish(); overridePendingTransition(0,0); } }) .start(); }
对了最后的最后别忘了移除MyApplication中的Bitmap,这可是个大家伙,别让他有机会给我们的程序抛出OOM的异常~
OK,实现比较简单,我也不附源码了,主要的代码都在上述文章里,希望给寻找开门动画的你一些灵感,我们的口号是:不要只做一个代码的搬运工...