前两篇《天啦噜!原来Android补间动画可以这么玩》和《天啦噜!原来Android帧动画这么简单》重点讲述了Android开发过程中补间动画和帧动画知识点,本篇文章我们重点总结一下属性动画的使用和原理。

Android动画系列:

  • 《天啦噜!原来Android补间动画可以这么玩》
  • 《天啦噜!原来Android帧动画这么简单》
  • 《天啦噜!原来Android属性动画也不过如此》

什么是属性动画

在一段时间内通过修改对象的属性而形成的动画叫属性动画(Property Animation),Google官方在Android 3.0添加Property Animation。属性动画的主要是修改对象的属性,如 View 的背景颜色、透明值、位置等。

属性动画和补间动画的区别

有同学可能会问不是已经有补间动画吗,为什么要引入属性动画?换句话说,Property Animation 到底能干哪些 Tween Animation 不能干的活呢?

Tween Animation 存在的问题:

  1. Tween Animation 只能作用于 View,不能作用于普通 Object 的属性。
  2. Tween Animation 只能改变 View 的一部分属性。Tween Animation 只支持修改 View 的这几个方面:Alpha、Scale、Translate、Rotate 和这些的组合,一旦想要改变的 View 的属性不在这个范围内,Tween Animation 就无能为力了,如 View 的 BackgroundColor。
  3. Tween Animation 只能改变 View 的“表面”位置,不能改变 View 的实际位置。

属性动画相关类

属性动画涉及的类主要有:

  1. Animator,所有 Animator 的父类,主要用于定义通用的接口。
  2. AnimatorSet,主要用于组合多个属性动画。
  3. ValueAnimator,属性动画的一种,主要用于根据起始值和终止值产生动画,只负责产生在起始值和终止值之间的值,
    不负责更新界面,需要用户自己实现更新界面的逻辑。
  4. ObjectAnimator,属性动画的一种,主要用于根据起始值和终止值产生动画,并将动画产生的值设置在目标对象上。
  5. TimeAnimator,提供了一个简单的回调机制,通过 TimeAnimator.TimeListener,在动画的每一帧处通知你。这个动画器没有时间,插值或是对象值设定。回调监听器为每一帧动画接受信息,包括总运行时间和从前一帧到现在的运行时间。

继承结构如下:

ValueAnimator和ObjectAnimator主要区别

该类作为ValueAnimator的子类不仅继承了ValueAnimator的所有方法和特性,并且还封装很多实用的方法,方便开发人员快速实现动画。同时,由于属性值会自动更新,使用ObjectAnimator实现动画不需要像ValueAnimator那样必须实现 ValueAnimator.AnimatorUpdateListener ,因此实现任意对象的动画显示就更加容易了。我们在大部分的开发工作中,都会使用ObjectAnimator而非ValueAnimator实现我们所需的动画效果。

属性动画实现形式

属性动画的实现形式有两种:xml创建和code实现。其中xml创建的xml动画文件要放在res/animator目录下,注意此处和补间动画(Tween Animation)存放位置不同。

通常情况下属性动画一般建议通过代码进行实现,因为他更灵活,尤其是在自定义View中常常有属性动画的身影。当然也需要根据实际场景自行选择,下边就通过这两种形式来总结一下属性动画几个类的使用。

ValueAnimator

ValueAnimator是Property Animation系统的核心类,它包含了配置Property Animation属性的大部分方法,那要实现一个Property Animation,都需要直接或间接使用ValueAnimator类。

一般使用ValueAnimator实现动画分为以下几个步骤:

  1. 调用ValueAnimation类中的ofInt(int…values)、ofFloat(String propertyName,float…values)等静态方法实例化ValueAnimator对象;
  2. 调用addUpdateListener(AnimatorUpdateListener mListener)方法为ValueAnimator对象设置属性变化的监听器,并在AnimatorUpdateListener 中的实现方法为目标对象的属性设置计算好的属性值。
  3. 创建自定义的插值器(Interpolator),调用setInterpolator(TimeInterpolator value)为ValueAniamtor设置自定义的Interpolator;(可选,不设置默认为缺省值)
  4. 创建自定义的估值器(TypeEvaluator),调用setEvaluator(TypeEvaluator value)为ValueAnimator设置自定义的TypeEvaluator;(可选,不设置默认为缺省值)
  5. 设置动画的持续时间、是否重复及重复次数等属性;
  6. 为ValueAnimator设置目标对象并开始执行动画。

需要注意目标对象的需要被设置的属性必须拥有get\set方法,格式类似 set()。

通过XML创建

语法

<animatorxmlns:android="http://schemas.android.com/apk/res/android"android:duration="int"android:interpolator="@[package:]anim/interpolator_resource"android:valueType=["intType" | "floatType"]android:valueFrom="float | int | color"android:valueTo="float | int | color"android:startOffset="int"android:repeatCount="int"android:repeatMode=["repeat" | "reverse"]/>

示例

//1. 创建 value_animator.xml
<?xml version="1.0" encoding="utf-8"?>
<animator xmlns:android="http://schemas.android.com/apk/res/android"android:duration="1800"android:interpolator="@android:anim/accelerate_decelerate_interpolator"android:valueType="floatType"android:valueFrom="-100"android:valueTo="800"android:startOffset="0"android:repeatCount="infinite"android:repeatMode="reverse"/>//2. 在代码中使用 value_animator
ValueAnimator mValueAnimator = (ValueAnimator) AnimatorInflater.loadAnimator(this, R.animator.value_animator);
mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {mTarget.setY((Float) animation.getAnimatedValue());}
});
mValueAnimator.start();

通过代码实现

语法

ValueAnimator valueAnimator = ValueAnimator.ofFloat(float... values);
valueAnimator.setDuration(long duration);
valueAnimator.setInterpolator(TimeInterpolator value);
valueAnimator.addUpdateListener(AnimatorUpdateListener listener);
…
valueAnimator.start();

示例

ValueAnimator mValueAnimator = ValueAnimator.ofFloat(0, 800);
mValueAnimator.setDuration(1800);
mValueAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
mValueAnimator.setRepeatCount(ValueAnimator.INFINITE);
mValueAnimator.setRepeatMode(ValueAnimator.REVERSE);
mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {mTarget.setY((Float) animation.getAnimatedValue());}
});
mValueAnimator.start();

ObjectAnimator

要动画显示 View 对象的某个属性,比如颜色或旋转值,我们所有要做的事情就是创建一个 Property animation,并设定对应的 View 属性。那接下来我们就用ObjectAnimator类来分别实现View的透明度渐变、收缩、移动和旋转等动画效果,那在此之前我们也来总结下使用ObjectAnimator实现动画的几个步骤,如下:

  1. 通过调用ofFloat()、ofInt()等方法创建ObjectAnimator对象,并设置目标对象、需要改变的目标属性名、初始值和结束值;
  2. 设置动画的持续时间、是否重复及重复次数等属性;
  3. 启动动画。

常用的几个属性值解释:

  • translationX 和 translationY:这两个属性控制着 View 的屏幕位置坐标变化量,以 layout 容器的左上角为坐标原点;
  • rotation、rotationX 和 rotationY:这三个属性控制着 2D 旋转角度(rotation属性)和围绕某枢轴点的 3D 旋转角度;
  • scaleX、scaleY:这两个属性控制着 View 围绕某枢轴点的 2D 缩放比例;
  • pivotX 和 pivotY: 这两个属性控制着枢轴点的位置,前述的旋转和缩放都是以此点为中心展开的,缺省的枢轴点是 View 对象的中心点;
  • x 和 y:这是指 View 在容器内的最终位置,等于 View 左上角相对于容器的坐标加上 translationX 和 translationY 后的值;
  • alpha:表示 View 的 alpha 透明度。缺省值为 1 (不透明),为 0 则表示完全透明(看不见);

通过XML创建

语法

<objectAnimatorxmlns:android="http://schemas.android.com/apk/res/android"android:duration="int"android:interpolator="@[package:]anim/interpolator_resource"android:propertyName="string"android:valueType=["intType" | "floatType"]android:valueFrom="float | int | color"android:valueTo="float | int | color"android:startOffset="int"android:repeatCount="int"android:repeatMode=["repeat" | "reverse"]/>

示例

//1. 创建 object_animator.xml
<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"android:duration="1800"android:interpolator="@android:anim/accelerate_decelerate_interpolator"android:propertyName="Y"android:valueType="floatType"android:valueFrom="0"android:valueTo="800"android:startOffset="0"android:repeatCount="infinite"android:repeatMode="reverse"/>//2. 在代码中使用 object_animator
ObjectAnimator mObjectAnimator = (ObjectAnimator) AnimatorInflater.loadAnimator(this, R.animator.object_animator);
mObjectAnimator.setTarget(mTarget);
mObjectAnimator.start();

通过代码实现

语法

ObjectAnimator objectAnimator = ObjectAnimator.ofObject(Object target, String propertyName, TypeEvaluator evaluator, Object... values);
objectAnimator.setDuration(long duration);
objectAnimator.setInterpolator(TimeInterpolator value);
…
objectAnimator.start();

示例

ObjectAnimator mObjectAnimator = ObjectAnimator.ofFloat(mTarget, "y", 0, 800);
mObjectAnimator.setDuration(1800);
mObjectAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
mObjectAnimator.setRepeatCount(ValueAnimator.INFINITE);
mObjectAnimator.setRepeatMode(ValueAnimator.REVERSE);
mObjectAnimator.start();

AnimatorSet

通过XML创建

语法

<setxmlns:android="http://schemas.android.com/apk/res/android"android:ordering=["together" | "sequentially"]><objectAnimatorandroid:propertyName="string"android:duration="int"android:valueFrom="float | int | color"android:valueTo="float | int | color"android:startOffset="int"android:repeatCount="int"android:repeatMode=["repeat" | "reverse"]android:valueType=["intType" | "floatType"]/><animatorandroid:duration="int"android:valueFrom="float | int | color"android:valueTo="float | int | color"android:startOffset="int"android:repeatCount="int"android:repeatMode=["repeat" | "reverse"]android:valueType=["intType" | "floatType"]/><set>...</set>
</set>

示例

//1. 创建 animator_set.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"android:ordering="together"><objectAnimatorandroid:duration="@integer/integer_one_thousand_and_eight_hundred"android:interpolator="@android:anim/accelerate_decelerate_interpolator"android:propertyName="Y"android:valueType="floatType"android:valueFrom="0"android:valueTo="800"android:startOffset="0"android:repeatCount="infinite"android:repeatMode="reverse"/><objectAnimatorandroid:duration="@integer/integer_one_thousand_and_eight_hundred"android:interpolator="@android:anim/accelerate_decelerate_interpolator"android:propertyName="ScaleX"android:valueType="floatType"android:valueFrom="1"android:valueTo="2"android:startOffset="0"android:repeatCount="infinite"android:repeatMode="reverse"/><objectAnimatorandroid:duration="@integer/integer_one_thousand_and_eight_hundred"android:interpolator="@android:anim/accelerate_decelerate_interpolator"android:propertyName="ScaleY"android:valueType="floatType"android:valueFrom="1"android:valueTo="2"android:startOffset="0"android:repeatCount="infinite"android:repeatMode="reverse"/>
</set>//2. 在代码中使用 animator_set
AnimatorSet mAnimatorSet = (AnimatorSet)AnimatorInflater.loadAnimator(this, R.animator.animator_set);
mAnimatorSet.setTarget(mTarget);
mAnimatorSet.start();

通过代码实现

语法

AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playTogether(Animator... items);
animatorSet.playSequentially(Animator... items);
//非必须
animatorSet.setTarget(mTarget);
…
animatorSet.start();

示例

ObjectAnimator translateYObjectAnimator = ObjectAnimator.ofFloat(mTarget, "y", 0, 800);
translateYObjectAnimator.setDuration(1800);
translateYObjectAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
translateYObjectAnimator.setRepeatCount(ValueAnimator.INFINITE);
translateYObjectAnimator.setRepeatMode(ValueAnimator.REVERSE);
ObjectAnimator scaleXObjectAnimator = ObjectAnimator.ofFloat(mTarget, "scaleX", 1, 2);
scaleXObjectAnimator.setDuration(1800);
scaleXObjectAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
scaleXObjectAnimator.setRepeatCount(ValueAnimator.INFINITE);
scaleXObjectAnimator.setRepeatMode(ValueAnimator.REVERSE);
ObjectAnimator scaleYObjectAnimator = ObjectAnimator.ofFloat(mTarget, "scaleY", 1, 2);
scaleYObjectAnimator.setDuration(1800);
scaleYObjectAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
scaleYObjectAnimator.setRepeatCount(ValueAnimator.INFINITE);
scaleYObjectAnimator.setRepeatMode(ValueAnimator.REVERSE);
mAnimatorSet = new AnimatorSet();
mAnimatorSet.playTogether(translateYObjectAnimator, scaleXObjectAnimator, scaleYObjectAnimator);
mAnimatorSet.playSequentially();
//非必须
//        mAnimatorSet.setTarget(mTarget);
mAnimatorSet.start();

监听属性动画

Property Animation 中一共有三种监听事件:

  • AnimatorListener;
  • AnimatorPauseListener;
  • AnimatorUpdateListener;

AnimatorListener

AnimatorListener 接口主要用于监听 Property Animation 的开始、结束、取消、重复状态,需要实现的方法分别是:

@Override
public void onAnimationStart(Animator animation) {}@Override
public void onAnimationEnd(Animator animation) {}@Override
public void onAnimationCancel(Animator animation) {}@Override
public void onAnimationRepeat(Animator animation) {}

AnimatorPauseListener

AnimatorPauseListener 主要用于监听 Property Animation 的暂停、恢复状态,需要实现的方法分别是:

@Override
public void onAnimationPause(Animator animation) {}@Override
public void onAnimationResume(Animator animation) {}

AnimatorUpdateListener

AnimatorUpdateListener 是 ValueAnimator 及其子类特有的接口,主要用于监听动画中值的变化,用于手动更新界面,需要实现的方法是:

@Override
public void onAnimationUpdate(ValueAnimator animation) {}

属性动画工作原理

当 ValueAnimator 调用 start 方法之后,ValueAnimator 会根据 Property Animation 当前运行时间与总的动画持续时间计算出一个时间消耗百分数(The elapsed fraction)。紧接着,ValueAnimator 将这个时间消耗百分数交给当前 ValueAnimator 的插值器(Interpolator),不同的 Interpolator 会根据不同的算法将这个时间消耗百分数转换成插值百分数(The interpolated fraction)。紧接着,ValueAnimator 会将这个插值百分数交给当前 ValueAnimator 的估值器(TypeEvaluator),不同的 TypeEvaluator 会根据不同的算法将这个插值百分数转换最终的动画值(The final value)。

拿AccelerateDecelerateInterpolator插值器举例:

public class AccelerateDecelerateInterpolator extends BaseInterpolatorimplements NativeInterpolatorFactory {public AccelerateDecelerateInterpolator() {}@SuppressWarnings({"UnusedDeclaration"})public AccelerateDecelerateInterpolator(Context context, AttributeSet attrs) {}/*** param input (The elapsed fraction)* return (The interpolated fraction)*/public float getInterpolation(float input) {return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;}/** @hide */@Overridepublic long createNativeInterpolator() {return NativeInterpolatorFactoryHelper.createAccelerateDecelerateInterpolator();}
}

上面这个属性动画的 Duration 为 40ms,Intepolator 为 AccelerateDecelerateInterpolator,Distance 为 40。

在 t = 10ms 时,The elapsed fraction 为 0.25 = 10/40,The interpolated fraction = (float)(Math.cos((0.25 + 1) * Math.PI) / 2.0f) + 0.5f = 0.14644662,The final value 为 5.8578648 = (40 - 0) * 0.14644662。

自定义插值器

自定义插值器要实现 Interpolator 接口,上篇文章已经有所说明,不做过多阐述。

public class DecelerateAccelerateInterpolator implements Interpolator {@Overridepublic float getInterpolation(float input) {return (float) ((Math.tan(Math.PI/2 * input - Math.PI/4) + 1)/2);}
}

自定义估值器

自定义估值器,只要实现 TypeEvaluator 接口,并实现其中定义的 evaluate 方法即可:

public class CustomTypeEvaluator implements TypeEvaluator {/*** param fraction 插值器最终值* param startValue 属性开始值* param endValue 属性结束值*/@Overridepublic Object evaluate(float fraction, Object startValue, Object endValue) {float startFloat = ((Number) startValue).floatValue();return 200 + fraction * (((Number) endValue).floatValue() - startFloat);}
}

ViewPropertyAnimator 使用简介

ViewPropertyAnimator、ObjectAnimator、ValueAnimator 这三种 Animator,它们其实是一种递进的关系:从左到右依次变得更加难用,也更加灵活。

它们的性能是一样的,因为 ViewPropertyAnimator 和 ObjectAnimator 的内部实现其实都是 ValueAnimator,ObjectAnimator 更是本来就是 ValueAnimator 的子类,它们三个的性能并没有差别。

它们的差别只是使用的便捷性以及功能的灵活性。所以在实际使用时候的选择,只要遵循一个原则就行:尽量用简单的。能用 View.animate() 实现就不用 ObjectAnimator,能用 ObjectAnimator 就不用 ValueAnimator。

当需要同时更改 View 的多个属性的时候,一般有三种方法:

  1. ObjectAnimator + AnimatorSet;
  2. PropertyValuesHolder + ObjectAnimator;
  3. ViewPropertyAnimator;

接下来,分别用三种方法分别实现同一种效果:View 的 Y 值从当前位置增到 400,Alpha 值 从 1.0f 变成 0.1f。

ObjectAnimator + AnimatorSet

ObjectAnimator alphaObjectAnimator = ObjectAnimator.ofFloat(mTarget, "alpha", 1.0f, 0.1f);
ObjectAnimator yObjectAnimator = ObjectAnimator.ofFloat(mTarget, "y", 400f);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playTogether(alphaObjectAnimator, yObjectAnimator);
animatorSet.start();

PropertyValuesHolder + ObjectAnimator

PropertyValuesHolder alphaPropertyValuesHolder = PropertyValuesHolder.ofFloat("alpha", 1.0f, 0.1f);
PropertyValuesHolder yPropertyValuesHolder = PropertyValuesHolder.ofFloat("y", 400f);
ObjectAnimator.ofPropertyValuesHolder(mTarget, alphaPropertyValuesHolder, yPropertyValuesHolder).start();

ViewPropertyAnimator

ViewPropertyAnimator viewPropertyAnimator = mTarget.animate();
viewPropertyAnimator.alpha(0.1f);
viewPropertyAnimator.y(400f);//也可以写成一句:
mTarget.animate().alpha(0.1f).y(400f);

PropertyValuesHolder

细心的同学可能会注意到,ValueAnimator、ObjectAnimator除了这些创建Animator实例的方法以外,都还有一个方法:

/*** valueAnimator的*/
public static ValueAnimator ofPropertyValuesHolder(PropertyValuesHolder... values)
/*** ObjectAnimator的*/
public static ObjectAnimator ofPropertyValuesHolder(Object target,PropertyValuesHolder... values)

PropertyValuesHolder这个类的意义就是,它其中保存了动画过程中所需要操作的属性和对应的值。我们通过ofFloat(Object target, String propertyName, float… values)构造的动画,ofFloat()的内部实现其实就是将传进来的参数封装成PropertyValuesHolder实例来保存动画状态。在封装成PropertyValuesHolder实例以后,后期的各种操作也是以PropertyValuesHolder为主的。

使用举例:

PropertyValuesHolder rotationHolder = PropertyValuesHolder.ofFloat("Rotation", 60f, -60f, 40f, -40f, -20f, 20f, 10f, -10f, 0f);
PropertyValuesHolder colorHolder = PropertyValuesHolder.ofInt("BackgroundColor", 0xffffffff, 0xffff00ff, 0xffffff00, 0xffffffff);
ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(mTextView, rotationHolder, colorHolder);
animator.setDuration(3000);
animator.setInterpolator(new AccelerateInterpolator());
animator.start();

最后

我是i猩人,总结不易,转载注明出处,喜欢本篇文章的童鞋欢迎点赞、关注哦。

参考

  • https://developer.android.com/reference/android/animation/Animator
  • https://carsonho.blog.csdn.net/article/details/72909894
  • https://blog.csdn.net/harvic880925/article/details/50752838
  • https://juejin.cn/post/6844903798687678478#heading-34

天啦噜!原来Android属性动画也不过如此相关推荐

  1. android动画封装,Android属性动画封装,快速构建动画

    Android实现动画效果的方式主要有帧动画.补间动画.属性动画.关于安桌动画的基础知识可以查看这篇文章Android属性动画完全解析 这里我要讲的是如何快速构建出一个动画效果,如下图: 如果我们用属 ...

  2. Android属性动画 ObjectAnimator

    转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/118709616 本文出自[赵彦军的博客] 文章目录 ObjectAnimator ...

  3. (转)Android属性动画完全解析(中),ValueAnimator和ObjectAnimator的高级用法

    版权声明:本文出自郭霖的博客,转载必须注明出处. 目录(?)[-] ValueAnimator的高级用法 ObjectAnimator的高级用法 转载请注明出处:http://blog.csdn.ne ...

  4. android 属性动画实例,Android属性动画完全解析 中 ,ValueAnimator和ObjectAnimator的高级用法...

    大家好,在上一篇文章当中,我们学习了Android属性动画的基本用法,当然也是最常用的一些用法,这些用法足以覆盖我们平时大多情况下的动画需求了.但是,正如上篇文章当中所说到的,属性动画对补间动画进行了 ...

  5. Android 系统(196)---Android 属性动画

    Android 属性动画 属性动画 总结&攻略 前言 动画的使用 是 Android 开发中常用的知识 本文将详细介绍 Android 动画中 属性动画的原理 & 使用 动画类型 关于 ...

  6. Android 属性动画Property Animation(中)

    Android 属性动画Property Animation(上)介绍了属性动画的概念以及相关的类和接口,本篇来看下具体肿么使用. ValueAnimator ValueAnimator指定整形.浮点 ...

  7. Android 属性动画使用(二)

    首先扯点别的:晚上稍微跑了一会步,然后逛了超市,晚饭喝的南瓜粥,吃了一碗面条,今天不是太饿,现在正一边吃着葡萄一边学习,也是没谁了. 比如说,我们想要实现从0过渡到100,使用ValueAnimato ...

  8. android+属性动画+高度,android 自定义view+属性动画实现充电进度条

    近期项目中需要使用到一种类似手机电池充电进度的动画效果,以前没学属性动画的时候,是用图片+定时器的方式来完成的,最近一直在学习动画这一块,再加上复习一下自定义view的相关知识点,所以打算用属性动画和 ...

  9. Android 属性动画(一)新手入门

    一.属性动画简介 Android 中动画有很多种,属性动画就是其中的一种.所谓的属性动画,就是在指定的时间内,通过改变对象的属性达到变化效果的动画.在 Android 中,属性动画系统是一个强健的框架 ...

  10. Android 属性动画 详解

    Android 属性动画 详解 Android动画类型: View Animation(即所谓的Tween Animation补间动画):View Animation相当简单,不过只能支持简单的缩放. ...

最新文章

  1. 神策数据陈世键:融合媒体渠道转型破局策略
  2. vscode怎么引用css_今天来安装一个骚气的 VS Code 主题
  3. 使用继承思想,去开发一款组件(element-ui collapse组件为例子)
  4. media player 控件播放音乐与视频 0130 winform
  5. SpringMVC中获得HttpRequest对象的方法
  6. jQuery中兄弟元素、子元素和父元素的获取
  7. java 下一代_Java 下一代: 混入和特征
  8. SQlite数据库的C编程接口(六) 返回值和错误码(Result Codes and Error Codes) ——《Using SQlite》读书笔记
  9. CentOS安装并设置MariaDB
  10. 软件设计原则(一) 单一职责原则
  11. Android动态生成答题卡,手机扫描答题卡改卷的最佳选择——ZipGrade
  12. 视频音频剪辑合并软件 免费强大 LosslessCut
  13. ionic 中使用 slidebox 利用angular ng-repeat 渲染后不显示问题
  14. C#文件和文件文件夹排序
  15. Android日常整理(一)---android返回键、Fragment、android分割线、button图片间距的设置
  16. PPT学习整理(六)从入门到放弃。
  17. 嵌入式了解 以及学习路线
  18. 【Educational Codeforces Round 61 (Rated for Div. 2)】A.B.C.D.E.F.G
  19. Hexo之静态+动态背景设置
  20. 关于RISC-V成为印度国家指令集的一些看法

热门文章

  1. 阿里 P9 揭秘职场晋升:明明一样做好了本职工作,只有我一直不被提拔?
  2. 阿里 P9 用 500 多页手册完成双十一高并发秒杀系统,绝了
  3. 警方通报“济南1家6口死亡”案:男子杀害亲人后放火跳楼
  4. 同一个jar包不同版本冲突解决方法
  5. win10电脑亮度调节失灵(win10电脑亮度调节失灵戴尔)
  6. AMD显卡怎么提高帧数?AMD显卡提高fps的方法
  7. 线性代数学习笔记——第四十讲——n维向量空间的概念
  8. 集合20210801
  9. 大一新生HTML期末作业 个人网页王嘉尔明星介绍网页设计与制作
  10. 前端关于Base64编码的一些技术分析