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

2020-11-03 21:26:06 浏览数 (1)

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

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

效果图:

xml布局:

代码语言:javascript复制
<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类:

代码语言:javascript复制
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

代码语言:javascript复制
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

代码语言:javascript复制
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;
}
}

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

0 人点赞