从Android动画到贝塞尔曲线
基础知识:
动画通过连续播放一系列画面,给视觉造成连续变化的图画。很通俗的一种解释。也很好理解。那么我们先来一个案例看看。
动画案例:百度贴吧小熊奔跑
效果:

代码:
<?xml version="1.0" encoding="utf-8"?>
<animation-list
xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="false">
<item android:drawable="@drawable/gif_loading1" android:duration="50"></item>
<item android:drawable="@drawable/gif_loading2" android:duration="50"></item>
<item android:drawable="@drawable/gif_loading3" android:duration="50"></item>
<item android:drawable="@drawable/gif_loading4" android:duration="50"></item>
<item android:drawable="@drawable/gif_loading5" android:duration="50"></item>
<item android:drawable="@drawable/gif_loading6" android:duration="50"></item>
<item android:drawable="@drawable/gif_loading7" android:duration="50"></item>
</animation-list>
AnimationDrawable iv_topicAnimation;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_topic);
ImageView iv_topic = (ImageView) findViewById(R.id.iv_topic);
iv_topic.setBackgroundResource(R.drawable.anim_topic);
iv_topicAnimation = (AnimationDrawable) iv_topic.getBackground();
}
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
iv_topicAnimation.start();
}
public static void startActiivty(Context context) {
context.startActivity(new Intent(context, TopicActivity.class));
}
It's important to note that the start()
method called on the AnimationDrawable cannot be called during the onCreate()
method of your Activity, because the AnimationDrawable is not yet fully
attached to the window. If you want to play the animation immediately,
without requiring interaction, then you might want to call it from the onWindowFocusChanged() method in your Activity, which will get called when Android brings your window into focus.
可以看到,实现小熊奔跑的效果是非常简单的。你需要理解的是
1.小熊奔跑的效果是有一张张图片交替播放,而动起来的。
2.动画通过连续播放一系列画面,给视觉造成连续变化的图画。

好了,前面只是一个热身,下面才是真正的介绍Android动画了。
Android动画:
- 逐帧动画(frame-by-frame animation)
- 补间动画(tweened animation)
- 属性动画(property animation)
对于逐帧动画和补间动画这里就不打算具体深入,但是你必须要知道的
- 补间动画,只是改变View的显示效果而已,并不会真正的改变View的属性
- 补间动画,只作用于View上面
写了个demo来解释一下上面的意思:

bt_tween.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
TranslateAnimation animation = new TranslateAnimation(0, 0, 0, 300);
animation.setDuration(1000);
animation.setFillAfter(true);
tv_tween_hello.startAnimation(animation);
}
});
tv_tween_hello.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(context,"hello animation",Toast.LENGTH_SHORT).show();
}
});
可以看到其实hello Animation平移之后点击没用了,而点击之前的位置的时候,还是有效的。这也正是我刚才说的
补间动画:只是改变View的显示效果而已,并不会真正的改变View的属性
看到这里,你也知道如果要用补间动画来做一些交互的动画是很蛋疼的。一般做法是:
- 预先在动画结束的地方设置一个不可见的View,然后动画结束之后,让其显示。这样就可以处理事件。
- 利用https://github.com/JakeWharton/NineOldAndroids
属性动画
在介绍属性动画前,我们还是先来看一个demo。

protected Person person;
protected TextView tv_property_info;
protected Button bt_property;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_property);
person = new Person();
person.setName("张三");
bt_property = (Button) findViewById(R.id.bt_property);
tv_property_info = (TextView) findViewById(R.id.tv_property_info);
bt_property.setOnClickListener(this);
}
@Override
public void onClick(View v) {
ObjectAnimator objectAnimator = ObjectAnimator.ofInt(person, "age", 1, 100);
objectAnimator.addUpdateListener(this);
objectAnimator.setDuration(5000);
objectAnimator.start();
}
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int values = (int) animation.getAnimatedValue();
person.setAge(values);
tv_property_info.setText(person.toString());
}
上面代码模拟了一个Person对象,从0-100岁之间的变化,可能你有点看不懂。我这是在干嘛,但是如果你还记的刚才我说的。属性动画,可以对任何对象属性进行修改,而补间动画,只作用于View上面。我们的demo就是对Person这个对象中属性age不断进行修改。现在,我们假设一个TextView要进行平移或则缩放,或则旋转。只要将对应的属性进行修改,然后重绘。不就可以达到动画的效果了吗?试试吧!

protected TextView tv_property_info;
protected Button bt_property;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_property);
bt_property = (Button) findViewById(R.id.bt_property);
bt_property.setText("View的改变");
tv_property_info = (TextView) findViewById(R.id.tv_property_info);
bt_property.setOnClickListener(this);
}
@Override
public void onClick(View v) {
ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(tv_property_info, "TranslationY", 0, 300);
objectAnimator.addUpdateListener(this);
objectAnimator.setDuration(500);
objectAnimator.start();
}
我们来总结一下.当我们调用
ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(tv_property_info, "TranslationY", 0, 300);
objectAnimator.setDuration(500);
的时候,将初始值和结束值给ObjectAnimator,并且告诉它所需的时长,然后它的内部使用一种时间循环的机制来计算值与值之间的过渡。然后将变化的值返回给我们。然后我们获取这个间隙的值,重绘界面就给视觉造成连续变化的图画。那么内部是怎么计算这个值的呢?
TypeEvaluator
/**
* This evaluator can be used to perform type interpolation between <code>float</code> values.
*/
public class FloatEvaluator implements TypeEvaluator<Number> {
/**
* This function returns the result of linearly interpolating the start and end values, with
* <code>fraction</code> representing the proportion between the start and end values. The
* calculation is a simple parametric calculation: <code>result = x0 + t * (v1 - v0)</code>,
* where <code>x0</code> is <code>startValue</code>, <code>x1</code> is <code>endValue</code>,
* and <code>t</code> is <code>fraction</code>.
*
* @param fraction The fraction from the starting to the ending values
* @param startValue The start value; should be of type <code>float</code> or
* <code>Float</code>
* @param endValue The end value; should be of type <code>float</code> or <code>Float</code>
* @return A linear interpolation between the start and end values, given the
* <code>fraction</code> parameter.
*/
public Float evaluate(float fraction, Number startValue, Number endValue) {
float startFloat = startValue.floatValue();
return startFloat + fraction * (endValue.floatValue() - startFloat);
}
}
通过查看源码我们知道,系统内置了一个FloatEvaluator,它通过计算告知动画系统如何从初始值过度到结束值。如果,我们想要自己控制这个时间段的运动轨迹的话,就可以自定义TypeEvaluator来返回轨迹点。下面让我们自己定义一个TypeEvaluator吧!
动画案例:饿了么购物车。
先看效果图片,这样有利于思考.

想要实现这个效果,我们必须先定义一个类。来记录小球的x和y的变量。
public class Point implements Parcelable {
public int x;
public int y;
public Point() {}
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public Point(Point src) {
this.x = src.x;
this.y = src.y;
}
/**
* Set the point's x and y coordinates
*/
public void set(int x, int y) {
this.x = x;
this.y = y;
}
}
这个类是android自带的一个类。用来记录小球的运动轨迹点,那么现在我们只需要小球的起始点和终点,然后就可以计算出小球的运动轨迹。那么怎么获取开始点和终点呢?终点的话,是一个定值。而开始点是我们根据点击事件获取的。可以根据
int position[] = new int[2];
view.getLocationInWindow(position);
来获取x=position[0];y=position[1]; 现在起始点和终点都已经确定好了,那么我们来计算一下小球运动的轨迹吧。从动画中可以看出来。类似于抛物线。这里我想到了用贝塞尔曲线来做。因为我们只需要确定一个控制点,就可以根据公式算出小球的运动轨迹。

二次方公式
二次方贝兹曲线的路径由给定点P0、P1、P2的函数B(t)追踪:![]()
TrueType字型就运用了以贝兹样条组成的二次贝兹曲线。
有了计算公式,现在还缺一个控制点。那么这个点,该怎么确定呢?
int pointX = (startPosition.x + endPosition.x) / 2;
int pointY = (int) (startPosition.y - convertDpToPixel(100, mContext));
Point controllPoint = new Point(pointX, pointY);
我这里确定控制点是,取起始点和终点X的中点,y取开始点网上偏移一个距离。这样就会有一个抛物线的角度了。当然你可以通过http://myst729.github.io/bezier-curve/ 微调下你的控制点。好了,现在我们3个点都已经确定好了,现在就需要计算小球的运动轨迹,然后绘制到Window上去就OK了。
public class BezierEvaluator implements TypeEvaluator<Point> {
private Point controllPoint;
public BezierEvaluator(Point controllPoint) {
this.controllPoint = controllPoint;
}
@Override
public Point evaluate(float t, Point startValue, Point endValue) {
int x = (int) ((1 - t) * (1 - t) * startValue.x + 2 * t * (1 - t) * controllPoint.x + t * t * endValue.x);
int y = (int) ((1 - t) * (1 - t) * startValue.y + 2 * t * (1 - t) * controllPoint.y + t * t * endValue.y);
return new Point(x, y);
}
}
上面就是取当小球在时间t的时候,x和y分别是多少,然后去改变View。
@Override
public void onAnimationUpdate(ValueAnimator animation) {
Point point = (Point) animation.getAnimatedValue();
setX(point.x);
setY(point.y);
invalidate();
}
记得在动画播放完成后记得移除掉这个小球.
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
ViewGroup viewGroup = (ViewGroup) getParent();
viewGroup.removeView(NXHooldeView.this);
}
});
那么现在如果叫你实现这种效果,你有思路吗?
也许放弃 才能靠近你 不再见你**
你才会把我记起 时间累积
这盛夏的果实 回忆里寂寞的香气
我要试着离开你 不要再想你
虽然这并不是我本意
你曾说过 会永远爱我
也许承诺 不过因为没把握 别用沉默
再去掩饰甚么 当结果是那么赤裸裸
以为你会说甚么 才会离开我
附上本有所有代码的链接:https://github.com/BelongsH/AnimationExample/tree/master
从Android动画到贝塞尔曲线的更多相关文章
- CSS总结六:动画(一)ransition:过渡、animation:动画、贝塞尔曲线、step值的应用
transition-property transition-duration transition-timing-function transition-delay animation-name a ...
- Android自定义View——贝塞尔曲线实现水波纹效果
我们使用到的是Path类的quadTo(x1, y1, x2, y2)方法,属于二阶贝塞尔曲线,使用一张图来展示二阶贝塞尔曲线,这里的(x1,y1)是控制点,(x2,y2)是终止点,起始点默认是Pat ...
- Android -- 贝塞尔曲线公式的推导
1,最近看了几个不错的自定义view,发现里面都会涉及到贝塞尔曲线知识,深刻的了解到贝塞尔曲线是进阶自定义view的一座大山,so,今天先和大家来了解了解. 2,贝塞尔曲线作用十分广泛,简单举几个的栗 ...
- Android -- 贝塞尔曲线公式的推导和简单使用
1,最近看了几个不错的自定义view,发现里面都会涉及到贝塞尔曲线知识,深刻的了解到贝塞尔曲线是进阶自定义view的一座大山,so,今天先和大家来了解了解. 2,贝塞尔曲线作用十分广泛,简单举几个的栗 ...
- Android中贝塞尔曲线的绘制方法
贝塞尔曲线,很多人可能不太了解,什么叫做贝塞尔曲线呢?这里先做一下简单介绍:贝塞尔曲线也可以叫做贝济埃曲线或者贝兹曲线,它由线段与节点组成,节点是可拖动的支点,线段像可伸缩的皮筋.一般的矢量图形软件常 ...
- 贝塞尔曲线与CSS3动画、SVG和canvas的应用
简介 贝塞尔曲线是可以做出很多复杂的效果来的,比如弹跳球的复杂动画效果,首先加速下降,停止,然后弹起时逐渐减速的效果. 使用贝塞尔曲线常用的两个网址如下: 缓动函数:http://www.xuanfe ...
- 贝塞尔曲线(cubic bezier)
对于css3的Transitions,网上很多介绍,相信大家都比较了解,这里用最简单的方式介绍下: transition语法:transition:<transition-property> ...
- OpenGL 实践之贝塞尔曲线绘制
说到贝塞尔曲线,大家肯定都不陌生,网上有很多关于介绍和理解贝塞尔曲线的优秀文章和动态图. 以下两个是比较经典的动图了. 二阶贝塞尔曲线: 三阶贝塞尔曲线: 由于在工作中经常要和贝塞尔曲线打交道,所以简 ...
- Android 利用二次贝塞尔曲线模仿购物车加入物品抛物线动画
Android 利用二次贝塞尔曲线模仿购物车加入物品抛物线动画 0.首先.先给出一张效果gif图. 1.贝塞尔曲线原理及相关公式參考:http://www.jianshu.com/p/c0d7ad79 ...
随机推荐
- Careercup - Microsoft面试题 - 5188169901277184
2014-05-12 06:12 题目链接 原题: Write a function to retrieve the number of a occurrences of a substring(ev ...
- leetcode 【 Remove Duplicates from Sorted Array II 】python 实现
题目: Follow up for "Remove Duplicates":What if duplicates are allowed at most twice? For ex ...
- 使用 Bullet,BulletManager 在 XNA 中创建子弹攻击目标(十五)
平方已经开发了一些 Windows Phone 上的一些游戏,算不上什么技术大牛.在这里分享一下经验,仅为了和各位朋友交流经验.平方会逐步将自己编写的类上传到托管项目中,没有什么好名字,就叫 WPXN ...
- Python 拓展之推导式
写在之前 推导式是从一个或多个迭代器快速简洁的创建数据结构的一种办法,它可以将循环和条件判断结合,从而可以避免语法冗长的代码. 列表推导式 我在之前的文章中(零基础学习 Python 之 for 循环 ...
- 二分法 Binary Search
二分法还是比较常见和简单的,之前也遇到过一些二分的相关题目,虽然不难,但是每次都需要在边界问题上诸多考虑,今天听了九章算法的课程,学习到一种方法使得边界问题简单化. 二分法的几个注意点: 1. mid ...
- [python][django学习篇][15]博客侧栏--自定义模板标签
我们的博客侧边栏有四项内容:最新文章.归档.分类和标签云. 这些内容相对比较固定,且在各个页面都会显示,如果像文章列表或者文章详情一样,从视图函数中获取然后传递给模板,则每个页面对应的视图函数里都要写 ...
- python3.x与python2.x的区别(转)
转自:http://www.cnblogs.com/codingmylife/archive/2010/06/06/1752807.html 1.性能 Py3.0运行 pystone benchmar ...
- xshell 连接redis
当我们安装好redis 在本地测试启动,存,取都正常,但是无法远程连接redis. 在redis.conf的文件默认配置中有一配置是 bind 127.0.0.1,表示绑定访问该redis的地址只能是 ...
- linux常用命令(复制)
显示目录和文件的命令 Ls:用于查看所有文件夹的命令. Dir:用于显示指定文件夹和目录的命令 Tree: 以树状图列出目录内容 Du:显示目录或文件大小 修改目录,文件权限和属主及数组命令 ...
- BZOJ 1787: [Ahoi2008]Meet 紧急集合(lca+贪心)
[Ahoi2008]Meet 紧急集合 Description Input Output Sample Input 6 4 1 2 2 3 2 4 4 5 5 6 4 5 6 6 3 1 2 4 4 ...