android动画

参考博客:https://blog.csdn.net/carson_ho/article/details/73087488

一 帧动画

1. 作用对象

视图控件(View

  1. AndroidTextView、Button等等
  2. 不可作用于View组件的属性,如:颜色、背景、长度等等

2. 原理

  • 将动画拆分为 帧 的形式,且定义每一帧 = 每一张图片
  • 逐帧动画的本质:按序播放一组预先定义好的图片

工具:GifSplitter可拆分gif得到图片

这里只使用java动态设置动画,并没有使用xml配置。 

public class FrameAnimActivity extends AppCompatActivity {
    private ImageView mImageView;
    private AnimationDrawable mAnimationDrawable;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_frame_anim);
        initView();
        initAnim();
    }

    private void initView() {
        mImageView = findViewById(R.id.frame_anim);
        mAnimationDrawable = new AnimationDrawable();
    }

    private void initAnim() {
        for (int i = 0; i < 44; i++) {
            int id = getResources().getIdentifier("a" + (i + 1), "drawable", getPackageName());
            Drawable drawable = getResources().getDrawable(id);
            mAnimationDrawable.addFrame(drawable, 100);
        }
        mAnimationDrawable.setOneShot(false);
        mImageView.setImageDrawable(mAnimationDrawable);
        mAnimationDrawable.start();
    }
}

 效果图:录的屏上传不了,只能贴gif了,一样的结果。

 

  •  优点:使用简单、方便
  • 缺点:容易引起 OOM,因为会使用大量 & 尺寸较大的图片资源
  • 应用场景:较为复杂的个性化动画效果。

二:补间动画

1.作用对象

视图控件(View

  1. AndroidTextView、Button等等
  2. 不可作用于View组件的属性,如:颜色、背景、长度等等  

2.原理 

通过确定开始的视图样式 & 结束的视图样式、中间动画变化过程由系统补全来确定一个动画。

根据不同的动画效果,补间动画分为4种动画:

  • 平移动画(Translate
  • 缩放动画(scale
  • 旋转动画(rotate
  • 透明度动画(alpha

 demo:此处只使用动态设置的方式,录屏上传不了,所以只贴代码了。

public class TweenActivity extends AppCompatActivity {
    private Button mButton;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_tween);

        mButton = findViewById(R.id.translate_btn);
    }

    @Override
    protected void onResume() {
        super.onResume();
        animSet();
    }

    private void translate() {
        TranslateAnimation animation = new TranslateAnimation(0, 500, 0, 500);
        animation.setDuration(3000);
        animation.setRepeatCount(Animation.INFINITE);
        mButton.startAnimation(animation);
    }

    private void scale() {
        ScaleAnimation scaleAnimation = new ScaleAnimation(0, 2, 0, 2, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
        scaleAnimation.setDuration(3000);
        scaleAnimation.setRepeatCount(Animation.INFINITE);
        mButton.startAnimation(scaleAnimation);
    }

    private void rotate() {
        RotateAnimation rotateAnimation = new RotateAnimation(0, 360, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
        rotateAnimation.setDuration(3000);
        rotateAnimation.setRepeatCount(Animation.INFINITE);
        mButton.startAnimation(rotateAnimation);
    }

    private void alpha() {
        AlphaAnimation alphaAnimation = new AlphaAnimation(1, 0);
        alphaAnimation.setDuration(3000);
        alphaAnimation.setRepeatCount(Animation.INFINITE);
        mButton.startAnimation(alphaAnimation);
    }

    private void animSet() {
        AnimationSet animationSet = new AnimationSet(true);

        TranslateAnimation animation = new TranslateAnimation(0, 500, 0, 0);
        animation.setDuration(3000);
        animation.setRepeatCount(Animation.INFINITE);

        ScaleAnimation scaleAnimation = new ScaleAnimation(0, 1, 0, 1, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
        scaleAnimation.setDuration(3000);
        scaleAnimation.setRepeatCount(Animation.INFINITE);

        RotateAnimation rotateAnimation = new RotateAnimation(0, 360, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
        rotateAnimation.setDuration(3000);
        rotateAnimation.setRepeatCount(Animation.INFINITE);

        AlphaAnimation alphaAnimation = new AlphaAnimation(1, 0.5f);
        alphaAnimation.setDuration(3000);
        alphaAnimation.setRepeatCount(Animation.INFINITE);

        animationSet.addAnimation(animation);
        animationSet.addAnimation(scaleAnimation);
        animationSet.addAnimation(rotateAnimation);
        animationSet.addAnimation(alphaAnimation);

        mButton.startAnimation(animationSet);
    }
}

三 属性动画

1. 背景

Android系统一开始就提供了两种实现动画的方式:逐帧动画(Frame Animation)补间动画( Tweened animation )

逐帧动画 & 补间动画存在一定的缺点:

a. 作用对象局限:View

即补间动画 只能够作用在视图View上,即只可以对一个ButtonTextView、甚至是LinearLayout、或者其它继承自View的组件进行动画操作,但无法对非View的对象进行动画操作

  1. 有些情况下的动画效果只是视图的某个属性 & 对象而不是整个视图;
  2. 如,现需要实现视图的颜色动态变化,那么就需要操作视图的颜色属性从而实现动画效果,而不是针对整个视图进行动画操作

b. 没有改变View的属性,只是改变视觉效果

  • 补间动画只是改变了View的视觉效果,而不会真正去改变View的属性。
  • 如,将屏幕左上角的按钮 通过补间动画 移动到屏幕的右下角
  • 点击当前按钮位置(屏幕右下角)是没有效果的,因为实际上按钮还是停留在屏幕左上角,补间动画只是将这个按钮绘制到屏幕右下角,改变了视觉效果而已。

c. 动画效果单一

  • 补间动画只能实现平移、旋转、缩放 & 透明度这些简单的动画需求
  • 一旦遇到相对复杂的动画效果,即超出了上述4种动画效果,那么补间动画则无法实现。 
  • 即在功能 & 可扩展性有较大局限性 

为了解决补间动画的缺陷,在 Android 3.0(API 11)开始,系统提供了一种全新的动画模式:属性动画(Property Animation

2.特点

  • 作用对象进行了扩展:不只是View对象,甚至没对象也可以
  • 动画效果:不只是4种基本变换,还有其他动画效果
  • 作用领域:API11后引入的

3.工作原理

  • 在一定时间间隔内,通过不断对值进行改变,并不断将该值赋给对象的属性,从而实现该对象在该属性上的动画效果

可以是任意对象的任意属性

  • 属性动画有两个非常重要的类:ValueAnimator 类 & ObjectAnimator 类
  • 其实属性动画的使用基本都是依靠这两个类

4.使用

1 ValueAnimator类

  • 定义:属性动画机制中 最核心的一个类
  • 实现动画的原理:通过不断控制 值 的变化,再不断 手动 赋给对象的属性,从而实现动画效果

ValueAnimator类中有3个重要方法:

  1. ValueAnimator.ofInt(int values)
  2. ValueAnimator.ofFloat(float values)
  3. ValueAnimator.ofObject(int values)

估值器(TypeEvaluator),可继承自定义估值器。

  • 作用:设置动画 如何从初始值 过渡到 结束值 的逻辑
  1. 插值器(Interpolator)决定 值 的变化模式(匀速、加速等)
  2. 估值器(TypeEvaluator)决定 值 的具体变化数值
  • ValueAnimator.ofInt()实现了 **将初始值 以浮点型的形式 过渡到结束值 ** 的逻辑,那么这个过渡逻辑具体是怎么样的呢?
  • 其实是系统内置了一个 IntEvaluator估值器,内部实现了初始值与结束值 以浮点型的过渡逻辑
public class MainActivity extends AppCompatActivity {
    private TextView mTextView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initView();
        valueAnim();
    }

    private void initView() {
        mTextView = findViewById(R.id.text);
    }

    private void valueAnim() {
        ValueAnimator valueAnimator = ValueAnimator.ofInt(mTextView.getLayoutParams().width, 500);
        valueAnimator.setDuration(9000);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                int value = (Integer) animation.getAnimatedValue();
                mTextView.getLayoutParams().width = value;
                mTextView.requestLayout();
            }
        });
        valueAnimator.start();
    }
}

2 ObjectAnimator类

实现动画的原理:

直接对对象的属性值进行改变操作,从而实现动画效果

  1. 如直接改变 View的 alpha 属性 从而实现透明度的动画效果
  2. 继承自ValueAnimator类,即底层的动画实现机制是基于ValueAnimator
  • 本质原理: 通过不断控制 值 的变化,再不断 自动 赋给对象的属性,从而实现动画效果。

ObjectAnimator与 ValueAnimator类的区别:

  • ValueAnimator 类是先改变值,然后 手动赋值 给对象的属性从而实现动画;是 间接 对对象属性进行操作;
  • ObjectAnimator 类是先改变值,然后 自动赋值 给对象的属性从而实现动画;是 直接 对对象属性进行操作;

自动赋给对象的属性的本质是调用该对象属性的set() & get()方法进行赋值

  • 所以,ObjectAnimator.ofFloat(Object object, String property, float ....values)的第二个参数传入值的作用是:让ObjectAnimator类根据传入的属性名 去寻找 该对象对应属性名的 set() & get()方法,从而进行对象属性值的赋值

3 .组合动画

  • 单一动画实现的效果相当有限,更多的使用场景是同时使用多种动画效果,即组合动画
  • 实现 组合动画 的功能:AnimatorSet

AnimatorSet.play(Animator anim)   :播放当前动画
AnimatorSet.after(long delay)   :将现有动画延迟x毫秒后执行
AnimatorSet.with(Animator anim)   :将现有动画和传入的动画同时执行
AnimatorSet.after(Animator anim)   :将现有动画插入到传入的动画之后执行
AnimatorSet.before(Animator anim) :  将现有动画插入到传入的动画之前执行

4. ViewPropertyAnimator用法

  • 从上面可以看出,属性动画的本质是对值操作
  • Java是面向对象的,所以 Google 团队添加面向对象操作的属性动画使用 - ViewPropertyAnimator

使用解析 
View.animate().xxx().xxx(); 
// ViewPropertyAnimator的功能建立在animate()上 
// 调用animate()方法返回值是一个ViewPropertyAnimator对象,之后的调用的所有方法都是通过该实例完成 
// 调用该实例的各种方法来实现动画效果 
// ViewPropertyAnimator所有接口方法都使用连缀语法来设计,每个方法的返回值都是它自身的实例
// 因此调用完一个方法后可直接连缀调用另一方法,即可通过一行代码就完成所有动画效果

5.监听动画

  • Animation类通过监听动画开始 / 结束 / 重复 / 取消时刻来进行一系列操作,如跳转页面等等
  • 通过在Java代码里addListener()设置

      Animation.addListener(new AnimatorListener() {
          @Override
          public void onAnimationStart(Animation animation) {
              //动画开始时执行
          }
      
           @Override
          public void onAnimationRepeat(Animation animation) {
              //动画重复时执行
          }

         @Override
          public void onAnimationCancel()(Animation animation) {
              //动画取消时执行
          }
    
          @Override
          public void onAnimationEnd(Animation animation) {
              //动画结束时执行
          }
      });

// 特别注意:每次监听必须4个方法都重写。

动画适配器AnimatorListenerAdapter

  • 背景:有些时候我们并不需要监听动画的所有时刻
  • 问题:但addListener(new AnimatorListener())监听器是必须重写4个时刻方法,这使得接口方法重写太累赘
  • 解决方案:采用动画适配器(AnimatorListenerAdapter),解决实现接口繁琐 的问题

anim.addListener(new AnimatorListenerAdapter() {  
// 向addListener()方法中传入适配器对象AnimatorListenerAdapter()
// 由于AnimatorListenerAdapter中已经实现好每个接口
// 所以这里不实现全部方法也不会报错
    @Override  
    public void onAnimationStart(Animator animation) {  
    // 如想只想监听动画开始时刻,就只需要单独重写该方法就可以
    }  
});  

public class ObAnimActivity extends AppCompatActivity {
    private Button mButton;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_ob_anim);

        initView();
        initAnim();
    }

    private void initView() {
        mButton = findViewById(R.id.obj_anim_btn);
    }

    private void initAnim() {
        float translationX = mButton.getTranslationX();
        ObjectAnimator translation = ObjectAnimator.ofFloat(mButton, "translationX", translationX, 300, translationX);
        ObjectAnimator rotation = ObjectAnimator.ofFloat(mButton, "rotation", 0, 360);
        ObjectAnimator scale = ObjectAnimator.ofFloat(mButton, "scaleX", 1, 2, 1);
        ObjectAnimator alpha = ObjectAnimator.ofFloat(mButton, "alpha", 1, 0, 1);

        AnimatorSet animatorSet = new AnimatorSet();
        animatorSet.play(translation).with(rotation).before(scale).after(alpha);
        animatorSet.setDuration(5000);

        animatorSet.start();
    }
}

 4.总结

属性动画的本质原理:通过不断对值进行改变,并不断将该值赋给对象的属性,从而实现该对象在该属性上的动画效果;

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
对于计算机专业的学生而言,参加各类比赛能够带来多方面的益处,具体包括但不限于以下几点: 技能提升: 参与比赛促使学生深入学习和掌握计算机领域的专业知识与技能,如编程语言、算法设计、软件工程、网络安全等。 比赛通常涉及实际问题的解决,有助于将理论知识应用于实践中,增强问题解决能力。 实践经验: 大多数比赛都要求参赛者设计并实现解决方案,这提供了宝贵的动手操作机会,有助于积累项目经验。 实践经验对于计算机专业的学生尤为重要,因为雇主往往更青睐有实际项目背景的候选人。 团队合作: 许多比赛鼓励团队协作,这有助于培养学生的团队精神、沟通技巧和领导能力。 团队合作还能促进学生之间的知识共享和思维碰撞,有助于形成更全面的解决方案。 职业发展: 获奖经历可以显著增强简历的吸引力,为求职或继续深造提供有力支持。 某些比赛可能直接与企业合作,提供实习、工作机会或奖学金,为学生的职业生涯打开更多门路。 网络拓展: 比赛是结识同行业人才的好机会,可以帮助学生建立行业联系,这对于未来的职业发展非常重要。 奖金与荣誉: 许多比赛提供奖金或奖品,这不仅能给予学生经济上的奖励,还能增强其成就感和自信心。 荣誉证书或奖状可以证明学生的成就,对个人品牌建设有积极作用。 创新与研究: 参加比赛可以激发学生的创新思维,推动科研项目的开展,有时甚至能促成学术论文的发表。 个人成长: 在准备和参加比赛的过程中,学生将面临压力与挑战,这有助于培养良好的心理素质和抗压能力。 自我挑战和克服困难的经历对个人成长有着深远的影响。 综上所述,参加计算机领域的比赛对于学生来说是一个全面发展的平台,不仅可以提升专业技能,还能增强团队协作、沟通、解决问题的能力,并为未来的职业生涯奠定坚实的基础。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值