热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

安卓开发之实现控件的贝塞尔曲线路径动画

贝塞尔曲线一般用在绘制canvas图层上,这里主要通过ObjectAnimation动画及估值器实现将贝塞尔曲线作为控件移动的动画路径。项目demo图:一、ObjectAnimat

贝塞尔曲线一般用在绘制canvas图层上,这里主要通过ObjectAnimation动画及估值器实现将贝塞尔曲线作为控件移动的动画路径。

项目demo图:

这里写图片描述

一、ObjectAnimator动画原理

  1. ObjectAnimator.ofObject(….,”xxx”,估值值,区间数组); 【定义动画属性xxx和区间】

  2. 插值器/加速器(Interpolator)【返回当前数字进度t】

  3. 估值值(Evaluator)【根当前数字进度计算并返回当前值】

  4. 调用setXxx函数 【根据封装好的setXxx函数并反射调用,将第三步返回当前值以参数传入】

二、自定义ObjectAnimator属性

上面我们理解了ObjectAnimator的动画实现原理,下面我们来自定义一个属性动画来看看实现贝塞尔路径效果吧。

一、保存贝塞尔路径的信息——ViewPoint

为了保存贝塞尔路径的一些关键点信息,我们定义一个类来保存:

public class ViewPoint{

float x ,y;

float x1,y1;

float x2,y2;

int operation;

public ViewPoint(float x, float y) {
this.x = x;
this.y = y;
}

// 设置起点
public static ViewPoint moveTo(float x, float y, int operation){
return new ViewPoint(x,y,operation);
}

// 直线移动
public static ViewPoint lineTo(float x, float y, int operation){
return new ViewPoint(x,y,operation);
}

// 三阶贝塞尔曲线移动
public static ViewPoint curveTo(float x, float y,float x1,float y1,float x2,float y2, int operation){
return new ViewPoint(x,y,x1,y1,x2,y2,operation);
}

// 二阶贝塞尔曲线移动
public static ViewPoint quadTo(float x, float y,float x1,float y1, int operation){
return new ViewPoint(x,y,x1,y1,operation);
}



private ViewPoint(float x, float y, int operation) {
this.x = x;
this.y = y;
this.operation = operation;
}

public ViewPoint(float x, float y, float x1, float y1, int operation) {
this.x = x;
this.y = y;
this.x1 = x1;
this.y1 = y1;
this.operation = operation;
}

public ViewPoint(float x, float y, float x1, float y1, float x2, float y2, int operation) {
this.x = x;
this.y = y;
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
this.operation = operation;
}

}

二、保存贝塞尔路径的集合——ViewPath

为了实现跟绘图时连续画线的效果,我们需要一个路径信息Path将路径的操作先后保存起来:

import java.util.ArrayList;
import java.util.Collection;

/**
* Created by cxm on 2016/9/8.
*/
public class ViewPath {

// 四种路径操作
public static final int MOVE = 0;
public static final int LINE = 1;
public static final int QUAD = 2;
public static final int CURVE = 3;

private ArrayList mPoints;


public ViewPath() {
mPoints = new ArrayList<>();
}

// 添加重置起点的路径
public void moveTo(float x, float y){
mPoints.add(ViewPoint.moveTo(x,y,MOVE));
}

// 添加直线移动的路径
public void lineTo(float x,float y){
mPoints.add(ViewPoint.lineTo(x,y,LINE));
}

// 添加三阶贝塞尔移动的路径
public void curveTo(float x,float y,float x1,float y1,float x2,float y2){
mPoints.add(ViewPoint.curveTo(x,y,x1,y1,x2,y2,CURVE));
}

// 添加二阶贝塞尔移动的路径
public void quadTo(float x,float y,float x1,float y1){
mPoints.add(ViewPoint.quadTo(x,y,x1,y1,QUAD));
}

// 获取路径集合
public Collection getPoints(){
return mPoints;
}


}

三、自定义ObjectAnimation动画:

    ViewPath path = new ViewPath(); //保存View的移动路径
path.moveTo(0,0);
path.lineTo(0,500);
path.curveTo(-300,200,-600,800,-800,400);
path.lineTo(-800,100);

// 自定义fabLoc动画。
// 第三个参数传入用于计算坐标的估值器,第四个参数传入用于估值的路径集合
ObjectAnimator anim = ObjectAnimator.ofObject(this,"fabLoc",new ViewPathEvaluator(),path.getPoints().toArray());
anim.setInterpolator(new AccelerateDecelerateInterpolator());

anim.setDuration(3000);
anim.start();


// 基于ObjectAnimation的实现原理定义:定义setFabLoc函数。参数为路径信息对象
public void setFabLoc(ViewPoint newLoc){
imageButton.setTranslationX(newLoc.x);
imageButton.setTranslationY(newLoc.y);
}

四、用于计算路径坐标的估值器ViewPathEvaluator

在自定义的估值器中,我们需要根据前后两个路径信息来计算应该返回的偏移坐标x,y。计算时我们需要用到贝塞尔曲线的路径公式。

公式可参考链接: 贝塞尔曲线介绍与公式

import android.animation.TypeEvaluator;

/**
* Created by cxm on 2016/9/8.
*/
public class ViewPathEvaluator implements TypeEvaluator {

// 自定义估值器:ViewPathEvaluator

public ViewPathEvaluator() {
}


@Override
public ViewPoint evaluate(float t, ViewPoint startValue, ViewPoint endValue) {

// 返回一个路径信息,其中包含偏移坐标x和偏移坐标y。
// 【返回用于反射调用setFabLoc时函数的传参】、

// startValue:前一个操作路径 endValue:后一个操作路径 t:操作进度(0->1)
// startValue和endValue为传入的路径集合数组中相邻的两个路径

float x ,y;

float startX,startY;

//分情况进行判断,计算出返回的偏移坐标:

if(endValue.operation == ViewPath.LINE){

//起点判断:
startX = (startValue.operation==ViewPath.QUAD)?startValue.x1:startValue.x;

startX = (startValue.operation == ViewPath.CURVE)?startValue.x2:startX;

startY = (startValue.operation==ViewPath.QUAD)?startValue.y1:startValue.y;

startY = (startValue.operation == ViewPath.CURVE)?startValue.y2:startY;

//返回的偏移坐标计算:根据公式
x = startX + t * (endValue.x - startX);
y = startY+ t * (endValue.y - startY);


}else if(endValue.operation == ViewPath.CURVE){

//起点判断:
startX = (startValue.operation==ViewPath.QUAD)?startValue.x1:startValue.x;
startY = (startValue.operation==ViewPath.QUAD)?startValue.y1:startValue.y;

float OneMinusT= 1 - t;

//返回的偏移坐标计算:根据公式
x = oneMinusT * oneMinusT * oneMinusT * startX +
3 * oneMinusT * oneMinusT * t * endValue.x +
3 * oneMinusT * t * t * endValue.x1+
t * t * t * endValue.x2;

y = oneMinusT * oneMinusT * oneMinusT * startY +
3 * oneMinusT * oneMinusT * t * endValue.y +
3 * oneMinusT * t * t * endValue.y1+
t * t * t * endValue.y2;


}else if(endValue.operation == ViewPath.MOVE){

x = endValue.x;
y = endValue.y;


}else if(endValue.operation == ViewPath.QUAD){

//起点判断:
startX = (startValue.operation==ViewPath.CURVE)?startValue.x2:startValue.x;
startY = (startValue.operation==ViewPath.CURVE)?startValue.y2:startValue.y;

//返回的偏移坐标计算:根据公式
float OneMinusT= 1 - t;
x = oneMinusT * oneMinusT * startX +
2 * oneMinusT * t * endValue.x +
t * t * endValue.x1;

y = oneMinusT * oneMinusT * startY +
2 * oneMinusT * t * endValue.y +
t * t * endValue.y1;


}else {
// 其他
x = endValue.x;
y = endValue.y;
}


return new ViewPoint(x,y);
}
}

返回偏移坐标x和y后,系统就会不断地反射调用setFabLoc函数让控件偏移:

// 基于ObjectAnimation的实现原理定义:定义setFabLoc函数。参数为路径信息对象
public void setFabLoc(ViewPoint newLoc){
imageButton.setTranslationX(newLoc.x);
imageButton.setTranslationY(newLoc.y);
}

后续:

  1. 因ObjectAnimation中的反射函数位置的原因,无法进行进一步的动画封装。

Github:Github


推荐阅读
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • 目录实现效果:实现环境实现方法一:基本思路主要代码JavaScript代码总结方法二主要代码总结方法三基本思路主要代码JavaScriptHTML总结实 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • Android JSON基础,音视频开发进阶指南目录
    Array里面的对象数据是有序的,json字符串最外层是方括号的,方括号:[]解析jsonArray代码try{json字符串最外层是 ... [详细]
  • 本文探讨了C语言中指针的应用与价值,指针在C语言中具有灵活性和可变性,通过指针可以操作系统内存和控制外部I/O端口。文章介绍了指针变量和指针的指向变量的含义和用法,以及判断变量数据类型和指向变量或成员变量的类型的方法。还讨论了指针访问数组元素和下标法数组元素的等价关系,以及指针作为函数参数可以改变主调函数变量的值的特点。此外,文章还提到了指针在动态存储分配、链表创建和相关操作中的应用,以及类成员指针与外部变量的区分方法。通过本文的阐述,读者可以更好地理解和应用C语言中的指针。 ... [详细]
  • 本文详细介绍了Spring的JdbcTemplate的使用方法,包括执行存储过程、存储函数的call()方法,执行任何SQL语句的execute()方法,单个更新和批量更新的update()和batchUpdate()方法,以及单查和列表查询的query()和queryForXXX()方法。提供了经过测试的API供使用。 ... [详细]
  • 个人学习使用:谨慎参考1Client类importcom.thoughtworks.gauge.Step;importcom.thoughtworks.gauge.T ... [详细]
  • 本文介绍了Redis的基础数据结构string的应用场景,并以面试的形式进行问答讲解,帮助读者更好地理解和应用Redis。同时,描述了一位面试者的心理状态和面试官的行为。 ... [详细]
  • 本文介绍了如何在给定的有序字符序列中插入新字符,并保持序列的有序性。通过示例代码演示了插入过程,以及插入后的字符序列。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • 计算机存储系统的层次结构及其优势
    本文介绍了计算机存储系统的层次结构,包括高速缓存、主存储器和辅助存储器三个层次。通过分层存储数据可以提高程序的执行效率。计算机存储系统的层次结构将各种不同存储容量、存取速度和价格的存储器有机组合成整体,形成可寻址存储空间比主存储器空间大得多的存储整体。由于辅助存储器容量大、价格低,使得整体存储系统的平均价格降低。同时,高速缓存的存取速度可以和CPU的工作速度相匹配,进一步提高程序执行效率。 ... [详细]
author-avatar
悶得咪_438
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有