Android共享动画兼容实现
2017-07-24 20:53
218 查看
生命不息,奋斗不止
这是
以上是在代码中动态设置,在
唯一要注意的就是名称必须相同
说下参数,第一个
这是
再回顾一下上面的代码,也就10行代码以内。所以对于只支持高版本的系统的朋友来说,真是爽歪歪。无图无真相,客官请看图。
界面背景是透明渐变的方式过度到另一个界面的。
控件是从第一个界面原地放大平移到第二个界面的控件位置上。
从上面的要点来看,对控件的动画实现是重中之重。具体的实现过程是:
请注意,这里我是对图片控件进行共享动画,如果是简单的TextView之类的控件就只需获取控件的宽高,相信客官们看了下面的实现方案也能迅速应对其它控件的类型。
有的客官可能会有所疑问,
说了这么多,客官们可能有点不耐烦了,开始
这里是ImageVIew中的ImageMatrix而不是View中的Matrix,具体的Matrix信息可以自行google
通过
既然说到
其中
后续进行缩放平移动画需要确定中心位置,由于要达到对图片进行缩放平移的效果,所以要得到图片的确切中心位置,默认为控件的中心
经过上面的解释说明,客官们对平移量的计算应该不难理解。核心是对中心位置进行偏移量计算。
对于进入动画,在之前的原理分析中已经指出,要先将第二个界面的控件缩放到第一个界面的位置上。所以我们直接先对控件进行缩放平移,使用
退出动画就相对简单一点,只需将第二个界面的控件缩放平移到第一个界面控件的位置上即可。
以上只是一个简单的透明动画的调用,不过直接这样调用你会发现效果不对,因为你还需要将Activity的theme设置为透明效果。只需将android:windowBackgroun设置为透明即可
demo地址
后续还会继续持续更新,如果客官们对此还有兴趣的话可以关注我的博客或者Github,谢谢支持。
前言
看了一下之前的文章记录,最近的文章是在3月12日写的,今天的7月16日。不知不觉已经4个月没有坐在电脑前认真的思考与静下心来做些总结。趁着刚刚王者荣耀超神的兴奋热度,接下来说说我对Android共享动画方面的一些心得。
实现方案
这里我姑且都认为大家都对共享动画的效果有所了解,简单的说就是从一个界面平移缩放过度到另一个界面。在实现方面上针对不同Android系统版本,有不同的做法。对于
Android 5.0(LOLLIPOP API 21)以上的系统,实现起来相对来说方便了许多,只需做一些契约与调用系统的
API即可。但是市场上对于
Android 5.0以下的机型还是存在的,我们并不能忽略它们,所以为了更好的兼容上下版本的机型,同时以为了让用户体验一致,我们必须自己动手实现共享动画的需求。
Android 5.0 及其以上的实现
为了满足部分只考虑Android 5.0以上实现的朋友,我这里也对系统的调用方法进行简单的示例说明。我这边总结了一下,主要分为三步。
建立契约
要想在第一个界面点击控件共享跳转到另一个界面的对于控件上,需要将这两个共享的控件进行绑定,即要让系统能够找到对应生效的控件。而为了达到这种效果, 系统给我们提供了一个方法public final void setTransitionName(String transitionName)
这是
View中的方法,就一个参数,该参数就是一个字符串类型的契约名称。即在两个界面上对需要进行共享的两个控件进行相同名称的设定。
public static final String TRANSITION_NAME_SHARE = "share"; imageView.setTransitionName(TRANSITION_NAME_SHARE);
以上是在代码中动态设置,在
xml文件中也能设置
android:transitionName="share"
唯一要注意的就是名称必须相同
调用ActivityOptionsCompat
上面建立的契约,就可以直接进入主题–开启共享动画。在进行界面的跳转,给平常的用发一样,创建Intent,调用
startActivity方法。只不过在调用
startActivity时要在传个
Bundle参数。该参数需要通过
ActivityOptionsCompat获取。
ActivityOptionsCompat compat = ActivityOptionsCompat.makeSceneTransitionAnimation(MainActivity.this, imageView, TRANSITION_NAME_SHARE);
说下参数,第一个
Activity,第二个需要共享的
View,第三个就是契约名称。最后开启跳转时传入。
startActivity(intent, compat.toBundle());
finishAfterTransition
使用上面的代码就能看到跳转的开启共享动画了,当然前提是在Andorid 5.0及其以上的手机上。上面只完成了开启,对于退出,实现也很简单,只需在退出的时候调用如下代码即可
finishAfterTransition();
这是
Activity的方法。所以可以直接在退出界面中调用。建议可以重写
onBackPressed方法,在其中进行调用。
再回顾一下上面的代码,也就10行代码以内。所以对于只支持高版本的系统的朋友来说,真是爽歪歪。无图无真相,客官请看图。
兼容全版本实现
我相信一直读到这里的客官心理都是很愉悦与轻松的,下面我需要提醒客官们,应该提起几分注意了来看下面的精彩内容。原理
基于上面的实现,我们再来看下上面的效果图,所谓一图胜千言,我们一起来结合效果图来分析实现原理。首先,我们通过效果图能够看到两个明显的效果:界面背景是透明渐变的方式过度到另一个界面的。
控件是从第一个界面原地放大平移到第二个界面的控件位置上。
从上面的要点来看,对控件的动画实现是重中之重。具体的实现过程是:
将第二个界面透明启动,同时将第二个界面的控件缩放平移到第一个界面的控件位置上,然后再进行放大平移到第二个界面原始的位置上。这样就实现了高版本的共享动画的效果。要想达到放大平移动画的准确进行,自然要得到相应的控件参数信息。所以我们在实现控件的放大动画,这里必须要得到两个界面的控件的宽高与控件内图片的宽高。再计算出需要缩放的比例。
请注意,这里我是对图片控件进行共享动画,如果是简单的TextView之类的控件就只需获取控件的宽高,相信客官们看了下面的实现方案也能迅速应对其它控件的类型。
有的客官可能会有所疑问,
为何要获取图片的宽高呢,图片的宽高不就等于控件的宽高吗?是的,对于绝大多数情况来说确实是如此,但有的时候控件的宽高并不一定等于图片的宽高,例如大图浏览模式下的图片。如果此时使用控件的宽高来计算缩放比例,自然得不到预想的效果,图片缩放的效果必然会不准确。其实本质是我们要
脱离控件,关注本质---图片效果。
说了这么多,客官们可能有点不耐烦了,开始
show me the code。
获取控件的相关参数
控件的宽高获取,这里就不多说了。我们主要来思考图片在控件中显示的真实宽高。看下面代码:public void convertOriginalInfo(ImageView oriView) { if (oriView == null || oriView.getDrawable() == null) { throw new NullPointerException("original ImageView or ImageView drawable must not null"); } //get original ImageView info oriView.getImageMatrix().getValues(mOriginalValues); Rect oriRect = oriView.getDrawable().getBounds(); mOriginalWidth = (int) (oriRect.width() * mOriginalValues[Matrix.MSCALE_X]); mOriginalHeight = (int) (oriRect.height() * mOriginalValues[Matrix.MSCALE_Y]); mOriginalViewWidth = oriView.getWidth(); mOriginalViewHeight = oriView.getHeight(); oriView.getLocationOnScreen(mOriginalLocation); }
Matrix
这里有一个知识点,每一张图片都有对应的一个Matrix,它代表的是一个
3*3的矩阵,其中包含了图片的相关信息,例如缩放,平移。
这里是ImageVIew中的ImageMatrix而不是View中的Matrix,具体的Matrix信息可以自行google
通过
Matrix的
getValues方法将
3*3的矩形值转化成一个大小为9的
float型数组
mOriginalValues。这样我们使用
Matrix.MSCALE_X与
Matrix.MSCALE_Y分别获取图片的
x与
y方向的缩放比例。再通过
ImageView的
getDrawable.getBounds方法获取图片原始相关信息。最后乘以比例系数,获取到我们所要的结果。
//calculator scale mScaleX = (float) mOriginalWidth / mTargetWidth; mScaleY = (float) mOriginalHeight / mTargetHeight;
既然说到
Matrix,就再简单说下它的两个值,
Matrix.MTRANS_X与
Matrix.MTRANS_Y分别代表图片平移的大小。类似与微信朋友圈中的大图浏览的下滑平移缩放退出效果,可以通过这两个值来获取图片在缩放过程中的平移量。
getLocationOnScreen
该方法能够直接获取到控件左上角在屏幕上的坐标位置。最终返回一个大小为2的数组。有个该方法我们就能方便的获取控件的中心坐标。//calculator pivot position mPivotX = mTargetLocation[0] + mTargetValues[Matrix.MTRANS_X] + mTargetWidth / 2; mPivotY = mTargetLocation[1] + mTargetValues[Matrix.MTRANS_Y] + mTargetHeight / 2;
其中
mTargetLocation[0]代表控件的在屏幕上的
x坐标位置,
mTargetLocation[1]代表控件在屏幕上的
y坐标位置。
后续进行缩放平移动画需要确定中心位置,由于要达到对图片进行缩放平移的效果,所以要得到图片的确切中心位置,默认为控件的中心
平移偏移量
mCenterOffsetX = (int) (mOriginalLocation[0] + mOriginalValues[Matrix.MTRANS_X] + mOriginalViewWidth / 2 - mTargetLocation[0] - mTargetValues[Matrix.MTRANS_X] - mTargetViewWidth / 2); mCenterOffsetY = (int) (mOriginalLocation[1] + mOriginalValues[Matrix.MTRANS_Y] + mOriginalViewHeight / 2 - CommonUtils.getStatusBarHeight(context) - mTargetLocation[1] - mTargetValues[Matrix.MTRANS_Y] - mTargetViewHeight / 2);
经过上面的解释说明,客官们对平移量的计算应该不难理解。核心是对中心位置进行偏移量计算。
进入动画
首先要确认控件动画的调用时机,必须要在控件绘制的时候进行调用,只有这样才能最早的获取控件的相关信息,为动画进行准备。我们可以采用注册addOnPreDrawListener进行监听控件的绘制。
public FKJShareElement convert(final ImageView tarView) { if (mInfo == null) { throw new NullPointerException("ShareElementInfo must not null"); } tarView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { @Override public boolean onPreDraw() { tarView.getViewTreeObserver().removeOnPreDrawListener(this); mInfo.convertTargetInfo(tarView, mContext); //init if (mEnter) { tarView.setPivotX(mInfo.getPivotX()); tarView.setPivotY(mInfo.getPivotY()); tarView.setTranslationX(mInfo.getCenterOffsetX()); tarView.setTranslationY(mInfo.getCenterOffsetY()); tarView.setScaleX(mInfo.getScaleX()); tarView.setScaleY(mInfo.getScaleY()); mAnimator = tarView.animate(); start(); startBackgroundAlphaAnimation(mBgView, new ColorDrawable(ContextCompat.getColor(mContext, R.color.fkj_white))); } return true; } }); return this; }
对于进入动画,在之前的原理分析中已经指出,要先将第二个界面的控件缩放到第一个界面的位置上。所以我们直接先对控件进行缩放平移,使用
View的
setTranslationX等方法。方法中的
mInfo保存了上面获取的图片相关信息。真正的动画执行是在
start中进行调用。目的是执行控件的还原动画。
private void start() { mAnimator.setDuration(mDuration) .scaleX(1.0f) .scaleY(1.0f) .translationX(0) .translationY(0); if (mListener != null) { mAnimator.setListener(mListener); } if (mInterpolator != null) { mAnimator.setInterpolator(mInterpolator); } mAnimator.start(); }
退出动画
public void startExitAnimator() { mEnter = false; mAnimator.setDuration(mDuration) .scaleX(mInfo.getScaleX()) .scaleY(mInfo.getScaleY()) .translationX(mInfo.getCenterOffsetX()) .translationY(mInfo.getCenterOffsetY()); if (mListener != null) { mAnimator.setListener(mListener); } if (mInterpolator != null) { mAnimator.setInterpolator(mInterpolator); } mAnimator.start(); startBackgroundAlphaAnimation(mBgView, new ColorDrawable(ContextCompat.getColor(mContext, R.color.fkj_white)), 255, 0); }
退出动画就相对简单一点,只需将第二个界面的控件缩放平移到第一个界面控件的位置上即可。
界面过度动画
在进入与退出动画中都调用了startBackgroundAlphaAnimation方法,该方法的作用就是对界面进行透明渐变。原理也简单,我们只需对第二个界面的背景
View进行背景渐变,具体实现如下:
private void startBackgroundAlphaAnimation(final View bgView, final ColorDrawable colorDrawable, int... value) { if (bgView == null) return; if (value == null || value.length == 0) { value = new int[]{0, 255}; } ObjectAnimator animator = ObjectAnimator.ofInt(colorDrawable, "alpha", value); animator.setDuration(mDuration); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { bgView.setBackground(colorDrawable); } }); animator.start(); }
以上只是一个简单的透明动画的调用,不过直接这样调用你会发现效果不对,因为你还需要将Activity的theme设置为透明效果。只需将android:windowBackgroun设置为透明即可
收割
不知道坚持看到这里的客官有多少,先在这里谢谢客官们的支持。最后将两种实现方式结合一起灵活的调用,在Android 5.0以上调用系统方法,
Android 5.0以下调用封装的方法。大概步骤如下:
引入依赖
dependencies { compile 'com.idisfkj.share:sharelibrary:1.0.0' }
执行界面
imageView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(MainActivity.this, ShareElementActivity.class); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { imageView.setTransitionName(TRANSITION_NAME_SHARE); ActivityOptionsCompat compat = ActivityOptionsCompat.makeSceneTransitionAnimation(MainActivity.this, imageView, TRANSITION_NAME_SHARE); startActivity(intent, compat.toBundle()); } else { ShareElementInfo info = new ShareElementInfo(); info.convertOriginalInfo(imageView); intent.putExtra(EXTRA_SHARE_ELEMENT_INFO, info); startActivity(intent); overridePendingTransition(0, 0); } } });
响应界面
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { mImageView.setTransitionName(MainActivity.TRANSITION_NAME_SHARE); } else { ShareElementInfo info = getIntent().getExtras().getParcelable(MainActivity.EXTRA_SHARE_ELEMENT_INFO); mShareElement = new FKJShareElement(info, this, mImageView.getRootView()); mShareElement.convert(mImageView) .setDuration(ANIMATOR_DURATION) .setInterpolator(new LinearInterpolator()) .startEnterAnimator(); }
@Override public void onBackPressed() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { finishAfterTransition(); } else { mShareElement.convert(mImageView) .setDuration(ANIMATOR_DURATION) .setInterpolator(new LinearInterpolator()) .setListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { finish(); overridePendingTransition(0, 0); } }) .startExitAnimator(); } }
镇文之宝
demo地址
后续还会继续持续更新,如果客官们对此还有兴趣的话可以关注我的博客或者Github,谢谢支持。
相关文章推荐
- Android 实现个性的ViewPager切换动画 实战PageTransformer(兼容Android3.0以下)
- 【Android】实现个性的ViewPager切换动画 实战PageTransformer(兼容Android3.0以下)
- Android 实现个性的ViewPager切换动画 实战PageTransformer(兼容Android3.0以下)
- Android 实现个性的ViewPager切换动画 实战PageTransformer(兼容Android3.0以下)
- Android 实现个性的ViewPager切换动画 实战PageTransformer(兼容Android3.0以下
- Android 实现个性的ViewPager切换动画 实战PageTransformer(兼容Android3.0以下)
- Android 实现个性的ViewPager切换动画 实战PageTransformer(兼容Android3.0以下)
- Android 实现个性的ViewPager切换动画 实战PageTransformer(兼容Android3.0以下)
- Android 实现个性的ViewPager切换动画 实战PageTransformer(兼容Android3.0以下)
- Android中Activity切换时共享视图元素的切换动画(4.x兼容方案)
- Android 实现个性的ViewPager切换动画 实战PageTransformer(兼容Android3.0以下)
- Android 实现个性的ViewPager切换动画 实战PageTransformer(兼容Android3.0以下)
- Android 实现个性的ViewPager切换动画 实战PageTransformer(兼容Android3.0以下) Android 实现个性的ViewPager切换动画 实战PageTrans
- Android 实现个性的ViewPager切换动画 实战PageTransformer(兼容Android3.0以下)
- Android 实现个性的ViewPager切换动画 实战PageTransformer(兼容Android3.0以下)
- Android中Activity切换时共享视图元素的切换动画(4.x兼容方案)
- Android 实现个性的ViewPager切换动画 实战PageTransformer(兼容Android3.0以下)
- Android 实现个性的ViewPager切换动画 实战PageTransformer(兼容Android3.0以下)
- Android 实现个性的ViewPager切换动画 实战PageTransformer(兼容Android3.0以下)
- Android中Activity切换时共享视图元素的切换动画(4.x兼容方案)