Android 利用三阶贝塞尔曲线绘制运动轨迹的示例

本篇文章主要介绍了Android 利用三阶贝塞尔曲线绘制运动轨迹的示例,分享给大家,具体如下:

实现点赞效果,自定义起始点以及运动轨迹

效果图:

xml布局:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:tools="http://schemas.android.com/tools"
  android:id="@+id/rl_root"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  tools:context="stone.wshh.com.touch.MainActivity">
  <stone.wshh.com.touch.MyLoveLayout
    android:layout_marginBottom="100dp"
    android:layout_marginRight="15dp"
    android:id="@+id/love_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
  </stone.wshh.com.touch.MyLoveLayout>
  <Button
    android:id="@+id/bt_bottom"
    android:text="begin"
    android:layout_alignParentBottom="true"
    android:layout_centerHorizontal="true"
    android:layout_marginBottom="20dp"
    android:layout_width="100dp"
    android:layout_height="50dp" />
</RelativeLayout>

MainActivity类:

public class MainActivity extends Activity implements View.OnClickListener{
  private Button btBottom;
//  private WaitNoticeDialog dialog;
//  public Handler handler;
  private MyLoveLayout love;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    btBottom = (Button) findViewById(R.id.bt_bottom);
    love = (MyLoveLayout) findViewById(R.id.love_layout);
    btBottom.setOnClickListener(this);
//    handler=new IHandler(this);
//    dialog = new WaitNoticeDialog(this);
  }

  static class IHandler extends Handler {
    private WeakReference<MainActivity> ui;
    IHandler(MainActivity ui) {
      this.ui = new WeakReference<MainActivity>(ui);
    }
    @Override
    public void handleMessage(Message msg) {
      if(ui!=null&&ui.get()!=null){
        ui.get().handleMsg(msg);
      }
    }
  }

  /**
   * 线程消息处理
   * @param msg
   */
  public void handleMsg(Message msg){
    switch (msg.what) {
    }
  }

  @Override
  public void onClick(View v) {
    switch (v.getId()){
      case R.id.bt_bottom:
        love.addHeart();
        break;
    }
  }

  @Override
  protected void onDestroy() {
    super.onDestroy();
//    handler.removeCallbacksAndMessages(null);
  }
}

自定义view:MyLoveLayout

public class MyLoveLayout extends RelativeLayout {
  private Drawable[] drawables;
  private Interpolator[] mInterpolators;
  private int dWidth, mWidth;
  private int dHeight, mHeight;
  private LayoutParams lp;
  private Random random = new Random();

  public MyLoveLayout(Context context, AttributeSet attrs) {
    super(context, attrs);
    //imageView位置是相对于MyLoveLayout
    init();
  }

  @Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    //得到本布局的宽高
    mWidth = getMeasuredWidth();
    mHeight = getMeasuredHeight();
  }

  private void init() {

    // 初始化显示的图片
    drawables = new Drawable[7];
    drawables[0] = getResources().getDrawable(R.drawable.heart_1);
    drawables[1] = getResources().getDrawable(R.drawable.heart_2);
    drawables[2] = getResources().getDrawable(R.drawable.heart_3);
    drawables[3] = getResources().getDrawable(R.drawable.heart_4);
    drawables[4] = getResources().getDrawable(R.drawable.heart_5);
    drawables[5] = getResources().getDrawable(R.drawable.heart_6);
    drawables[6] = getResources().getDrawable(R.drawable.heart_7);

    // 初始化插补器
    mInterpolators = new Interpolator[4];
    mInterpolators[0] = new LinearInterpolator();// 线性
    mInterpolators[1] = new AccelerateInterpolator();// 加速
    mInterpolators[2] = new DecelerateInterpolator();// 减速
    mInterpolators[3] = new AccelerateDecelerateInterpolator();// 先加速后减速

    // 获取图片宽高
//    dWidth = drawables[0].getIntrinsicWidth();
//    dHeight = drawables[0].getIntrinsicHeight();
    //手动设置宽高
    dWidth = dip2px(getContext(), 40);
    dHeight = dip2px(getContext(), 40);
    lp = new LayoutParams(dWidth, dHeight);
    //设置view控件的起始位置
//    lp.addRule(CENTER_HORIZONTAL, TRUE);// 这里的TRUE 要注意 不是true
    lp.addRule(ALIGN_PARENT_RIGHT, TRUE);
    lp.addRule(ALIGN_PARENT_BOTTOM, TRUE);

  }

  /**
   * dp转px值
   */
  private int dip2px(Context context, float dpValue) {
    float scale = context.getResources().getDisplayMetrics().density;
    return (int) (dpValue * scale + 0.5f);
  }

  /**
   * 进场动画,三种同时播放
   * alpha透明度 (80%-0%)
   * scaleX 宽度 target(20%-100%)
   * scaleY 高度
   * @param target
   * @return
   */
  private AnimatorSet getEnterAnimator(final View target) {
    ObjectAnimator alpha = ObjectAnimator.ofFloat(target, View.ALPHA, 0.2f, 1f);
    ObjectAnimator scaleX = ObjectAnimator.ofFloat(target, View.SCALE_X, 0.2f, 1f);
    ObjectAnimator scaleY = ObjectAnimator.ofFloat(target, View.SCALE_Y, 0.2f, 1f);
    AnimatorSet enter = new AnimatorSet();
    enter.setTarget(target);
    enter.setInterpolator(new LinearInterpolator());
    enter.setDuration(500).playTogether(alpha, scaleX, scaleY);
    return enter;
  }

  private ValueAnimator getBezierValueAnimator(final View target) {
    // 初始化贝塞尔估值器
    //随机产生两个点,以确定一条3阶贝塞尔曲线
    BezierEvaluator evaluator = new BezierEvaluator(getPointF(2), getPointF(1));
    // 起点在底部中心位置,终点在底部随机一个位置,改变new PointF()中值来改变起始位置
//    ValueAnimator animator = ValueAnimator.ofObject(evaluator, new PointF((mWidth - dWidth) /
//        2, mHeight - dHeight), new PointF(random.nextInt(getWidth()), 0));
    // 起点在右下角位置,终点在左上角位置
    ValueAnimator animator = ValueAnimator.ofObject(evaluator, new PointF(mWidth - dWidth, mHeight - dHeight), new PointF(0, 0));
    animator.setTarget(target);
    animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
      @Override
      public void onAnimationUpdate(ValueAnimator valueAnimator) {
        // 这里获取到贝塞尔曲线计算出来的的x y值 赋值给view 这样就能让爱心随着曲线走啦
        PointF pointF = (PointF) valueAnimator.getAnimatedValue();
        target.setX(pointF.x);
        target.setY(pointF.y);
        // alpha动画,根据运动距离改变透明度
//        target.setAlpha(1 - valueAnimator.getAnimatedFraction());
        target.setAlpha(1 - valueAnimator.getAnimatedFraction() + 0.3f);
      }
    });

    animator.setDuration(3000);
    return animator;
  }

  private PointF getPointF(int i) {
    PointF pointF = new PointF();
    //pointF.x,pointF.y都是随机,因此可以产生n多种轨迹
    pointF.x = random.nextInt(mWidth);//0~loveLayout.Width

    //为了美观,建议尽量保证P2在P1上面,那怎么做呢??
    //只需要将该布局的高度分为上下两部分,让p1只能在下面部分范围内变化(1/2height~height),让p2只能在上面部分范围内变化(0~1/2height),因为坐标系是倒着的;

    //0~loveLayout.Height/2
    if (i == 1) {
      pointF.y = random.nextInt(mHeight / 2) + mHeight / 2;//P1点Y轴坐标变化
    } else if (i == 2) {//P2点Y轴坐标变化
      pointF.y = random.nextInt(mHeight / 2);
    }
//    写死的一条轨迹
//    if (i == 1) {
//      pointF.x=mWidth-dWidth*2;
//      pointF.y = 3*dHeight;
//    } else if (i == 2) {
//      pointF.x=dWidth*2;
//      pointF.y = mHeight -dHeight;
//    }

    return pointF;
  }

  public void addHeart() {

    final ImageView imageView = new ImageView(getContext());
    // 随机选一个爱心
    imageView.setImageDrawable(drawables[random.nextInt(6)]);
    imageView.setLayoutParams(lp);
    addView(imageView);

    AnimatorSet finalSet = new AnimatorSet();

    AnimatorSet enterAnimatorSet = getEnterAnimator(imageView);//入场动画
    ValueAnimator bezierValueAnimator = getBezierValueAnimator(imageView);//贝塞尔曲线路径动画

    finalSet.playSequentially(enterAnimatorSet, bezierValueAnimator);
//    finalSet.playSequentially(bezierValueAnimator);
    finalSet.setInterpolator(mInterpolators[random.nextInt(4)]);
    finalSet.setTarget(imageView);

    finalSet.addListener(new AnimatorListenerAdapter() {
      @Override
      public void onAnimationEnd(Animator animation) {
        super.onAnimationEnd(animation);
        removeView((imageView));//删除爱心
      }
    });
    finalSet.start();

  }
}

贝塞尔估值器:BezierEvaluator

public class BezierEvaluator implements TypeEvaluator<PointF> {
  private PointF mControlP1;
  private PointF mControlP2;
  public BezierEvaluator(PointF controlP1, PointF controlP2) {
    this.mControlP1 = controlP1;
    this.mControlP2 = controlP2;
  }

  @Override
  public PointF evaluate(float time, PointF start, PointF end) {

    float timeLeft = 1.0f - time;
    PointF point = new PointF();

    point.x = timeLeft * timeLeft * timeLeft * (start.x) + 3 * timeLeft * timeLeft * time *
        (mControlP1.x) + 3 * timeLeft * time *
        time * (mControlP2.x) + time * time * time * (end.x);

    point.y = timeLeft * timeLeft * timeLeft * (start.y) + 3 * timeLeft * timeLeft * time *
        (mControlP1.y) + 3 * timeLeft * time *
        time * (mControlP2.y) + time * time * time * (end.y);
    return point;
  }
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

您可能感兴趣的文章:

  • Android贝塞尔曲线初步学习第三课 Android实现添加至购物车的运动轨迹
(0)

相关推荐

  • Android贝塞尔曲线初步学习第三课 Android实现添加至购物车的运动轨迹

    不知上一节高仿QQ未读消息气泡大家还喜欢么,今天继续练习贝赛尔曲线,这一节我们通过贝赛尔曲线和属性动画估值器实现添加至购物车的运动轨迹,效果如下: 1.新建自定义View,重写构造方法,初始化Paint.Path: 2.确定起始点.终止点.控制点坐标,这里我们直接固定: @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh);

  • Android 利用三阶贝塞尔曲线绘制运动轨迹的示例

    本篇文章主要介绍了Android 利用三阶贝塞尔曲线绘制运动轨迹的示例,分享给大家,具体如下: 实现点赞效果,自定义起始点以及运动轨迹 效果图: xml布局: <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/rl_root&

  • Android利用贝塞尔曲线绘制动画的示例代码

    目录 彩虹系列 弹簧动画 复杂立体感动画 总结 前面我们花了几篇介绍了贝塞尔曲线的原理和绘制贝塞尔曲线,着实让我们见识到了贝塞尔曲线的美.好奇心驱使我想看看贝塞尔曲线动起来会是什么样?本篇就借由动画驱动贝塞尔曲线绘制看看动起来的贝塞尔曲线什么效果. 彩虹系列 通过动画控制绘制的结束点,就可以让贝塞尔曲线动起来.例如下面的动图展示的效果,看起来像搭了一个滑滑梯一样.实际上就是用7条贝塞尔曲线实现的,我们使用了 Animation 对象的值来控制绘制的结束点,从而实现了对应的动画效果. 具体源码如下

  • Android利用二阶贝塞尔曲线实现添加购物车动画详解

    一.引入 其实之前一直以为像饿了么或者是美团外卖那种把商品添加到购物车的动画会很难做,但是实际做起来好像并没有想象中的那么难哈哈. 布局主要使用CoordinatorLayout+AppBarLayout+CollapsingToolbarLayout+TabLayout+ViewPager 动画主要使用二阶贝塞尔曲线与属性动画 消息传递使用EventBus普通事件 二.大致思路 1.如图所示主要有三个点,起点.终点.以及贝塞尔曲线的控制点 2.起点即点击的View的位置,一般来说用如下方式即可

  • Android 贝塞尔曲线绘制一个波浪球

    目录 前言 一.绘制 backgroundColor 文本 二.构建 circlePath 三.绘制波浪线 四.取交集 五.绘制 foregroundColor 文本 六.添加动画 七.使用 前言 当 flutter 的现有组件无法满足产品要求的 UI 效果时,我们就需要通过自绘组件的方式来进行实现了.本篇文章就来介绍如何用 flutter 自定义实现一个带文本的波浪球,效果如下所示: 先来总结下 WaveLoadingWidget 的特点,这样才能归纳出实现该效果所需要的步骤: widget

  • Android自定义view贝塞尔曲线

    本文实例为大家分享了Android自定义view贝塞尔曲线,供大家参考,具体内容如下 贝塞尔曲线 以一个简单的贝塞尔曲线为例,二阶曲线原理 贝塞尔曲线很多功能都会用到,比如小火箭发射,再比如淘宝的购物车功能 所幸的是Android有封装好的贝塞尔曲线,我们直接拿过来用就可以了: //二阶贝赛尔  public void quadTo(float x1, float y1, float x2, float y2)  public void rQuadTo(float dx1, float dy1,

  • python基于三阶贝塞尔曲线的数据平滑算法

    前言 很多文章在谈及曲线平滑的时候,习惯使用拟合的概念,我认为这是不恰当的.平滑后的曲线,一定经过原始的数据点,而拟合曲线,则不一定要经过原始数据点. 一般而言,需要平滑的数据分为两种:时间序列的单值数据.时间序列的二维数据.对于前者,并非一定要用贝塞尔算法,仅用样条插值就可以轻松实现平滑:而对于后者,不管是 numpy 还是 scipy 提供的那些插值算法,就都不适用了. 本文基于三阶贝塞尔曲线,实现了时间序列的单值数据和时间序列的二维数据的平滑算法,可满足大多数的平滑需求. 贝塞尔曲线 关于

  • Android利用ViewDragHelper轻松实现拼图游戏的示例

    前言 最近一段时间看了一些介绍ViewDragHelper的博客,感觉这是一个处理手势滑动的神奇,看完以后就想做点东西练练手,于是就做了这个Android拼图小游戏. 先上个效果图 源码 https://github.com/kevin-mob/Puzzle ViewDragHelper 其实ViewDragHelper并不是第一个用于分析手势处理的类,gesturedetector也是,但是在和拖动相关的手势分析方面gesturedetector只能说是勉为其难. 关于ViewDragHelp

  • Android中贝塞尔曲线的绘制方法示例代码

    贝塞尔曲线,很多人可能不太了解,什么叫做贝塞尔曲线呢?这里先做一下简单介绍:贝塞尔曲线也可以叫做贝济埃曲线或者贝兹曲线,它由线段与节点组成,节点是可拖动的支点,线段像可伸缩的皮筋.一般的矢量图形软件常利用贝塞尔曲线来精确画出曲线. 上面的介绍中,"线段像可伸缩的皮筋"这句话非常关键,但也特别好理解.至于贝塞尔曲线的详细内容大家可以查阅相关资料.        Android提供的贝塞尔曲线绘制接口 在Android开发中,要实现贝塞尔曲线其实还是很简单的,因为Android已经给我们提

  • Android自定义View绘制贝塞尔曲线实现流程

    目录 前言 二阶贝塞尔曲线 三阶贝塞尔曲线 前言 对于Android开发,实现贝塞尔曲线还是比较方便的,有对应的API供你调用.由于一阶贝塞尔曲线就是一条直线,实际没啥多大用处,因此,下面主要讲解二阶和三阶. 二阶贝塞尔曲线 在Android中,使用quadTo来实现二阶贝塞尔 path.reset() path.moveTo(startX, startY) path.quadTo(currentX, currentY, endX, endY) canvas.drawPath(path, cur

  • Android自定义View绘制贝塞尔曲线中小红点的方法

    目录 前言 需求 效果图 代码 主要问题 简单画法 使用贝塞尔曲线 前言 上一篇文章用扇形图练习了一下安卓的多点触控,实现了单指旋转.二指放大.三指移动,四指以上同时按下进行复位的功能.今天这篇文章用很多应用常见的小红点,来练习一下贝塞尔曲线的使用. 需求 这里想法来自QQ的拖动小红点取消显示聊天条数功能,不过好像是记忆里的了,现在看了下好像效果变了.总而言之,就是一个小圆点,拖动的时候变成水滴状,超过一定范围后触发消失回调,核心思想如下: 1.一个正方形view,中间是小红点,小红点距离边框有

随机推荐