本文共 18372 字,大约阅读时间需要 61 分钟。
1.业务需求:Activity正反两面,沿中心X,Y轴旋转180度
2.业务分析:两个界面放在同一个布局中
3.具体动画逻辑做法:看具体代码
4.关于Activity实现切换的方式
5.使用overridePendingTransition方法实现Activity跳转动画
6.使用style的方式定义Activity的切换动画
6.5 可能出现的bug
7.使用ActivityOptions切换动画实现Activity跳转动画
8.使用ActivityOptions之后内置的动画效果通过style的方式
9.使用ActivityOptions动画共享组件的方式实现跳转Activity动画
10.其他说明
1.0 具体业务需求
1.1 用3D效果做翻转动画
1.2 用2D效果做翻转动画【实际是缩小-放大,看上去是翻转】
1.SplashAdFirstActivity,3d效果,沿中心x,y轴旋转,可以设置缩放效果,只需要设置z轴位移就可以2.SplashAdSecondActivity,3d效果,借鉴与网络,不是旋转的动画,但是效果很炫3.SplashAdThirdActivity,2d效果。看上去像是旋转,实则是缩放效果。几乎看上去效果和1类似由于产品需求,后来采用第三种方法
2.1 布局设计思路分析
2.2 代码展示
3.1 定义3D旋转动画
public class Rotate3D extends Animation { // 开始角度 private final float mFromDegrees; // 结束角度 private final float mToDegrees; // X轴中心点 private final float mCenterX; // Y轴中心点 private final float mCenterY; // Z轴中心点 private final float mDepthZ; //是否需要扭曲 private final boolean mReverse; //摄像头 private Camera mCamera; public Rotate3D(float fromDegrees, float toDegrees, float centerX,float centerY, float depthZ, boolean reverse) { mFromDegrees = fromDegrees; mToDegrees = toDegrees; mCenterX = centerX; mCenterY = centerY; mDepthZ = depthZ; mReverse = reverse; } @Override public void initialize(int width, int height, int parentWidth, int parentHeight) { super.initialize(width, height, parentWidth, parentHeight); mCamera = new Camera(); } @Override protected void applyTransformation(float interpolatedTime, Transformation t) { final float fromDegrees = mFromDegrees; // 生成中间角度 float degrees = fromDegrees + ((mToDegrees - fromDegrees) * interpolatedTime); final float centerX = mCenterX; final float centerY = mCenterY; final Camera camera = mCamera; //取得当前矩阵 final Matrix matrix = t.getMatrix(); camera.save(); if (mReverse) { camera.translate(0.0f, 0.0f, mDepthZ * interpolatedTime); } else { camera.translate(0.0f, 0.0f, mDepthZ * (1.0f - interpolatedTime)); } //翻转 camera.rotateY(degrees); // 取得变换后的矩阵 camera.getMatrix(matrix); camera.restore(); matrix.preTranslate(-centerX, -centerY); matrix.postTranslate(centerX, centerY); }}
3.2 在activity处理翻转逻辑
public class SplashAdFirstActivity extends BaseActivity { @Bind(R.id.iv_ad) ImageView ivAd; @Bind(R.id.tv_time) TextView tvTime; //页面翻转容器FrameLayout @Bind(R.id.fl) LinearLayout fl; //布局1界面RelativeLayout @Bind(R.id.fl_ad) FrameLayout flAd; //布局2界面RelativeLayout @Bind(R.id.fl_main) FrameLayout flMain; private TimeCount timeCount; private boolean isClick = false; //z轴翻转 private float z = 200.0f; @Override protected void onDestroy() { super.onDestroy(); if (timeCount != null) { timeCount.cancel(); timeCount = null; } } @Override public int getContentView() { return R.layout.activity_splash_ad; } @Override public void initView() { initTimer(); } private void initTimer() { timeCount = new TimeCount(5 * 1000, 1000, tvTime); timeCount.start(); } @Override public void initListener() { ivAd.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { isClick = true; applyRotation(1,0,90); //applyRotation(0,0,-90); } }); } @Override public void initData() { } private class TimeCount extends CountDownTimer { private TextView tv; TimeCount(long millisInFuture, long countDownInterval, TextView tv) { super(millisInFuture, countDownInterval); //参数依次为总时长,和计时的时间间隔 this.tv = tv; } //计时完毕时触发 @Override public void onFinish() { //如果是点击了翻转,那么就不执行这里面的代码 if(!isClick){ //第一阶段翻转 applyRotation(1,0,90); //applyRotation(0,0,-90); } } @Override public void onTick(long millisUntilFinished) { //计时过程显示 StringBuilder sb = new StringBuilder(); sb.append("倒计时"); sb.append(millisUntilFinished / 1000); sb.append("秒"); tv.setText(sb.toString()); } } /** * 执行翻转第一阶段翻转动画 * @param tag view索引 * @param start 起始角度 * @param end 结束角度 */ private void applyRotation(int tag, float start, float end) { // 得到中心点(以中心翻转) //X轴中心点 final float centerX = fl.getWidth() / 2.0f; //Y轴中心点 final float centerY = fl.getHeight() / 2.0f; //Z轴中心点 final float depthZ = z; // 根据参数创建一个新的三维动画,并且监听触发下一个动画 final Rotate3D rotation = new Rotate3D(start, end, centerX, centerY, depthZ, true); //设置动画持续时间 rotation.setDuration(2500); //设置动画变化速度 //rotation.setInterpolator(new AccelerateInterpolator()); //设置第一阶段动画监听器 rotation.setAnimationListener(new DisplayNextView(tag)); fl.startAnimation(rotation); } /** * 第一阶段动画监听器 */ private final class DisplayNextView implements Animation.AnimationListener { private final int tag; private DisplayNextView(int tag) { this.tag = tag; } @Override public void onAnimationStart(Animation animation) {} @Override public void onAnimationEnd(Animation animation) { //第一阶段动画结束时,也就是整个Activity垂直于手机屏幕, //执行第二阶段动画 fl.post(new SwapViews(tag)); //调整两个界面各自的visibility adjustVisibility(); } @Override public void onAnimationRepeat(Animation animation) { } } /** * 执行翻转第二个阶段动画 */ private final class SwapViews implements Runnable { private final int tag; SwapViews(int position) { tag = position; } @Override public void run() { if (tag == 0) { //首页页面以90~0度翻转 showView(flAd, flMain, 90, 0); } else if (tag == 1) { //音乐页面以-90~0度翻转 showView(flMain, flAd, -90, 0); } } } /** * 显示第二个视图动画 * @param showView 要显示的视图 * @param hiddenView 要隐藏的视图 * @param startDegree 开始角度 * @param endDegree 目标角度 */ private void showView(FrameLayout showView, FrameLayout hiddenView, int startDegree, int endDegree) { //同样以中心点进行翻转 float centerX = showView.getWidth() / 2.0f; float centerY = showView.getHeight() / 2.0f; float centerZ = z; if (centerX == 0 || centerY == 0) { //调用该方法getMeasuredWidth(),必须先执行measure()方法,否则会出异常。 showView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED); //获取该view在父容器里面占的大小 centerX = showView.getMeasuredWidth() / 2.0f; centerY = showView.getMeasuredHeight() / 2.0f; } hiddenView.setVisibility(View.GONE); showView.setVisibility(View.VISIBLE); Rotate3D rotation = new Rotate3D(startDegree, endDegree, centerX, centerY, centerZ, true); //设置动画持续时间 rotation.setDuration(2500); //设置动画变化速度 //rotation.setInterpolator(new DecelerateInterpolator()); fl.startAnimation(rotation); } /** * 两个布局的visibility调节 */ private void adjustVisibility(){ if(flAd.getVisibility() == View.VISIBLE){ flAd.setVisibility(View.GONE); }else { flMain.setVisibility(View.VISIBLE); } if(flMain.getVisibility() == View.VISIBLE){ flMain.setVisibility(View.GONE); }else { flAd.setVisibility(View.VISIBLE); } }}
public class SplashAdThirdActivity extends BaseActivity { @Bind(R.id.fl_ad) FrameLayout flAd; @Bind(R.id.fl_main) FrameLayout flMain; @Bind(R.id.fl) LinearLayout fl; @Bind(R.id.iv_ad) ImageView ivAd; @Bind(R.id.tv_time) TextView tvTime; private TimeCount timeCount; private boolean isClick = false; private ScaleAnimation sato0 = new ScaleAnimation(1,0,1,1, Animation.RELATIVE_TO_PARENT,0.5f, Animation.RELATIVE_TO_PARENT,0.5f); private ScaleAnimation sato1 = new ScaleAnimation(0,1,1,1, Animation.RELATIVE_TO_PARENT,0.5f,Animation.RELATIVE_TO_PARENT,0.5f); @Override protected void onDestroy() { super.onDestroy(); if (timeCount != null) { timeCount.cancel(); timeCount = null; } } @Override public int getContentView() { return R.layout.activity_splash_ad; } @Override public void initView() { initTimer(); } private void initTimer() { timeCount = new TimeCount(5 * 1000, 1000, tvTime); timeCount.start(); } @Override public void initListener() { ivAd.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { isClick = true; flAd.startAnimation(sato0); } }); } @Override public void initData() { showView1(); sato0.setDuration(500); sato1.setDuration(500); sato0.setAnimationListener(new Animation.AnimationListener() { @Override public void onAnimationStart(Animation animation) { } @Override public void onAnimationEnd(Animation animation) { if (flAd.getVisibility() == View.VISIBLE){ flAd.setAnimation(null); showView2(); flMain.startAnimation(sato1); }else{ flMain.setAnimation(null); showView1(); flAd.startAnimation(sato1); } } @Override public void onAnimationRepeat(Animation animation) { } }); } private class TimeCount extends CountDownTimer { private TextView tv; TimeCount(long millisInFuture, long countDownInterval, TextView tv) { super(millisInFuture, countDownInterval); //参数依次为总时长,和计时的时间间隔 this.tv = tv; } //计时完毕时触发 @Override public void onFinish() { //如果是点击了翻转,那么就不执行这里面的代码 if(!isClick){ //第一阶段翻转 flAd.startAnimation(sato0); } } @Override public void onTick(long millisUntilFinished) { //计时过程显示 StringBuilder sb = new StringBuilder(); sb.append("倒计时"); sb.append(millisUntilFinished / 1000); sb.append("秒"); tv.setText(sb.toString()); } } private void showView1(){ flAd.setVisibility(View.VISIBLE); flMain.setVisibility(View.GONE); } private void showView2(){ flAd.setVisibility(View.GONE); flMain.setVisibility(View.VISIBLE); }}
4.1 实现切换的方式
4.2 activity退出动画与显示动画
5.1 如何实现,步骤演示
Intent intent = new Intent(this, MainActivity.class);startActivity(intent);overridePendingTransition(R.anim.screen_zoom_in, R.anim.screen_zoom_out);finish();
5.2 源码分析
看下面这一段注释可以知道:
/** * Call immediately after one of the flavors of {@link #startActivity(Intent)} * or {@link #finish} to specify an explicit transition animation to * perform next. * *As of {@link android.os.Build.VERSION_CODES#JELLY_BEAN} an alternative * to using this with starting activities is to supply the desired animation * information through a {@link ActivityOptions} bundle to * {@link #startActivity(Intent, Bundle)} or a related function. This allows * you to specify a custom animation even when starting an activity from * outside the context of the current top activity. * * @param enterAnim A resource ID of the animation resource to use for * the incoming activity. Use 0 for no animation. * @param exitAnim A resource ID of the animation resource to use for * the outgoing activity. Use 0 for no animation. */public void overridePendingTransition(int enterAnim, int exitAnim) { try { ActivityManagerNative.getDefault().overridePendingTransition( mToken, getPackageName(), enterAnim, exitAnim); } catch (RemoteException e) { }}
6.1 定义包含动画的 Activity 主题
6.2 定义切换动画 style
还是res/values/styles.xml 文件中
6.3 定义具体动画文件
6.4 应用到对应 Activity
6.5 可能出现的bug
6.5.1 添加动画后,出现从黑屏到新activity的过度
6.5.2 没有动画,或动画与设置不一致
7.1 这种动画方式介绍
7.2 代码实现方式
代码诠释
Intent intent = new Intent(MainActivity.this, ThreeActivity.class);startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(MainActivity.this).toBundle());
@Overrideprotected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 设置contentFeature,可使用切换动画 getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS); Transition explode = TransitionInflater.from(this).inflateTransition(Android.R.transition.explode); getWindow().setEnterTransition(explode); setContentView(R.layout.activity_main);}
8.1 定义动画文件
- @transition/activity_explode
- @transition/activity_explode
/*** 点击按钮,实现Activity的跳转操作* 通过Android5.0及以上style的方式实现activity的跳转动画*//*** 调用ActivityOptions.makeSceneTransitionAnimation实现过度动画*/Intent intent = new Intent(MainActivity.this, FourActivity.class);startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(MainActivity.this).toBundle());
9.1 定义共享组件
9.2 调用startActivity执行跳转动画
/*** 点击按钮,实现Activity的跳转操作* 通过Android5.0及以上共享组件的方式实现activity的跳转动画*/Intent intent = new Intent(MainActivity.this, FiveActivity.class);startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(MainActivity.this, button, "shareNames").toBundle());
9.3 关于这几种方式的动画总结
10.1 参考案例
10.2 更新日志
10.3 关于我的博客
转载地址:http://yicfl.baihongyu.com/