Android动画深入原理分析

简介:

动画分类

Android动画可以分3种:View动画,帧动画和属性动画;属性动画为API11的新特性,在低版本是无法直接使用属性动画的,但可以用nineoldAndroids来实现(但是本质还是viiew动画)。学习本篇内容主要掌握以下知识:

1,View动画以及自定义View动画。
2,View动画的一些特殊使用场景。
3,对属性动画做了一个全面的介绍。
4,使用动画的一些注意事项。


view动画

  • View动画的四种变换效果对应着Animation的四个子类:TranslateAnimation(平移动画)、ScaleAnimation(缩放动画)、RotateAnimation(旋转动画)和AlphaAnimation(透明度动画),他们即可以用代码来动态创建也可以用XML来定义,推荐使用可读性更好的XML来定义。
  • <set>标签表示动画集合,对应AnimationSet类,它可以包含若干个动画,并且他的内部也可以嵌套其他动画集合。android:interpolator 表示动画集合所采用的插值器,插值器影响动画速度,比如非匀速动画就需要通过插值器来控制动画的播放过程。
    android:shareInterpolator表示集合中的动画是否和集合共享同一个插值器,如果集合不指定插值器,那么子动画就需要单独指定所需的插值器或默认值。
  • Animation通过setAnimationListener方法可以给View动画添加过程监听。
  • 自定义View动画只需要继承Animation这个抽象类,并重写initialize和applyTransformation方法,在initialize方法中做一些初始化工作,在applyTransformation中进行相应的矩形变换,很多时候需要采用Camera来简化矩形变换过程。
  • 帧动画是顺序播放一组预先定义好的图片,类似电影播放;使用简单但容易引发OOM,尽量避免使用过多尺寸较大的图片。

  • view动画应用场景

    LayoutAnimation作用于ViewGroup,为ViewGroup指定一个动画,当他的子元素出场的时候都会具有这种动画,ListView上用的多,LayoutAnimation也是一个View动画。
    代码实现:

    [html]  view plain  copy
     print ? 在CODE上查看代码片 派生到我的代码片
    1. <?xml version="1.0" encoding="utf-8"?>  
    2. <layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"  
    3.  android:animationOrder="normal"  
    4.  android:delay="0.3" android:animation="@anim/anim_item"/>  
    5.   
    6. //--- animationOrder 表示子元素的动画的顺序,有三种选项:  
    7. //normal(顺序显示)、reverse(逆序显示)和random(随机显示)。  
    8.   
    9. <?xml version="1.0" encoding="utf-8"?>  
    10. <set xmlns:android="http://schemas.android.com/apk/res/android"  
    11.  android:duration="300"  
    12.  android:shareInterpolator="true">  
    13.  <alpha  
    14.      android:fromAlpha="0.0"  
    15.      android:toAlpha="1.0" />  
    16.  <translate  
    17.      android:fromXDelta="300"  
    18.      android:toXDelta="0" />  
    19. </set>  
    第一种,在布局中引用LayoutAnimation
    [html]  view plain  copy
     print ? 在CODE上查看代码片 派生到我的代码片
    1. <ListView  
    2.      android:id="@+id/lv"  
    3.      android:layout_width="match_parent"  
    4.      android:layout_height="0dp"  
    5.      android:layout_weight="1"  
    6.      android:layoutAnimation="@anim/anim_layout"/>  
    第二种,代码种使用
    [html]  view plain  copy
     print ? 在CODE上查看代码片 派生到我的代码片
    1. Animation animation = AnimationUtils.loadAnimation(this, R.anim.anim_item);  
    2. LayoutAnimationController controller = new LayoutAnimationController(animation);  
    3. controller.setDelay(0.5f);  
    4. controller.setOrder(LayoutAnimationController.ORDER_NORMAL);  
    5. listview.setLayoutAnimation(controller);  

    帧动画

      逐帧动画(Frame-by-frame Animations)从字面上理解就是一帧挨着一帧的播放图片,类似于播放电影的效果。不同于View动画,Android系统提供了一个类AnimationDrawable来实现帧动画,帧动画比较简单,我们看一个例子就行了。
    [html]  view plain  copy
     print ? 在CODE上查看代码片 派生到我的代码片
    1. <?xml version="1.0" encoding="utf-8"?>  
    2. <animation-list xmlns:android="http://schemas.android.com/apk/res/android"  
    3.     android:oneshot="false">  
    4.   
    5.     <item  
    6.         android:drawable="@mipmap/lottery_1"  
    7.         android:duration="200" />  
    8.   // ...省略很多  
    9.     <item  
    10.         android:drawable="@mipmap/lottery_6"  
    11.         android:duration="200" />  
    12.   
    13. </animation-list>  

    然后
    [html]  view plain  copy
     print ? 在CODE上查看代码片 派生到我的代码片
    1. imageView.setImageResource(R.drawable.frame_anim);  
    2. AnimationDrawable animationDrawable = (AnimationDrawable) imageView.getDrawable();  
    3. animationDrawable.start();//启动start,关闭stop  

    属性动画

    属性动画是Android 3.0新加入(api 11)的功能,不同于之前的view动画(看过的都知道,view动画比如实现的位移其实不是真正的位置移动,只是实现了一些简单的视觉效果)。属性动画对之前的动画做了很大的拓展,毫不夸张的说,属性动画可以实现任何动画效果,因为在作用的对象是属性(对象),属性动画中有几个概念需要我们注意下,

    ValueAnimator、ObjectAnimator、AnimatorSet等。

    属性动画作用属性

    1,属性动画可以对任意对象的属性进行动画而不仅仅是View,属性动画默认间隔300ms,默认帧率10ms/帧。
    2,看一段代码
    [html]  view plain  copy
     print ? 在CODE上查看代码片 派生到我的代码片
    1. <set  
    2.   android:ordering=["together" | "sequentially"]>  
    3.   
    4.     <objectAnimator  
    5.         android:propertyName="string"  
    6.         android:duration="int"  
    7.         android:valueFrom="float | int | color"  
    8.         android:valueTo="float | int | color"  
    9.         android:startOffset="int"  
    10.         android:repeatCount="int"  
    11.         android:repeatMode=["repeat" | "reverse"]  
    12.         android:valueType=["intType" | "floatType"]/>  
    13.   
    14.     <animator  
    15.         android:duration="int"  
    16.         android:valueFrom="float | int | color"  
    17.         android:valueTo="float | int | color"  
    18.         android:startOffset="int"  
    19.         android:repeatCount="int"  
    20.         android:repeatMode=["repeat" | "reverse"]  
    21.         android:valueType=["intType" | "floatType"]/>  
    22.   
    23.     <set>  
    24.         ...  
    25.     </set>  
    26. </set>  

    <set>
    它代表的就是一个AnimatorSet对象。里面有一个 ordering属性,主要是指定动画的播放顺序。

    <objectAnimator> 
    它表示一个ObjectAnimator对象。它里面有很多属性,我们重点需要了解的也是它。
    android:propertyName -------属性名称,例如一个view对象的”alpha”和”backgroundColor”。
    android:valueFrom   --------变化开始值
    android:valueTo ------------变化结束值
    android:valueType -------变化值类型 ,它有两种值:intType和floatType,默认值floatType。
    android:duration ---------持续时间
    android:startOffset ---------动画开始延迟时间
    android:repeatCount --------重复次数,-1表示无限重复,默认为-1
    android:repeatMode 重复模式,前提是android:repeatCount为-1 ,它有两种值:”reverse”和”repeat”,分别表示反向和顺序方向。

    <animator>
    它对应的就是ValueAnimator对象。它主要有以下属性。
    android:valueFrom
    android:valueTo
    android:duration
    android:startOffset
    android:repeatCount
    android:repeatMode
    android:valueType

    定义了一组动画之后,我们怎么让它运行起来呢?
    [html]  view plain  copy
     print ? 在CODE上查看代码片 派生到我的代码片
    1. AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(myContext,  
    2.     R.anim.property_animator);  
    3. set.setTarget(myObject);//myObject表示作用的对象  
    4. set.start();  

    插值器和估值器

    时间插值器(TimeInterpolator)的作用是根据时间流逝的百分比来计算出当前属性值改变的百分比,系统预置的有LinearInterpolator(线性插值器:匀速动画),AccelerateDecelerateInterpolator(加速减速插值器:动画两头慢中间快),DecelerateInterpolator(减速插值器:动画越来越慢)。

    注:这里的插值器很多,可以翻看我之前关于插值器的讲解。

    估值器(TypeEvaluator)的作用是根据当前属性改变的百分比来计算改变后的属性值。系统预置有IntEvaluator 、FloatEvaluator 、ArgbEvaluator。

    举个简单的例子吧
    [html]  view plain  copy
     print ? 在CODE上查看代码片 派生到我的代码片
    1. public class IntEvaluator implements TypeEvaluator<Integer> {  
    2.  public Integer evaluate(float fraction, Integer startValue, Integer endValue) {  
    3.      int startInt = startValue;  
    4.      return (int)(startInt + fraction * (endValue - startInt));  
    5.  }  
    6. }  
    上述代码就是计算当前属性所占总共的百分百。

    插值器和估值器除了系统提供之外,我们还可以自定义实现,自定义插值器需要实现Interpolator或者TimeInterpolator;自定义估值器算法需要实现TypeEvaluator。

    属性动画监听器

    属性动画监听器用于监听动画的播放过程,主要有两个接口:AnimatorUpdateListener和AnimatorListener 。
    AnimatorListener 
    [html]  view plain  copy
     print ? 在CODE上查看代码片 派生到我的代码片
    1. public static interface AnimatorListener {  
    2.     void onAnimationStart(Animator animation); //动画开始  
    3.     void onAnimationEnd(Animator animation); //动画结束  
    4.     void onAnimationCancel(Animator animation); //动画取消  
    5.     void onAnimationRepeat(Animator animation); //动画重复播放  
    6. }  
    AnimatorUpdateListener
    [html]  view plain  copy
     print ? 在CODE上查看代码片 派生到我的代码片
    1. public static interface AnimatorUpdateListener {  
    2.     void onAnimationUpdate(ValueAnimator animator);  
    3. }  

    应用场景

    这里我们先提一个问题:给Button加一个动画,让Button在2秒内将宽带从当前宽度增加到500dp,也行你会说,很简单啊,直接用view动画就可以实现,view动画不是有个缩放动画,但是你可以试试,view动画是不支持对宽度和高度进行改变的。Button继承自TextView,setWidth是对TextView的,所以直接对Button做setWidth是不行的。那么要怎么做呢?
    针对上面的问题,官网api给出了如下的方案:
    • 给你的对象加上get和set方法,如果你有权限的话
    • 用一个类来包装原始对象,间接提高get和set方法
    • 采用ValueAnimator,监听动画执行过程,实现属性的改变

    有了上面的说明,我们大致明白了,要实现开始说的这个问题的效果,我们需要用一个间接的类来实现get和set方法或者自己实现一个ValueAnimator。
    第一种,自己封装一个类实现get和set方法,这也是我们常用的,拓展性强

    [html]  view plain  copy
     print ? 在CODE上查看代码片 派生到我的代码片
    1. public class ViewWrapper {  
    2.  private View target;  
    3.  public ViewWrapper(View target) {  
    4.      this.target = target;  
    5.  }  
    6.  public int getWidth() {  
    7.      return target.getLayoutParams().width;  
    8.  }  
    9.  public void setWidth(int width) {  
    10.      target.getLayoutParams().width = width;  
    11.      target.requestLayout();  
    12.  }  
    13. }  

    第二种,采用ValueAnimator,监听动画过程。

    [html]  view plain  copy
     print ? 在CODE上查看代码片 派生到我的代码片
    1. private void startValueAnimator(final View target, final int start, final int end) {  
    2.    ValueAnimator valueAnimator = ValueAnimator.ofInt(1, 100);  
    3.    valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {  
    4.        private IntEvaluator mEvaluation = new IntEvaluator();//新建一个整形估值器作为临时变量  
    5.   
    6.        @Override  
    7.        public void onAnimationUpdate(ValueAnimator animation) {  
    8.            //获得当前动画的进度值 1~100之间  
    9.            int currentValue = (int) animation.getAnimatedValue();  
    10.            //获得当前进度占整个动画过程的比例,浮点型,0~1之间  
    11.            float fraction = animation.getAnimatedFraction();  
    12.            //调用估值器,通过比例计算出宽度   
    13.            int targetWidth = mEvaluation.evaluate(fraction, start, end);  
    14.            target.getLayoutParams().width = targetWidth;  
    15.            //设置给作用的对象,刷新页面  
    16.            target.requestLayout();  
    17.        }  
    18.    });  
    19. }  

    属性动画的工作原理

    属性动画的工作原理,主要是对作用的对象不断的调用get/set方法来改变初始值和最终值,然后set到动画属性上即可。然后通过消息机制(Handler(不过这里的Handler不是我们常用的handler,而是AnimationHandler,它其实本质就是一个Runable)和Looper去将动画执行出来),通过代码我们发现它调了JNI的代码,不过这个我们不用关心,我们直接看ObjectAnimator.start()
    [html]  view plain  copy
     print ? 在CODE上查看代码片 派生到我的代码片
    1. private void start(boolean playBackwards) {  
    2.         if(Looper.myLooper() == null) {  
    3.             throw new AndroidRuntimeException("Animators may only be run on Looper threads");  
    4.         } else {  
    5.             this.mPlayingBackwards = playBackwards;  
    6.             this.mCurrentIteration = 0;  
    7.             this.mPlayingState = 0;  
    8.             this.mStarted = true;  
    9.             this.mStartedDelay = false;  
    10.             ((ArrayList)sPendingAnimations.get()).add(this);  
    11.             if(this.mStartDelay == 0L) {  
    12.                 this.setCurrentPlayTime(this.getCurrentPlayTime());  
    13.                 this.mPlayingState = 0;  
    14.                 this.mRunning = true;  
    15.                 if(this.mListeners != null) {  
    16.                     ArrayList animationHandler = (ArrayList)this.mListeners.clone();  
    17.                     int numListeners = animationHandler.size();  
    18.   
    19.                     for(int i = 0; i < numListeners; ++i) {  
    20.                         ((AnimatorListener)animationHandler.get(i)).onAnimationStart(this);  
    21.                     }  
    22.                 }  
    23.             }  
    24.   
    25.             ValueAnimator.AnimationHandler var5 = (ValueAnimator.AnimationHandler)sAnimationHandler.get();  
    26.             if(var5 == null) {  
    27.                 var5 = new ValueAnimator.AnimationHandler(null);  
    28.                 sAnimationHandler.set(var5);  
    29.             }  
    30.   
    31.             var5.sendEmptyMessage(0);  
    32.         }  
    33.     }  

    [html]  view plain  copy
     print ? 在CODE上查看代码片 派生到我的代码片
    1. private static final ThreadLocal<ArrayList<ValueAnimator>> sAnimations = new ThreadLocal() {  
    2.        protected ArrayList<ValueAnimator> initialValue() {  
    3.            return new ArrayList();  
    4.        }  
    5.    };  
    6.    private static final ThreadLocal<ArrayList<ValueAnimator>> sPendingAnimations = new ThreadLocal() {  
    7.        protected ArrayList<ValueAnimator> initialValue() {  
    8.            return new ArrayList();  
    9.        }  
    10.    };  
    11.    private static final ThreadLocal<ArrayList<ValueAnimator>> sDelayedAnims = new ThreadLocal() {  
    12.        protected ArrayList<ValueAnimator> initialValue() {  
    13.            return new ArrayList();  
    14.        }  
    15.    };  
    16.    private static final ThreadLocal<ArrayList<ValueAnimator>> sEndingAnims = new ThreadLocal() {  
    17.        protected ArrayList<ValueAnimator> initialValue() {  
    18.            return new ArrayList();  
    19.        }  
    20.    };  
    21.    private static final ThreadLocal<ArrayList<ValueAnimator>> sReadyAnims = new ThreadLocal() {  
    22.        protected ArrayList<ValueAnimator> initialValue() {  
    23.            return new ArrayList();  
    24.        }  
    25.    };  

    这里系统怎么计算每一帧的动画的呢,看看下面的代码
    [html]  view plain  copy
     print ? 在CODE上查看代码片 派生到我的代码片
    1. void animateValue(float fraction) {  
    2.         fraction = this.mInterpolator.getInterpolation(fraction);  
    3.         this.mCurrentFraction = fraction;  
    4.         int numValues = this.mValues.length;  
    5.   
    6.         int numListeners;  
    7.         for(numListeners = 0; numListeners < numValues; ++numListeners) {  
    8.             this.mValues[numListeners].calculateValue(fraction);  
    9.         }  
    10.   
    11.         if(this.mUpdateListeners != null) {  
    12.             numListeners = this.mUpdateListeners.size();  
    13.   
    14.             for(int i = 0; i < numListeners; ++i) {  
    15.                 ((ValueAnimator.AnimatorUpdateListener)this.mUpdateListeners.get(i)).onAnimationUpdate(this);  
    16.             }  
    17.         }  
    18.   
    19.     }  

    不过我们知道要改变动画,一定调用了get/set方法,那我们重点看下这相关的代码。这段代码在setProperty方法里面
    [html]  view plain  copy
     print ? 在CODE上查看代码片 派生到我的代码片
    1. public void setProperty(Property property) {  
    2.        if(this.mValues != null) {  
    3.            PropertyValuesHolder valuesHolder = this.mValues[0];  
    4.            String oldName = valuesHolder.getPropertyName();  
    5.            valuesHolder.setProperty(property);  
    6.            this.mValuesMap.remove(oldName);  
    7.            this.mValuesMap.put(this.mPropertyName, valuesHolder);  
    8.        }  
    9.   
    10.        if(this.mProperty != null) {  
    11.            this.mPropertyName = property.getName();  
    12.        }  
    13.   
    14.        this.mProperty = property;  
    15.        this.mInitialized = false;  
    16.    }  
    这里有一个PropertyValuesHolder,顾名思义这是一个操作数据的类,和我们的adapter的Holder差不多,该方法的get方法主要用到了反射。
    [html]  view plain  copy
     print ? 在CODE上查看代码片 派生到我的代码片
    1. private void setupValue(Object target, Keyframe kf) {  
    2.        if(this.mProperty != null) {  
    3.            kf.setValue(this.mProperty.get(target));  
    4.        }  
    5.   
    6.        try {  
    7.            if(this.mGetter == null) {  
    8.                Class e = target.getClass();  
    9.                this.setupGetter(e);  
    10.            }  
    11.   
    12.            kf.setValue(this.mGetter.invoke(target, new Object[0]));  
    13.        } catch (InvocationTargetException var4) {  
    14.            Log.e("PropertyValuesHolder", var4.toString());  
    15.        } catch (IllegalAccessException var5) {  
    16.            Log.e("PropertyValuesHolder", var5.toString());  
    17.        }  
    18.   
    19.    }  

    代码就看到这,有兴趣的可以去看下源码



    使用属性动画需要注意的事项

  • 使用帧动画时,当图片数量较多且图片分辨率较大的时候容易出现OOM,需注意,尽量避免使用帧动画。
  • 使用无限循环的属性动画时,在Activity退出时即使停止,否则将导致Activity无法释放从而造成内存泄露
  • View动画是对View的影像做动画,并不是真正的改变了View的状态,因此有时候会出现动画完成后View无法隐藏(setVisibility(View.GONE)失效),这时候调用view.clearAnimation()清理View动画即可解决。
  • 不要使用px,使用px会导致不同设备上有不同的效果。
  • View动画是对View的影像做动画,View的真实位置没有变动,也就导致点击View动画后的位置触摸事件不会响应,属性动画不存在这个问题。
  • 使用动画的过程中,使用硬件加速可以提高动画的流畅度。
  • 动画在3.0以下的系统存在兼容性问题,特殊场景可能无法正常工作,需做好适配工作。
  • 目录
    相关文章
    |
    10月前
    |
    开发框架 前端开发 Android开发
    Flutter 与原生模块(Android 和 iOS)之间的通信机制,包括方法调用、事件传递等,分析了通信的必要性、主要方式、数据传递、性能优化及错误处理,并通过实际案例展示了其应用效果,展望了未来的发展趋势
    本文深入探讨了 Flutter 与原生模块(Android 和 iOS)之间的通信机制,包括方法调用、事件传递等,分析了通信的必要性、主要方式、数据传递、性能优化及错误处理,并通过实际案例展示了其应用效果,展望了未来的发展趋势。这对于实现高效的跨平台移动应用开发具有重要指导意义。
    922 4
    |
    4月前
    |
    Android开发 开发者
    Android利用SVG实现动画效果
    本文介绍了如何在Android中利用SVG实现动画效果。首先通过定义`pathData`参数(如M、L、Z等)绘制一个简单的三角形SVG图形,然后借助`objectAnimator`实现动态的线条绘制动画。文章详细讲解了从配置`build.gradle`支持VectorDrawable,到创建动画文件、关联SVG与动画,最后在Activity中启动动画的完整流程。此外,还提供了SVG绘制原理及工具推荐,帮助开发者更好地理解和应用SVG动画技术。
    191 30
    |
    4月前
    |
    Android开发 UED 计算机视觉
    Android自定义view之线条等待动画(灵感来源:金铲铲之战)
    本文介绍了一款受游戏“金铲铲之战”启发的Android自定义View——线条等待动画的实现过程。通过将布局分为10份,利用`onSizeChanged`测量最小长度,并借助画笔绘制动态线条,实现渐变伸缩效果。动画逻辑通过四个变量控制线条的增长与回退,最终形成流畅的等待动画。代码中详细展示了画笔初始化、线条绘制及动画更新的核心步骤,并提供完整源码供参考。此动画适用于加载场景,提升用户体验。
    371 5
    Android自定义view之线条等待动画(灵感来源:金铲铲之战)
    |
    4月前
    |
    API Android开发 开发者
    Android颜色渐变动画效果的实现
    本文介绍了在Android中实现颜色渐变动画效果的方法,重点讲解了插值器(TypeEvaluator)的使用与自定义。通过Android自带的颜色插值器ArgbEvaluator,可以轻松实现背景色的渐变动画。文章详细分析了ArgbEvaluator的核心代码,并演示了如何利用Color.colorToHSV和Color.HSVToColor方法自定义颜色插值器MyColorEvaluator。最后提供了完整的源码示例,包括ColorGradient视图类和MyColorEvaluator类,帮助开发者更好地理解和应用颜色渐变动画技术。
    122 3
    |
    4月前
    |
    Android开发 开发者
    Android SVG动画详细例子
    本文详细讲解了在Android中利用SVG实现动画效果的方法,通过具体例子帮助开发者更好地理解和应用SVG动画。文章首先展示了动画的实现效果,接着回顾了之前的文章链接及常见问题(如属性名大小写错误)。核心内容包括:1) 使用阿里图库获取SVG图形;2) 借助工具将SVG转换为VectorDrawable;3) 为每个路径添加动画绑定属性;4) 创建动画文件并关联SVG;5) 在ImageView中引用动画文件;6) 在Activity中启动动画。文末还提供了完整的代码示例和源码下载链接,方便读者实践操作。
    255 65
    |
    4月前
    |
    XML Java Maven
    Android线条等待动画JMWorkProgress(可添加依赖直接使用)
    这是一篇关于Android线条等待动画JMWorkProgress的教程文章,作者计蒙将其代码开源至GitHub,提升可读性。文章介绍了如何通过添加依赖库使用该动画,并详细讲解了XML与Java中的配置方法,包括改变线条颜色、宽度、添加文字等自定义属性。项目已支持直接依赖集成(`implementation &#39;com.github.Yufseven:JMWorkProgress:v1.0&#39;`),开发者可以快速上手实现炫酷的等待动画效果。文末附有GitHub项目地址,欢迎访问并点赞支持!
    111 26
    |
    4月前
    |
    XML Android开发 数据格式
    Android中SlidingDrawer利用透明动画提示效果
    本文介绍了在Android中使用`SlidingDrawer`实现带有透明动画提示效果的方法。通过XML布局配置`SlidingDrawer`的把手(handle)和内容(content),结合Activity中的代码实现动态动画效果。最终实现了交互性强、视觉效果良好的滑动抽屉功能。
    Android中SlidingDrawer利用透明动画提示效果
    |
    4月前
    |
    XML Java Android开发
    Android 动画之帧动画 + 补间动画 + 属性动画
    本文介绍了Android开发中的三种动画类型:帧动画、补间动画和属性动画。帧动画通过依次播放一系列静态图片实现动态效果,支持Java代码与XML两种实现方式。补间动画基于起始和结束位置自动生成过渡效果,涵盖透明度、位移、旋转、缩放及组合动画等多种形式,并可搭配插值器优化动画过程。属性动画则通过改变对象属性实现动画,支持透明度、位移、旋转、缩放及组合动画,灵活性更高且适用于更复杂的场景。文中提供了详细的代码示例,帮助开发者快速上手。
    251 15
    |
    4月前
    |
    Android开发 开发者
    Android自定义view之围棋动画(化繁为简)
    本文介绍了Android自定义View的动画实现,通过两个案例拓展动态效果。第一个案例基于`drawArc`方法实现单次动画,借助布尔值控制动画流程。第二个案例以围棋动画为例,从简单的小球直线运动到双向变速运动,最终实现循环动画效果。代码结构清晰,逻辑简明,展示了如何化繁为简实现复杂动画,帮助读者拓展动态效果设计思路。文末提供完整源码,适合初学者和进阶开发者学习参考。
    Android自定义view之围棋动画(化繁为简)
    |
    4月前
    |
    Java Android开发 开发者
    Android自定义view之围棋动画
    本文详细介绍了在Android中自定义View实现围棋动画的过程。从测量宽高、绘制棋盘背景,到创建固定棋子及动态棋子,最后通过属性动画实现棋子的移动效果。文章还讲解了如何通过自定义属性调整棋子和棋盘的颜色及动画时长,并优化视觉效果,如添加渐变色让白子更明显。最终效果既可作为围棋动画展示,也可用作加载等待动画。代码完整,适合进阶开发者学习参考。

    热门文章

    最新文章