android抛物线动画实现

最近面试,面试官说了一个场景,大概类似于类似于两个点比如AB吧,从A到B这个运动轨迹要以抛物线的形式运动,说下实现的思路,因为我做过新手引导,通过获取到两个点的绝对坐标,这样通过动画来说,面试官说了,动画的话是一条直线,我当时立刻想到了贝塞尔曲线,我说用贝塞尔曲线来实现,他不说话了,貌似这个面试官是Java的,对安卓不是很了解,我觉得我面试的基本都回答的很不错,竟然后续也没通知了,额,心累啊,最近行情确实不好啊,可能缘分未到吧,那咱们今天就来实现下这个动画,既然面试官说了,Animation动画实现的是一条直线,那就来验证下是否是条直线.

先来看下效果图,基本上应该是这样的效果吧,那两条线是贝瑟尔曲线,不看那个线,看点击A时候的动画


一 分析场景

1.可以看到,面试官说的那种效果应该大概是这样的,但是他说Animation实现的是条直线,其实是不完全对的,如果单一的动画肯定是条线性的直线,但是通过组合动画可以模拟实现这个类抛物线的效果,思路就是让A到B水平X轴方向线性移动的同时,让其同时在Y轴方向有线性或加速移动;当然,贝塞尔曲线肯定也是可以的,咱先以这种的方式实现下

2.A和B(购物车)的都是TextView,两者的坐标很容易可以算出来,通过系统的getLocationInWindow,

  /**
 * <p>Computes the coordinates of this view in its window. The argument
 * must be an array of two integers. After the method returns, the array
 * contains the x and y location in that order.</p>
 *
 * @param outLocation an array of two integers in which to hold the coordinates
 */
public void getLocationInWindow(@Size(2) int[] outLocation) {
    if (outLocation == null || outLocation.length < 2) {
        throw new IllegalArgumentException("outLocation must be an array of two integers");
    }


    outLocation[0] = 0;
    outLocation[1] = 0;


    transformFromViewToWindowSpace(outLocation);

可以看下对这个方法的解释,返回的是2个长度的int类型的数组,ok,定义两个int类型的长度为2的数组

int [] firstPoint = new int[2];
int [] endPoint = new int[2];
简单的封装下获取点的坐标的方法

private int[] getLocationXY(View view) {
    final int[] xy = new int[2];
    view.getLocationInWindow(xy);
    return xy;
}
组合动画,主要的是要分析这个,动画我们是给控件设定,之前咱们已经得到了这个两点的坐标,则开始点和结束点坐标X 轴的差值,Y轴的差值可以算出来,咱们只考虑差值即可

private Animation createAnimation(int startX, int startY) {
    //组合动画集合
    AnimationSet set = new AnimationSet(false);
    //开始点和结束点坐标X 轴的差值
    Animation translationX = new TranslateAnimation(0, endPoint[0] - startX, 0, 0);
    //线性插值器 默认就是线性
    translationX.setInterpolator(new LinearInterpolator());
    //开始点和结束点坐标Y 轴的差值
    Animation translationY = new TranslateAnimation(0, 0, 0, endPoint[1] - startY);
    //设置加速插值器
    translationY.setInterpolator(new AccelerateInterpolator());
    //还可以设置一些其他的动画
    // Animation alpha = new AlphaAnimation(1,0.4f);
    set.addAnimation(translationX);
    set.addAnimation(translationY);
    //  set.addAnimation(alpha);
    set.setDuration(500);
    return set;
}
则启动动画的方法如下:

@OnClick(R.id.animation_tv)
void startAnim() {
    //A点在屏幕上的坐标
    animationTv.getLocationInWindow(firstPoint);
    //购物车在屏幕上的坐标
    bottomMoneyCarTv.getLocationInWindow(endPoint);
    Animation animation = createAnimation(firstPoint[0], firstPoint[1]);
    animation.setAnimationListener(new Animation.AnimationListener() {
        @Override
        public void onAnimationStart(Animation animation) {

        }

        @Override
        public void onAnimationEnd(Animation animation) {
             TODO: 2016/12/19  more action

        }

        @Override
        public void onAnimationRepeat(Animation animation) {

        }
    });
    animationTv.startAnimation(animation);
}

上面的是以组合动画的方式实现了这种类抛物线的动画.


二 使用贝塞尔曲线实现动画

  1.贝塞尔曲线的公式就不列举了,看下一摘取网络上的图片

   

可以看到使用贝塞尔的时候,需要算出这个t的实时点,这个就是动画的轨迹

看下贝塞尔实现的效果图,我觉得和组合动画差不多


同样两个定点P0和P2很容易,即firstPoint和endPoint,下面是具体实现,其实也是模范代码了,注释已经很清楚了,不会的再去百度谷歌吧

//A点在屏幕上的坐标
animationTv.getLocationInWindow(firstPoint);
// 得到父布局的起始点坐标(用于辅助计算动画开始/结束时的点的坐标,这个很重要,因为二次贝塞尔曲线就是3个定点,这个点相当于是给弧线定一个范围)
int[] parentLocation = new int[2];
positionView.getLocationInWindow(parentLocation);
//购物车在屏幕上的坐标
bottomMoneyCarTv.getLocationInWindow(endPoint);

float startX = firstPoint[0] - parentLocation[0] + animationTv.getWidth() / 2;
float startY = firstPoint[1] - parentLocation[1] + animationTv.getHeight() / 2;

float toX = endPoint[0] - parentLocation[0] + bottomMoneyCarTv.getWidth() / 2;
float toY = endPoint[1] - parentLocation[1];
// 开始绘制贝塞尔曲线
Path path = new Path();
// 移动到起始点(贝塞尔曲线的起点)
path.moveTo(startX, 0);
// 使用二阶贝塞尔曲线:注意第一个起始坐标越大,贝塞尔曲线的横向距离就会越大,一般按照下面的式子取即可
path.quadTo((startX + toX) / 2, startY, toX, toY);
// mPathMeasure用来计算贝塞尔曲线的曲线长度和贝塞尔曲线中间插值的坐标,如果是true,path会形成一个闭环
mPathMeasure = new PathMeasure(path, false);
// 属性动画实现(从0到贝塞尔曲线的长度之间进行插值计算,获取中间过程的距离值)
ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, mPathMeasure.getLength());
valueAnimator.setDuration(1000);
valueAnimator.setRepeatCount(0);

// 匀速线性插值器
valueAnimator.setInterpolator(new LinearInterpolator());
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        // 当插值计算进行时,获取中间的每个值,
        // 这里这个值是中间过程中的曲线长度(下面根据这个值来得出中间点的坐标值)
        float value = (Float) animation.getAnimatedValue();
        // 获取当前点坐标封装到mCurrentPosition
        // boolean getPosTan(float distance, float[] pos, float[] tan) :
        // 传入一个距离distance(0<=distance<=getLength()),然后会计算当前距离的坐标点和切线,pos会自动填充上坐标,这个方法很重要。
        // mCurrentPosition此时就是中间距离点的坐标值
        mPathMeasure.getPosTan(value, mCurrentPosition, null);
        // 移动的TextView的坐标设置为该中间点的坐标
        animationTv.setTranslationX(mCurrentPosition[0]);
        animationTv.setTranslationY(mCurrentPosition[1]);

    }
});

// 开始执行动画
valueAnimator.start();

// 动画结束后的处理
valueAnimator.addListener(new Animator.AnimatorListener() {
    @Override
    public void onAnimationStart(Animator animation) {
    }

    @Override
    public void onAnimationEnd(Animator animation) {
         TODO: 2016/12/19  
    }

    @Override
    public void onAnimationCancel(Animator animation) {
    }

    @Override
    public void onAnimationRepeat(Animator animation) {
    }
});

OK,这个就是面试给我的灵感吧,虽说不难,代码这东西,也是贵在实践加总结,找出适合自己的套路,解决问题就行!


  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值