android 通过贝塞尔曲线 实现爱心点赞功能:
  ValueAnimatior:
核心功能:
    已知起点p0, 终p3,中间点 p1、p2  通过贝塞尔曲线 计算路径中各个点
案例1:  重力抛物线  
案例2:  爱心点赞功能

核心代码:LoveLayout 

package mk.denganzhi.com.zhiwenku;

import java.util.Random;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.content.Context;
import android.graphics.PointF;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Log;
import android.widget.ImageView;
import android.widget.RelativeLayout;

import mk.denganzhi.com.zhiwenku.BezierEvaluator;
import mk.denganzhi.com.zhiwenku.R;

public class LoveLayout extends RelativeLayout {


    Drawable[] drawables = new Drawable[3];
    private Random random = new Random();
    private int dHeight;
    private int dWidth;
    private LayoutParams params;
    private int mWidth;
    private int mHeight;

    public LoveLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    public LoveLayout(Context context) {
        super(context);
        init();
    }
    public LoveLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        // 必须在测量完毕以后才知道控件高度、和宽度
        mWidth = getMeasuredWidth();
        mHeight = getMeasuredHeight();
    }

    private void init() {
        //准备图片集合
        drawables[0] = getResources().getDrawable(R.drawable.red);
        drawables[1] = getResources().getDrawable(R.drawable.yellow);
        drawables[2] = getResources().getDrawable(R.drawable.blue);
        //得到图片的原始高度
        dWidth = drawables[0].getIntrinsicWidth();
        dHeight = drawables[0].getIntrinsicHeight();
        params = new LayoutParams(dWidth, dHeight);
        //将iv添加到父容器底部、水平居中位置
        params.addRule(CENTER_HORIZONTAL);
        params.addRule(ALIGN_PARENT_BOTTOM);
    }

    // 1. 第一步:  绘制ImageView 心形
    public void addLoveIcon(){
        //添加心形,并开始执行动画
        final ImageView iv = new ImageView(getContext());
        iv.setImageDrawable(drawables[random.nextInt(3)]);
        //将iv添加到父容器底部、水平居中位置
        iv.setLayoutParams(params);
        addView(iv);
        //开始属性动画:平移、透明度渐变、缩放动画
        AnimatorSet set = getAnimator(iv);

        //监听动画执行完毕,将iv移除或者复用
        set.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                removeView(iv);
            }
        });
        set.start();
    }

    // 得到一个iv的动画集合
    private AnimatorSet getAnimator(ImageView iv) {
        //平移、透明度渐变、缩放动画
        //1.alpha动画
        ObjectAnimator alpha = ObjectAnimator.ofFloat(iv, "alpha", 0.3f,1f);
        //2.缩放动画
        ObjectAnimator scaleX = ObjectAnimator.ofFloat(iv, "scaleX", 0.3f,1f);
        ObjectAnimator scaleY = ObjectAnimator.ofFloat(iv, "scaleY", 0.3f,1f);
        //三个动画同时执行
        AnimatorSet enter = new AnimatorSet();
        enter.setDuration(600);
        enter.playTogether(alpha,scaleX,scaleY);
//		enter.setTarget(iv);

        //设置平移的曲线动画---贝塞尔曲线
        // 2. 第二步启动估值器
        ValueAnimator bezierAnimator = getBezierValueAnimator(iv);

        AnimatorSet set = new AnimatorSet();
        //同时依次执行 实现属性动画
        set.playSequentially(enter,bezierAnimator);
        set.setTarget(iv);
        return set;
    }

    //得到一个贝塞尔曲线动画
    private ValueAnimator getBezierValueAnimator(final ImageView iv) {
        //根据贝塞尔公式确定四个点(起始点p0,拐点1p1,拐点2p2,终点p3)
        PointF pointF0 = new PointF((mWidth-dWidth)/2, mHeight-dHeight);
        PointF pointF3 = new PointF(random.nextInt(mWidth), 0);
        PointF pointF1 = getPointF(1);
        PointF pointF2 = getPointF(2);
        BezierEvaluator evaluator = new BezierEvaluator(pointF1,pointF2);
        ValueAnimator animator = ValueAnimator.ofObject(evaluator, pointF0,pointF3);
        animator.addUpdateListener(new AnimatorUpdateListener() {

            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                PointF pointF = (PointF) animation.getAnimatedValue();
//                iv.setX(pointF.x);
//                iv.setY(pointF.y);
                iv.setX(pointF.x);
                iv.setY(pointF.y);
                Log.e("denganzhi1","坐标是:x"+ pointF.x +"  y:" + pointF.y);
                iv.setAlpha(1-animation.getAnimatedFraction());//1~0 百分比
            }
        });

        animator.setDuration(10000);
        return animator;
    }

    private PointF getPointF(int i) {
        PointF pointF = new PointF();
        pointF.x = random.nextInt(mWidth);
        if(i==1){
            pointF.y = random.nextInt(mHeight/2)+mHeight/2;
        }else{
            pointF.y = random.nextInt(mHeight/2);
        }
        return pointF;
    }

}

 估值器:BezierEvaluator

package mk.denganzhi.com.zhiwenku;

import android.animation.TypeEvaluator;
import android.graphics.PointF;

public class BezierEvaluator implements TypeEvaluator<PointF> {

	private PointF pointF1;
	private PointF pointF2;

	public BezierEvaluator(PointF pointF1, PointF pointF2) {
		// TODO Auto-generated constructor stub
		this.pointF1 = pointF1;
		this.pointF2 = pointF2;
	}

	@Override
	public PointF evaluate(float t, PointF point0, PointF point3) {
		// b(t)=p0*(1-t)*(1-t)*(1-t)+3*p1*t*(1-t)*(1-t)+3*p2*t*t*(1-t)+p3*t*t*t
		PointF point = new PointF();
		point.x = point0.x*(1-t)*(1-t)*(1-t)
				+3*pointF1.x*t*(1-t)*(1-t)
				+3*pointF2.x*t*t*(1-t)
				+point3.x*t*t*t;
		point.y = point0.y*(1-t)*(1-t)*(1-t)
				+3*pointF1.y*t*(1-t)*(1-t)
				+3*pointF2.y*t*t*(1-t)
				+point3.y*t*t*t;
		return point;
	}

}

 

MainActivity代码,使用控件:

package mk.denganzhi.com.zhiwenku;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;

public class MainActivity extends AppCompatActivity {
    private LoveLayout loveLayout;

    MyBazierLayout  myBazierLayout;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        loveLayout = (LoveLayout)findViewById(R.id.loveLayout);
        myBazierLayout = findViewById(R.id.loveLayout2);


    }

    public void  start(View view){
        loveLayout.addLoveIcon();

      //  myBazierLayout.addLoveIcon();
    }

    public void translate(View view) {
     //   myBazierLayout.translateFun();
    }
}

 MainActivity 的xml布局

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
     >

    <mk.denganzhi.com.zhiwenku.LoveLayout
        android:id="@+id/loveLayout"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        />

    <mk.denganzhi.com.zhiwenku.MyBazierLayout
        android:id="@+id/loveLayout2"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:visibility="gone">

    </mk.denganzhi.com.zhiwenku.MyBazierLayout>
<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal">

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="start"
        android:text="赞一个" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="translate"
        android:text="移动" />

</LinearLayout>

</RelativeLayout>

效果图功能: 

  源码地址:https://download.csdn.net/download/dreams_deng/12255079 

参考:https://www.jb51.net/article/137316.htm

ValueAnimator.ofObject的使用:  https://blog.csdn.net/zhaihaohao1/article/details/80941009

归纳:使用贝塞尔公式绘制曲线路径

package mk.denganzhi.com.zhiwenku;

import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.PointF;
import android.graphics.drawable.BitmapDrawable;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.WindowManager;
import android.widget.ImageButton;
import android.widget.ImageView;

public class TestActivity extends AppCompatActivity {

    ImageButton mybtn;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);

        mybtn = (ImageButton) findViewById(R.id.mybtn);


    }
  boolean isFlag=false;
    public void startAnimation(View view) {

        WindowManager wm = (WindowManager) this
                .getSystemService(Context.WINDOW_SERVICE);
        final int width = wm.getDefaultDisplay().getWidth();
        int height = wm.getDefaultDisplay().getHeight();

        float startX= mybtn.getLeft();
        float startY =mybtn.getTop();

        PointF pointF0 = new PointF(startX, startY);
        PointF pointF2 = new PointF(startX-50,startY-10);
        PointF pointF3 = new PointF(startX-300,startY-500);
        
        // 贝塞尔公式
        PointF pointF4 = new PointF(width/2-mybtn.getWidth()/2,height/2-mybtn.getHeight()/2);
        BezierEvaluator evaluator = new BezierEvaluator(pointF2,pointF3);

        Log.e("denganzhi","startx:"+startX+"  startY:"+startY);

        ValueAnimator animator = ValueAnimator.ofObject(evaluator, pointF0,pointF4);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                PointF pointF = (PointF) animation.getAnimatedValue();
//                iv.setX(pointF.x);
//                iv.setY(pointF.y);


                int scaleBeiShu= (width)/mybtn.getWidth();

                mybtn.setX(pointF.x);
                mybtn.setY(pointF.y);
                Log.e("denganzhi1","坐标是:x"+ pointF.x +"  y:" + pointF.y +" scaleBeiShu:"+scaleBeiShu);


                if(pointF.x<350){

                    if(!isFlag){
                        mybtn.setImageDrawable(new BitmapDrawable());
/**
 *  .scaleY(2) :  放大到2倍
 *  scaleYBy(1):  在原来基础上放大1倍,累加过程
 *
 *  scrollTo
 *  scrollBy
 */
                        mybtn.animate()
                                .scaleYBy(1)
                                .scaleXBy(scaleBeiShu)
                                .setDuration(3000);


                        isFlag=true;
                    }

                }

           //     mybtn.setAlpha(1-animation.getAnimatedFraction());//1~0 百分比
            }
        });

        animator.setDuration(1000);

        animator.start();

    }


}

activity_test 布局:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="mk.denganzhi.com.zhiwenku.TestActivity">


    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:background="#bfa"
        android:layout_centerVertical="true"></LinearLayout>

    <ImageButton
        android:id="@+id/mybtn"
        android:layout_width="60dp"
        android:layout_height="60dp"
        android:src="@mipmap/ic_launcher"
        android:layout_alignParentBottom="true"
        android:layout_alignParentRight="true"
        android:layout_marginBottom="30dp"
        android:layout_marginRight="30dp"
        android:background="#00f"
        android:onClick="startAnimation"
        />



</RelativeLayout>

效果:

 直接起点,终点, 中间2个点就可以使用贝塞尔曲线,绘制绘制轨迹

 问题就是:不知道它的点,知道点以后才知道轨迹 

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐