咱们都知道 Android 自带了 Roate Scale Translate Alpha 多种框架动画,咱们能够经过她们实现丰富的动画效果,可是这些宽家动画却有一个致命的弱点,它们只是改变了 View 显示的大小,而没有改变 View 的响应区域。这时以 ObjectAnimator、ValueAnimator 为表明的属性动画也就应运而生了。java
属性动画字如其名,是经过改变 View 的属性值来改变控件的形态,说白了就是经过反射技术来获取控件的一些属性如宽度、高度等的 get 和 set 方法,从而实现所谓的动画效果。因此,这就须要咱们的 View (如自定义 View 中)具备 set 和 get 方法,若是没有则会致使程序的 Clash 。 具体步骤android
由此也能够看出:属性动画直接改变了控件的属性,因此动画结束后控件也就发生了永久性的变化。git
这里我打算经过使用 ObjectAnimator 实现四大动画框架:github
给你们讲解下 ObjectAnimator 使用编程
private void iniAnimation(){
// 透明度动画
ObjectAnimator.ofFloat(mAlphaImage, "alpha", 1, 0, 1)
.setDuration(4000)
.start();
// 缩放
final AnimatorSet animatorSet = new AnimatorSet();
mScaleImage.setPivotX(mScaleImage.getWidth()+250);
mScaleImage.setPivotY(mScaleImage.getHeight()+250);
animatorSet.playTogether(
ObjectAnimator.ofFloat(mScaleImage, "scaleX", 1, 0)
.setDuration(2000),
ObjectAnimator.ofFloat(mScaleImage, "scaleY", 1, 0)
.setDuration(2000)
);
animatorSet.start();
// 平移 translation
final AnimatorSet translationAnimatorSet = new AnimatorSet();
translationAnimatorSet.playTogether(
ObjectAnimator.ofFloat(mTranslationImage, "translationX", 20, 100)
.setDuration(2000),
ObjectAnimator.ofFloat(mTranslationImage, "translationY", 20,100)
.setDuration(2000)
);
translationAnimatorSet.start();
// 利用 ObjectAnimator 实现旋转动画
final AnimatorSet rotateAnimationSet = new AnimatorSet();
rotateAnimationSet.playTogether(
ObjectAnimator.ofFloat(mRotationImage, "rotation",0, 360)
.setDuration(2000)
);
rotateAnimationSet.start();
}
复制代码
以上代码就经过了 ObjectAnimator 实现了,四大效果,实现过程基本能够概括为app
最后的运行效果如开头动画所示 一样的,咱们能够在一个 playTogether 方法中添加多个动画,这样就能实现多动画组合的效果。这里就不在赘述了,你们能够本身试试看(我 GIF 图中,右下角的动画,就是旋转 + 透明度)框架
ValueAnimator 是 ObjectAnimator 的父类,他两之间的区别是,ObjectAnimator 在ValueAnimator 的基础上,经过反射技术实现了动画功能,也就像我刚刚所举的例子,子要给了 ObjectAnimator 两个值(from,to),在肯定动画类型(“scale,translate”),他就能自动生成动画。 与之造成区别,虽然咱们一样须要给 ValueAnimator 传递起始和最终两个值,可是 ValueAnimator 并不会自动去执行什么,而是会经过 addUpdateListener 的监听方法,在时间插值器的做用下,有序的返回一连串数值,而后咱们就能够经过这些数值,对控件进行设置。 ide
最近看各大厂商,彷佛都迷上了对 FloatingActionButton 进行操做,我就也来趁波热点。 布局
这个效果既能够经过动画框架实现,也可经过属性动画实现,这里我给你们讲下实现的方法。 首先是思路 因为这里咱们是采用 ValueAnimator 实现的,因此更具 ValueAnimator 的特性,在咱们对其设定完时间插值器以后,它会规律的返回一系列数。因此咱们只要更具这一系列数对控件的属性进行设置便可。post
private FloatingActionButton fab;
private ImageView imageView;
private int buttonSize = 0, imageSize = 0;
private float startY = 0;
private float endY = 0;
private ValueAnimator createValueAnimate(final View view, int start, int end){
ValueAnimator valueAnimator = ValueAnimator.ofInt(start, end);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
ViewGroup.LayoutParams params = view.getLayoutParams();
params.height = (int) animation.getAnimatedValue();
params.width = (int) animation.getAnimatedValue();
view.setLayoutParams(params);
}
});
return valueAnimator;
}
复制代码
能够看到咱们传入三个参数,这里我作的是缩放动画,因此给的分别是控件,控件当前大小和控件目标大小。而后根据监听器返回的值进行设置便可。 调用方面 我这里实现的是上拉隐藏和下拉显示,因此咱们须要判断下 Y轴 滑动方向:
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_UP:
startY = event.getY();
if ((startY - endY) < 0){
// 缩小
animationDown(fab, buttonSize);
animationDown(imageView, imageSize);
}else if ((startY - endY) > 0){
// 放大
animationUp(fab, buttonSize);
animationUp(imageView, imageSize);
}
break;
case MotionEvent.ACTION_DOWN:
endY = event.getY();
break;
}
return super.onTouchEvent(event);
}
private void animationDown(final View view, int originalSize){
ValueAnimator animator = createValueAnimate(view, originalSize, 0);
animator.addListener(new AnimatorListenerAdapter(){
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
view.setVisibility(View.GONE);
}
});
animator.setInterpolator(new BounceInterpolator());
animator.setDuration(500).start();
}
private void animationUp(final View view, int originalSize){
view.setVisibility(View.VISIBLE);
ValueAnimator animator = createValueAnimate(view, 0, originalSize);
animator.setInterpolator(new BounceInterpolator());
animator.setDuration(500).start();
}
复制代码
这里咱们会发现,因为是属性动画,因此改变的直接就是控件的大小,这就致使了一个问题,若是是实时的获取控件大小。那么咱们在执行完多小动画,也就是 animationDown 后,就没法在得到控件原始大小了。 因此这里咱们在 onResume 方法中获取控件大小:
@Override
protected void onResume() {
super.onResume();
fab.post(new Runnable() {
@Override
public void run() {
buttonSize = fab.getHeight();
}
});
imageView.post(new Runnable() {
@Override
public void run() {
imageSize = imageView.getHeight();
}
});
}
复制代码
属性动画能够做为 ViewGroup 增长活减小控件是的动画,是的界面的变换不是那么的突兀,其实细心的同窗可能有发现,android 是自带切换效果的,可是形式比较单一,因此这里我经过自定义 ObjectAnimator 的方法。
用一样的方法设置 remove 动画
LayoutTransition transition = new LayoutTransition();
ObjectAnimator appendAnimator = ObjectAnimator.ofPropertyValuesHolder(
(ImageView) null,
PropertyValuesHolder.ofFloat("scaleX", 0.0f, 1.0f),
PropertyValuesHolder.ofFloat("scaleY", 0.0f, 1.0f),
PropertyValuesHolder.ofFloat("alpha" , 0.0f, 1.0f)
);
appendAnimator.setInterpolator(new BounceInterpolator());
transition.setAnimator(LayoutTransition.APPEARING, appendAnimator);
transition.setDuration(LayoutTransition.APPEARING, transition.getDuration(LayoutTransition.APPEARING));
transition.setStartDelay(LayoutTransition.APPEARING, transition.getStartDelay(LayoutTransition.APPEARING));
ObjectAnimator removeAnimator = ObjectAnimator.ofPropertyValuesHolder(
(ImageView) null,
PropertyValuesHolder.ofFloat("scaleX", 1.0f, 0.0f),
PropertyValuesHolder.ofFloat("scaleY", 1.0f, 0.0f),
PropertyValuesHolder.ofFloat("alpha", 1.0f, 0.0f)
);
removeAnimator.setInterpolator(new BounceInterpolator());
transition.setAnimator(LayoutTransition.DISAPPEARING, removeAnimator);
transition.setDuration(LayoutTransition.DISAPPEARING, transition.getDuration(LayoutTransition.DISAPPEARING));
transition.setStartDelay(LayoutTransition.DISAPPEARING, transition.getStartDelay(LayoutTransition.DISAPPEARING));
复制代码
最后经过 setLayoutTransition 将这个 LayoutTransition 对象付给你的 ViewGroup 便可
layout = findViewById(R.id.layout);
layout.setLayoutTransition(transition);
复制代码
测试是分为添加控件和移除控件,功能在活动中动态的执行: 添加方法:
移除方法
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.btn_add_image:
ImageView imageView = new ImageView(ExtendActivity.this);
imageView.setScaleType(ImageView.ScaleType.FIT_XY);
imageView.setImageResource(R.drawable.heart);
ViewGroup.LayoutParams params = new LinearLayout.LayoutParams(200, 200);
imageView.setLayoutParams(params);
layout.addView(imageView,0);
break;
case R.id.btn_remove_image:
int count = layout.getChildCount();
if (count > 0){
layout.removeViewAt(0);
}
break;
}
}
复制代码
项目 Demo 点击前往https://github.com/FishInWater-1999/android_view_user_defined_first
到此为止全部属性动画的使用基本介绍完毕
因为是我的学习的总结,若是有问题或是我我的疏漏,但愿你们在评论区给我留言
祝你们编程愉快,少码 bug ,哈哈哈