Android深入分析属性动画源码

1.先看一段动画的代码实现

ObjectAnimator alpha = ObjectAnimator.ofFloat(view, "alpha", 1, 0,1);
alpha.setDuration(500);
alpha.start();

代码很简单,上面三行代码就可以开启一个透明度变化的动画。 那么android系统到底是如何实现的呢?进入源码分析。

1)看第一行代码:

ObjectAnimator alpha = ObjectAnimator.ofFloat(view, "alpha", 1, 0,1);

创建了一个ObjectAnimator对象,并把values数组设置给了anim对象。

public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) {
        ObjectAnimator anim = new ObjectAnimator(target, propertyName);
        anim.setFloatValues(values);
        return anim;
}

ObjectAnimator 构造函数中。将传过来的View对象和propertyName赋值给成员变量。

private ObjectAnimator(Object target, String propertyName) {
        //将传过来的View对象赋值给成员变量mTarget
        setTarget(target);
        //将propertyName赋值给成员变量mPropertyName
        setPropertyName(propertyName);
}

注意这个mTarget为什么要用一个软引用?

那是为了防止Activity发生内存泄漏。因为会有Activity已经退出,但是动画可能还未执行完,这个时候View得不到释放的话,会引发Activity内存泄漏。

private WeakReference<Object> mTarget;
public void setTarget(@Nullable Object target) {
    final Object oldTarget = getTarget();
    if (oldTarget != target) {
        if (isStarted()) {
            cancel();
        }
        //将传进来的View对象赋值给mTarget
        mTarget = target == null ? null : new WeakReference<Object>(target);
        mInitialized = false;
    }
}

再看第二行代码做了啥?anim.setFloatValues(values);

首次进来mValues==null,mProperty==null,所以会执行这行代码。 setValues(PropertyValuesHolder.ofFloat(mPropertyName, values))。

public void setFloatValues(float... values) {
    if (mValues == null || mValues.length == 0) {
        if (mProperty != null) {
            setValues(PropertyValuesHolder.ofFloat(mProperty, values));
        } else {
            setValues(PropertyValuesHolder.ofFloat(mPropertyName, values));
        }
    } else {
        super.setFloatValues(values);
    }
}

setValue将得到的 PropertyValuesHolder数组赋值给成员变量PropertyValuesHolder[] mValues;

再看PropertyValuesHolder.ofFloat(mPropertyName, values));

先调用super构造函数,将propertyName赋值给父类的mPropertyName,

 public FloatPropertyValuesHolder(String propertyName, float... values) {
    super(propertyName);
    setFloatValues(values);
}

然后再调用setFloatValues(values);

public void setFloatValues(float... values) {
    super.setFloatValues(values);
    //将mKeyframes强转为mFloatKeyframes
    mFloatKeyframes = (Keyframes.FloatKeyframes) mKeyframes;
}
//调用父类方法创建了KeyframeSet对象,赋值给了mKeyframes
public void setFloatValues(float... values) {
    mValueType = float.class;
    mKeyframes = KeyframeSet.ofFloat(values);
}

KeyframeSet.ofFloat(values);这行代码创建了一个关键帧的集合。

public static KeyframeSet ofFloat(float... values) {
    boolean badValue = false;
    int numKeyframes = values.length;
    //创建一个value长度的 FloatKeyFrame的数组
    FloatKeyframe keyframes[] = new FloatKeyframe[Math.max(numKeyframes,2)];
    //numKeyframes==1的话,其实是没有View是没有动画的。如果传过来的values的长度是1的话,会报错的。
    if (numKeyframes == 1) {
        keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f);
        keyframes[1] = (FloatKeyframe) Keyframe.ofFloat(1f, values[0]);
        if (Float.isNaN(values[0])) {
            badValue = true;
        }
    } else {
        //下面的代码才是关键的 Keyframe ofFloat(float fraction, float value)是创建关键帧。
        //fraction英文单词意思是部分,在这作为参数的意思是:从动画启示位置,到当前位置,所占的整个动画的百分比。
        //value就是某个部分对应的属性值。
        // 比如传进来的value值是1.0f 2.0f 3.0f 4.0f,5.0f。整个动画有5个值。因为1.0是初始值,要完成整个动画需要4步。
         //从1-2,2-3,3-4,4-5;4个部分。
         //第0个位置是起始位置,所以他所在的部分就是0。第一个位置就是四分之一,第二个就是四分之二....
         //第i个位置,所在整个动画的部分就是i/(i-1)。而这个位置对应的动画的属性值,就是value[i]
        //所以这个keyframes[]数组的目的就是保存,动画的关键位置所占的百分比和关键位置对应的属性值。
        keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f, values[0]);
        for (int i = 1; i < numKeyframes; ++i) {
            keyframes[i] =
                    (FloatKeyframe) Keyframe.ofFloat((float) i / (numKeyframes - 1), values[i]);
            if (Float.isNaN(values[i])) {
                badValue = true;
            }
        }
    }
    return new FloatKeyframeSet(keyframes);
}

到这为止,第一行代码执行完毕。

ObjectAnimator.ofFloat(view, "alpha", 1, 0,1)

将view赋值给ObjectAnimator成员变量。

将propertyName赋值给PropertyValuesHolder,会通过属性name来反射它的set方法,用来修改属性值。

创建KeyframeSet,关键帧集合。将value数组转换成对应的关键帧集合,通过动画执行的时间,来计算当前时间对应的属性值,然后再调用view的set属性方法,从而达到形成动画的目的。

这块的代码会再后面看到。

2).看动画的第二行代码alpha.start();

ObjectAnimator的父类是ValueAnimator。start()里面调用到的方法会在子类和父类里跳来跳去,这也增大了阅读的难度。

首先看ValueAnimator#start(boolean playBackwards)方法

addAnimationCallback:向Choreographer注册回调函数,我们知道Choreographer可以接受Vsync信号,16.66ms一次,也是屏幕刷新一次的时间。这样在屏幕刷新的时候,就可以通过向Choreographer注册回调函数进行动画的更新。

 private void start(boolean playBackwards) {
        //Animators 必须运行在一个Looper不能为空的线程中,因为动画需要涉及到Choreographer。
        if (Looper.myLooper() == null) {
            throw new AndroidRuntimeException("Animators may only be run on Looper threads");
        }
        mStarted = true;
        mPaused = false;
        mRunning = false;
        mAnimationEndRequested = false;
        mStartTime = -1;
        //这个是一个回调函数。这块是由Choreographer回调的,稍后分析。
        addAnimationCallback(0);
        if (mStartDelay == 0 || mSeekFraction >= 0 || mReversing) {
            //开始动画。
            startAnimation();
        }
    }

先看startAnimation方法(),会在这个方法中调用initAnimation();

在这会先调用子类ObjectAnimator,然后在调用父类的ValueAnimator的initAnimation方法。

先看子类的initAnimation(),这个方法根据propertyName来反射view的set属性方法。

void initAnimation() {
     if (!mInitialized) {
        //先拿到target,也就是view对象。
         final Object target = getTarget();
         if (target != null) {
         // PropertyValuesHolder[] mValues;这个values就是PropertyValuesHolder的集合。
             final int numValues = mValues.length;
             for (int i = 0; i < numValues; ++i) {
                //在PropertyValuesHolder中传进了属性值,下面这行代码就是根据属性值,来反射view的set方法,
                //通过set方法,就可以动态的改变view的属性值的变化。
                 mValues[i].setupSetterAndGetter(target);
             }
         }
         //调用父类的initAnimation()方法
         super.initAnimation();
     }
}

再看父类ValueAnimator的initAnimation方法。调用了PropertyValuesHolder的init()方法。

在init方法中,向KeyframeSet关键帧集合设置了一个估值器,这个用来计算属性值的,后面会看到具体的计算方法。

void initAnimation() {
    if (!mInitialized) {
        int numValues = mValues.length;
        for (int i = 0; i < numValues; ++i) {
        //调用PropertyValuesHolder#init方法
            mValues[i].init();
        }
        mInitialized = true;
    }
}
 void init() {
    if (mEvaluator == null) {
        //得到一个估值器
        mEvaluator = (mValueType == Integer.class) ? sIntEvaluator :
                (mValueType == Float.class) ? sFloatEvaluator : null;
    }
    if (mEvaluator != null) {
        //向KeyframeSet中设置一个估值器,这个估值器用来计算动画在某个时刻的属性值。
        mKeyframes.setEvaluator(mEvaluator);
    }
}
private static final TypeEvaluator sIntEvaluator = new IntEvaluator();
private static final TypeEvaluator sFloatEvaluator = new FloatEvaluator();
public class FloatEvaluator implements TypeEvaluator<Number> {
    //This function returns the result of linearly interpolating the start and end values
    这个方法返回一个在动画开始和结束之间的一个线性的结果。其实就是个一元一次方程,来计算动画当前的位置。
    //result = x0 + t * (v1 - v0)
    public Float evaluate(float fraction, Number startValue, Number endValue) {
        float startFloat = startValue.floatValue();
        return startFloat + fraction * (endValue.floatValue() - startFloat);
    }
}

至此,initAnimation的代码已经执行完毕。主要做的工作可以总结为两点:

1.调用PropertyValuesHolder的setupSetterAndGetter方法,通过反射拿到View的setter方法。

2.向KeyframeSet中设置一个估值器,用来计算动画某一时刻的属性值。

3)接下来看ValueAnimator#addAnimationCallback

这个方法是向Choreographer设置了一个会回调函数,每隔16.66ms回调一次,用来刷新动画。

还设置了一个回调集合,在Choreographer的回调函数中,回调集合里面的回调函数,来实现属性动画的刷新

private void addAnimationCallback(long delay) {
    if (!mSelfPulse) {
        return;
    }
    //getAnimationHandler 就是上面创建的AnimationHandler。
    //将this作为 AnimationFrameCallback的回调,会回调doAnimationFrame(long frameTime)
    getAnimationHandler().addAnimationFrameCallback(this, delay);
}
//AnimationHandler#addAnimationFrameCallback
getProvider()拿到的是MyFrameCallbackProvider。
public void addAnimationFrameCallback(final AnimationFrameCallback callback, long delay) {
    if (mAnimationCallbacks.size() == 0) {
        //向Choreographer加入一个回调函数mFrameCallback
        getProvider().postFrameCallback(mFrameCallback);
    }
    //将添加的回调函数加入一个回调的集合。
    if (!mAnimationCallbacks.contains(callback)) {
        mAnimationCallbacks.add(callback);
    }
}

先看这个getProvider().postFrameCallback(mFrameCallback);这个就是向Choreographer注册一个回调。

final Choreographer mChoreographer = Choreographer.getInstance();
    //这行代码是向编舞者Choreographer添加了一个回调函数。
    public void postFrameCallback(Choreographer.FrameCallback callback) {
        mChoreographer.postFrameCallback(callback);
}
Choreographer中
public void postFrameCallback(FrameCallback callback) {
        postFrameCallbackDelayed(callback, 0);
}

下面这行代码就是向Choreographer添加CallBackType为CALLBACK_ANIMATION,Token为FRAME_CALLBACK_TOKEN的回调函数。 callback 就是传进来的mFrameCallback。

public void postFrameCallbackDelayed(FrameCallback callback, long delayMillis) {
    postCallbackDelayedInternal(CALLBACK_ANIMATION,
            callback, FRAME_CALLBACK_TOKEN, delayMillis);
}

省略中间的调用过程。。。这块的代码在Choreographer源码分析过。

MyFrameCallbackProvider#postFrameCallback就是向Choreographer添加一个回调函数。 我们知道,Choreographer在接收到Vsync信号后调用这些回调函数。

 void doFrame(long frameTimeNanos, int frame) {
 doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);
    doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
    doCallbacks(Choreographer.CALLBACK_INSETS_ANIMATION, frameTimeNanos);
    doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
    doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos);
 }
最终会调到这里,根据上面传过来的token,转换成不同的回调函数,调用不同的方法。
//在将View绘制时,调用的是else分支的回调
//在动画这里,传进来的是mFrameCallback,Choreographer.FrameCallback的实例,会调用到doFrame方法
 public void run(long frameTimeNanos) {
    if (token == FRAME_CALLBACK_TOKEN) {
        ((FrameCallback)action).doFrame(frameTimeNanos);
    } else {
        ((Runnable)action).run();
    }
}
private final Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() {
    @Override
    public void doFrame(long frameTimeNanos) {
        doAnimationFrame(getProvider().getFrameTime());
        if (mAnimationCallbacks.size() > 0) {
            //再次向Choreographer注册回调,等到下一次Vsync信号来的时候调用,
            //针对于60Hz的屏幕,刷新时间间隔是16.66ms,也就是Vsync回调的时间间隔
            //也就是说属性动画16.66毫秒会改变一次
            getProvider().postFrameCallback(this);
        }
    }
};

Choreographer中每个16.6ms会回调doFrame方法(),在doAnimationFrame方法中,就会回调注册的回调集合。

private void doAnimationFrame(long frameTime) {
    long currentTime = SystemClock.uptimeMillis();
    final int size = mAnimationCallbacks.size();
    for (int i = 0; i < size; i++) {
        final AnimationFrameCallback callback = mAnimationCallbacks.get(i);
        if (callback == null) {
            continue;
        }
        //遍历mAnimationCallbacks,调用callBack回调函数,
        //这个回调函数是ValueAnimator的doAnimationFrame
        if (isCallbackDue(callback, currentTime)) {
            callback.doAnimationFrame(frameTime);
        }
    }
}

doAnimationFrame是AnimationFrameCallback的回调函数,由ValueAnimator实现。

 public final boolean doAnimationFrame(long frameTime) {
       //frameTime 这个时间是从Choreographer传过来的时间,
       //记录为上一次动画刷新的时间
        mLastFrameTime = frameTime;
        final long currentTime = Math.max(frameTime, mStartTime);
        boolean finished = animateBasedOnTime(currentTime);
        return finished;
 }
 public final boolean doAnimationFrame(long frameTime) {
       //frameTime 这个时间是从Choreographer传过来的时间,
       //记录为上一次动画刷新的时间
        mLastFrameTime = frameTime;
        final long currentTime = Math.max(frameTime, mStartTime);
        boolean finished = animateBasedOnTime(currentTime);
        return finished;
 }
boolean animateBasedOnTime(long currentTime) {
     boolean done = false;
     if (mRunning) {
        //拿到总时间
         final long scaledDuration = getScaledDuration();
         //通过计算得到动画当前执行占比多少。(currentTime - mStartTime)动画执行的时间
         //除以scaledDuration总时间,得到就是已经执行的部分,如果是一个重复的动画,这个值可能会大于1.
         final float fraction = scaledDuration > 0 ?
                 (float)(currentTime - mStartTime) / scaledDuration : 1f;
        //下面通过计算对fraction进行修正,减去重复执行的部分,得到真正的在一次动画中要执行到哪一部分
         mOverallFraction = clampFraction(fraction);
         float currentIterationFraction = getCurrentIterationFraction(
                 mOverallFraction, mReversing);
         animateValue(currentIterationFraction);
     }
     return done;
 }

注意animateValue,这个方法在父类ValueAnimator和子类ObjectAnimator都有实现。

所以这里先调用子类ObjectAnimator的方法。

//这个方法是调用的子类的方法
void animateValue(float fraction) {
     final Object target = getTarget();
     if (mTarget != null && target == null) {
         cancel();
         return;
     }
     //先调用父类的方法
     super.animateValue(fraction);
     //再回到子类
     int numValues = mValues.length;
     for (int i = 0; i < numValues; ++i) {
        //给View设置改变后的属性值
         mValues[i].setAnimatedValue(target);
     }
 }

先看super.animateValue方法,这个方法就是去计算动画变动后的属性值。

 void animateValue(float fraction) {
     //通过插值器,来修改。如果没有设置插值器,那么fraction的变化就是匀速的。
     //经过插值器的计算,fraction的变化就会呈现出加速、减速变化的效果。
     fraction = mInterpolator.getInterpolation(fraction);
     mCurrentFraction = fraction;
     int numValues = mValues.length;
     for (int i = 0; i < numValues; ++i) {
         //PropertyValuesHolder[] mValues,因为一个View可以有多个属性动画,所以这用一个数组来存储。
         mValues[i].calculateValue(fraction);
     }
 }
AccelerateDecelerateInterpolator 插值器
public float getInterpolation(float input) {
    return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
}
void calculateValue(float fraction) {
    //mKeyframes 就是前面创建的关键帧集合KeyframeSet
    Object value = mKeyframes.getValue(fraction);
   // 将得到的值,赋值给mAnimatedValue
    mAnimatedValue = mConverter == null ? value : mConverter.convert(value);
}

下面这个方法是真正去计算改变后的属性值。通过估值器mEvaluator去计算的。

public Object getValue(float fraction) {
    //第一关键帧记做前一关键帧
    Keyframe prevKeyframe = mFirstKeyframe;
    for (int i = 1; i < mNumKeyframes; ++i) {
        //得到下一关键帧
        Keyframe nextKeyframe = mKeyframes.get(i);
        if (fraction < nextKeyframe.getFraction()) {
            final TimeInterpolator interpolator = nextKeyframe.getInterpolator();
            //得到前一关键帧,对应的部分
            final float prevFraction = prevKeyframe.getFraction();
            //fraction - prevFraction 当前要执行的部分距离前一关键帧是多少。
            //nextKeyframe.getFraction() - prevFraction,这一帧有多少
            //两者相除,得到的就是当前部分在这一帧的占比
            float intervalFraction = (fraction - prevFraction) /
                (nextKeyframe.getFraction() - prevFraction);
            if (interpolator != null) {
                //通过插值器来修改,这一部分的大小
                intervalFraction = interpolator.getInterpolation(intervalFraction);
            }
            //通过估值器,来计算属性值要变化到多少
            //这个估值器就是上面赋值的FloatEvaluator或IntEvaluator
            return mEvaluator.evaluate(intervalFraction, prevKeyframe.getValue(),
                    nextKeyframe.getValue());
        }
        prevKeyframe = nextKeyframe;
    }
    // shouldn't reach here
    //不应该执行到这里,在上面的for循环就应该返回当前动画,属性变化的大小。
    return mLastKeyframe.getValue();
}

通过估值器计算view的属性值。

public Float evaluate(float fraction, Number startValue, Number endValue) {
    float startFloat = startValue.floatValue();
    //通过一个一元一次方程,来计算得到当前的属性值。
    return startFloat + fraction * (endValue.floatValue() - startFloat);
}

至此,动画要变动后的属性值,已经计算出来了,

通过 mValues[i].setAnimatedValue(target);用来修改View的属性值大小。

 void setAnimatedValue(Object target) {
       //前面已经通过反射拿到了View的setter方法
      if (mSetter != null) {
          try {
               //拿到属性值大小,
              mTmpValueArray[0] = getAnimatedValue();
             //通过反射,修改view属性值的大小
              mSetter.invoke(target, mTmpValueArray);
          } catch (InvocationTargetException e) {
              Log.e("PropertyValuesHolder", e.toString());
          } catch (IllegalAccessException e) {
              Log.e("PropertyValuesHolder", e.toString());
          }
      }
  }
Object getAnimatedValue() {
    return mAnimatedValue;
}

至此,android属性动画的整个执行流程已经分析完毕。

可以总结以下几点:

1.ValueAnimator是父类,ObjectAnimator是子类,这里面封装了一个target,也就是view对象。

2.PropertyValuesHolder,有属性名,属性值,通过属名来反射view的setter方法,来动态修改属性值。

3.KeyframeSet,是一个关键帧集合,封装了定义动画是value数组的值,每一个值都被记录为一个关键帧FloatKeyframe。

4.通过插值器,可以改变属性变化的快慢,通过估值器计算属性值的大小。

5.给Choreographer注册了一个回调,每隔16.66ms回调一次,每一次回调都会去改变view属性值的大小。改变是通过fraction计算的,进而通过计算得到改变后的属性值大小。

这样动态的改变view属性值的大小,就连贯的形成一幅动画。

到此这篇关于Android深入分析属性动画源码的文章就介绍到这了,更多相关Android属性动画内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Android 属性动画ValueAnimator与插值器详解

    Android 属性动画ValueAnimator与插值器详解 一.ValueAnimator详解: ValueAnimator是整个动画的核心,ObjectAnimator即是继承自ValueAnimator来实现. ValueAnimator更像是一个数值发生器,用来产生具有一定规律的数字,从而让调动者来控制动画的实现过程. 1.ValueAnimator的使用: ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 100); val

  • Android源码解析之属性动画详解

    前言 大家在日常开发中离不开动画,属性动画更为强大,我们不仅要知道如何使用,更要知道他的原理.这样,才能得心应手.那么,今天,就从最简单的来说,了解下属性动画的原理. ObjectAnimator .ofInt(mView,"width",100,500) .setDuration(1000) .start(); ObjectAnimator#ofInt 以这个为例,代码如下. public static ObjectAnimator ofInt(Object target, Stri

  • Android利用属性动画实现优酷菜单

    利用属性动画实现优酷菜单,供大家参考,具体内容如下 布局文件 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="fill_parent" android:layout_height="fill_p

  • Android属性动画之ValueAnimator代码详解

    属性动画通过改变一个对象的属性值来进行动画,属性动画包含了以下几个特性: 1.持续时间(Duration) 主要用来定义动画的持续时间,默认值为300ms. 2.时间插值器(Time interpolation) 指定时间变化的百分比,就是当前流逝时间除以指定的持续时间,这个可以自定义,继承Interpolator,重写getInterpolation方法. 3.重复次数和行为(Repeat count and behavior) 指定动画的执行次数和动画的重复模式 4.动画集(Animator

  • Android动画教程之属性动画详解

    简介 Android 开发中,总是需要一些动画来优化用户的交互体验,提高用户满意度.因此,Google 为我们提供了一些用于处理动画效果的动画框架.Android 的动画框架分为两类: 传统动画(Animation):通过系统不断调用onDraw方法重绘界面,来达到动画的效果. 属性动画(Animator):通过操纵一个属性的get/set方法,真实地改变目标的某些属性. 传统动画框架的局限性 既然有了传统动画框架,Google 为什么还要创造一个属性动画框架呢? 我们下面举个例子来说明一下传统

  • Android动画系列之属性动画的基本使用教程

    前言 属性动画相较帧动画和补间动画更强大,帧动画和补间动画只能应用于 View 及其子类,而属性动画可以修改任何对象的属性值,属性值可在指定的一段时间内自动改变,根据对象属性值的变化进而实现更复杂的动画. 属性动画的常用设置 ValueAnimator ObjectAnimator 关键帧 插值器和估值器 属性动画的常用设置 下面是属性动画的常用设置,具体如下: //设置属性动画持续时间 animator.setDuration(2000); //设置属性插值器 animator.setInte

  • Android使用属性动画如何自定义倒计时控件详解

    为什么要引入属性动画? Android之前的补间动画机制其实还算是比较健全的,在android.view.animation包下面有好多的类可以供我们操作,来完成一系列的动画效果,比如说对View进行移动.缩放.旋转和淡入淡出,并且我们还可以借助AnimationSet来将这些动画效果组合起来使用,除此之外还可以通过配置Interpolator来控制动画的播放速度等等等等.那么这里大家可能要产生疑问了,既然之前的动画机制已经这么健全了,为什么还要引入属性动画呢? 其实上面所谓的健全都是相对的,如

  • Android属性动画实现图片从左到右逐渐消失

    前言:dp/dip代表独立像素,dpi代表屏幕每英寸像素点的个数,px与dp的转换公式为: px = dp *(dpi / 160) 一.效果图 二.源代码 AnimationActivity: package com.example.duoyi.clientaidl; import android.animation.Animator; import android.animation.ObjectAnimator; import android.support.annotation.NonN

  • Android属性动画特点详解

    本文实例为大家分享了Android属性动画使用的具体代码,供大家参考,具体内容如下 MainActivity.java /* 属性动画的特点:动画效果会改变控件的位置.且开启动画的是动画对象,而不是控件对象. 只有旋转的属性动画是经常用的,注意参数. 注意:这些方法都是安卓在3.0以后出现的新特性,所以要把AndroidManifest.xml里的android:minSdkVersion值修改为11以上 */ //注释后面有222的暂时不用管. public class MainActivit

  • Android深入分析属性动画源码

    1.先看一段动画的代码实现 ObjectAnimator alpha = ObjectAnimator.ofFloat(view, "alpha", 1, 0,1); alpha.setDuration(500); alpha.start(); 代码很简单,上面三行代码就可以开启一个透明度变化的动画. 那么android系统到底是如何实现的呢?进入源码分析. 1)看第一行代码: ObjectAnimator alpha = ObjectAnimator.ofFloat(view, &q

  • 实时获取股票数据的android app应用程序源码分享

    最近学习Android应用开发,不知道写一个什么样的程序来练练手,正好最近股票很火,就一个App来实时获取股票数据,取名为Mystock.使用开发工具Android Studio,需要从Android官网下载,下载地址:http://developer.android.com/sdk/index.html.不幸的是Android是Google公司的,任何和Google公司相关的在国内都无法直接访问,只能通过VPN访问. 下图为Android Studio打开一个工程的截图: 下面按步介绍Myst

  • Android消息循环机制源码深入理解

    Android消息循环机制源码 前言: 搞Android的不懂Handler消息循环机制,都不好意思说自己是Android工程师.面试的时候一般也都会问这个知识点,但是我相信大多数码农肯定是没有看过相关源码的,顶多也就是网上搜搜,看看别人的文章介绍.学姐不想把那个万能的关系图拿出来讨论. 近来找了一些关于android线程间通信的资料,整理学习了一下,并制作了一个简单的例子. andriod提供了 Handler 和 Looper 来满足线程间的通信.例如一个子线程从网络上下载了一副图片,当它下

  • Android实现屏幕锁定源码详解

    最近有朋友问屏幕锁定的问题,自己也在学习,网上找了下也没太详细的例子,看的资料书上也没有有关屏幕锁定程序的介绍,下个小决心,自己照着官方文档学习下,现在做好了,废话不多说,先发下截图,看下效果,需要注意的地方会加注释,有问题的朋友可以直接留言,我们共同学习交流,共同提高进步!直接看效果图: 一:未设置密码时进入系统设置的效果图如下: 二:设置密码方式预览: 三:密码解密效果图 四:九宫格解密时的效果图 下面来简单的看下源码吧,此处讲下,这个小DEMO也是临时学习下的,有讲的不明白的地方请朋友直接

  • Android Studio如何查看源码并调试的方法步骤

    背景 最近遇到一个Dialog问题,在6.0的手机上才可以重现,但是我们的工程已经是targetsdk=28了,点击Dialog的引用,只能看见28的class文件,现在我也越来越懒了,以前针对这种问题,我都会写个demo,去重现问题,但是现在希望能直接在当前工程快速定位出原因,能够基于源码debug肯定更好了,为了实现这个懒的目标,我找了很多办法,下面就一一介绍下 方法一 切换compilesdk studio默认使用的是gradle里配置的compilesdkversion,只要你把comp

  • Android串口通信apk源码详解(附完整源码)

    1.SerialPortHelper「Android串口通信」介绍 原项目地址 https://github.com/freyskill/SerialPortHelper Android串口通讯助手可以用于需要使用串口通信的Android外设,该库有如下特点: 1.串口通信部分使用C++实现,在笔者接触的部分设备上实测,使用C++实现与Google官方提供的Demo的方式要快: 2.支持且必须设置串口接收最大数据长度,初始化库时填入该参数,这样设置的原因是考虑在实际使用中,规定的串口通信协议格式

  • 解析Android框架之OkHttp3源码

    OkHttp流程图 OkHttp基本使用 gradle依赖 implementation 'com.squareup.okhttp3:okhttp:3.11.0' implementation 'com.squareup.okio:okio:1.15.0' /** *这里拿get请求来 * 异步的get请求 */ public void okhttpAsyn() { //设置超时的时间 OkHttpClient.Builder builder = new OkHttpClient.Builder

  • 解析Android框架之Volley源码

    Volley简单使用 我这里是以依赖架包的形式 ,大家也可以以gradle的形式进行依赖. 好了,接下来上代码了..... //获取volley的请求对象 RequestQueue requestQueue = Volley.newRequestQueue(getApplicationContext()); StringRequest stringRequest = new StringRequest(StringRequest.Method.GET, "http://www.baidu.com

  • Android文件存储SharedPreferences源码解析

    1.我们都知道SharedPreferences 是android可以用来存放key value的的文件. SharedPreferences sp = getSharedPreferences("fileName", Context.MODE_PRIVATE); SharedPreferences.Editor editor = sp.edit(); editor.putString("key","value"); editor.commit(

  • Android Jetpack 组件LiveData源码解析

    目录 前言 基本使用 疑问 源码分析 Observer ObserverWrapper LifecycleBoundObserver MutableLiveData postValue setValue 问题答疑 LiveData 特性引出的问题 问题解决 最后 前言 本文来分析下 LiveData 的源码,以及其在实际开发中的一些问题. 基本使用 一般来说 LiveData 都会配合 ViewModel 使用,篇幅原因关于 ViewModel 的内容将在后续博客中分析,目前可以将 ViewMo

随机推荐