爆炸的圆

爆炸的圆_第1张图片
Paste_Image.png

自定义一个view画(ondraw)两个静态圆

先放到布局文件中
绘制两个圆—要看看两个圆的坐标大致放在哪里
(300,300)(500,300)
半径给20

public class GooView extends View {
    private Paint paint;

    public GooView(Context context) {
        this(context, null);
    }

    public GooView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public GooView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        paint = new Paint(Paint.ANTI_ALIAS_FLAG);//抗锯齿
        paint.setColor(Color.RED);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.drawCircle(300, 300, 20, paint);
        canvas.drawCircle(300, 400, 20, paint);
    }
}

绘制静态圆的连接部分

花两根曲线—填充中间部分—由于是不规则图形—所以通过path来画

爆炸的圆_第2张图片
Paste_Image.png

曲线的因素—给定起点和终点,还有弯曲点

@Override
protected void onDraw(Canvas canvas) {
    canvas.drawCircle(300, 300, 20, paint);
    canvas.drawCircle(300, 400, 20, paint);
    Path path1 = new Path();
    path1.moveTo(400, 280);//指定曲线的起点
    //quadTo画贝塞尔曲线--参1参2控制点(弯曲点)--参3参4结束点
    path1.quadTo(350, 300, 300, 280);
    //把path画出来
    //连线到第2条曲线的起点
    path1.lineTo(300, 320);//直线
    path1.quadTo(350, 300, 400, 320);
    path1.close();//闭合--会自动闭合
    canvas.drawPath(path1, paint);//会自动闭合,会自动填充

}

定义变量来替换固定值的位置—以达到动态变换

FloatEvaluator floatEval = new FloatEvaluator();
float dragRadius = 20;//drag圆的半径
float stickyRadius = 20;//sticky圆的半径
PointF dragCenter = new PointF(400, 300);//drag圆的圆心(PointF点)(Point--是int值)
PointF stickyCenter = new PointF(400, 300);//sticky圆的圆心
PointF controlPoint = new PointF(350, 300);//控制点
//sticky圆上的2个点—数组方便计算
PointF[] stickyPoints = {new PointF(400, 280), new PointF(400, 320)};
//drag圆上的2个点
PointF[] dragPoints = {new PointF(300, 280), new PointF(300, 320)};

double lineK;//角的斜率,就是正切值, tan

@Override
protected void onDraw(Canvas canvas) {
    canvas.drawCircle(dragCenter.x, dragCenter.y, dragRadius, paint);
    canvas.drawCircle(stickyCenter.x, stickyCenter.y, stickyRadius, paint);
    //2.绘制2圆中间连接的部分
    Path path = new Path();
    path.moveTo(stickyPoints[0].x, stickyPoints[0].y);//指定曲线的起点
    //quadTo画贝塞尔曲线
    path.quadTo(controlPoint.x, controlPoint.y, dragPoints[0].x, dragPoints[0].y);
    //连线到第2条曲线的起点
    path.lineTo(dragPoints[1].x, dragPoints[1].y);
    path.quadTo(controlPoint.x, controlPoint.y, stickyPoints[1].x, stickyPoints[1].y);
    path.close();
    canvas.drawPath(path, paint);
}

让drag圆跟随手指的移动而改变位置

Drag圆的圆心与手指的触摸点的圆心一致
取出触摸点—在ontouch中
并让drag圆的圆心变成触摸点

@Override
public boolean onTouchEvent(MotionEvent event) {
    switch (event.getAction()) {
        //按下和移动都可以改变圆的坐标
        case MotionEvent.ACTION_DOWN:
        case MotionEvent.ACTION_MOVE:
            //设置drag圆的圆心点--会改变圆心的坐标-但界面不会发生变化--得重绘—point有设置点的坐标的方法
            dragCenter.set(event.getX(), event.getY());
            break;
    }
    //刷新重绘
    invalidate();
    return true;
}

动态计算4个点和控制点的坐标

圆心与半径已知

爆炸的圆_第3张图片
Paste_Image.png
double lineK;//角的斜率,就是正切值, tan

@Override
protected void onDraw(Canvas canvas) {
    //a.计算正切值
    float dy = dragCenter.y - stickyCenter.y;//2圆圆心y的差值
    float dx = dragCenter.x - stickyCenter.x;//
    if (dx != 0) {
        lineK = dy / dx;
    }
    //b.动态计算4个点的坐标
    dragPoints = GeometryUtil.getIntersectionPoints(dragCenter, dragRadius, lineK);
    stickyPoints = GeometryUtil.getIntersectionPoints(stickyCenter, stickyRadius, lineK);

    //c.动态计算控制点
    controlPoint = GeometryUtil.getPointByPercent(dragCenter, stickyCenter, 0.618f);
}

动态计算stick圆点 的坐标

//2圆圆心最大的距离
int maxDistance = 150;
//浮点计算器
FloatEvaluator floatEval = new FloatEvaluator();

/**
 * 动态计算sticky圆的半径
 *
 * @return
 */
private float calculateStickyRadius() {
    //计算2圆圆心当前的距离
    float distance = GeometryUtil.getDistanceBetween2Points(dragCenter, stickyCenter);
    //比例
    float fraction = distance / maxDistance;
    //浮点计算器--计算--参1比例--参2起始值,参3结束值
    return floatEval.evaluate(fraction, 20, 4);
}

@Override
protected void onDraw(Canvas canvas) {
    //动态计算sticky圆的半径
    stickyRadius = calculateStickyRadius();
}

几何图形工具类

/**
 * 几何图形工具
 */
public class GeometryUtil {

    /**
     * As meaning of method name.
     * 获得两点之间的距离
     *
     * @param p0
     * @param p1
     * @return
     */
    public static float getDistanceBetween2Points(PointF p0, PointF p1) {
        float distance = (float) Math.sqrt(Math.pow(p0.y - p1.y, 2) + Math.pow(p0.x - p1.x, 2));
        return distance;
    }

    /**
     * Get middle point between p1 and p2.
     * 获得两点连线的中点
     *
     * @param p1
     * @param p2
     * @return
     */
    public static PointF getMiddlePoint(PointF p1, PointF p2) {
        return new PointF((p1.x + p2.x) / 2.0f, (p1.y + p2.y) / 2.0f);
    }

    /**
     * Get point between p1 and p2 by percent.
     * 根据百分比获取两点之间的某个点坐标
     *
     * @param p1
     * @param p2
     * @param percent
     * @return
     */
    public static PointF getPointByPercent(PointF p1, PointF p2, float percent) {
        return new PointF(evaluateValue(percent, p1.x, p2.x), evaluateValue(percent, p1.y, p2.y));
    }

    /**
     * 根据分度值,计算从start到end中,fraction位置的值。fraction范围为0 -> 1
     *
     * @param fraction
     * @param start
     * @param end
     * @return
     */
    public static float evaluateValue(float fraction, Number start, Number end) {
        return start.floatValue() + (end.floatValue() - start.floatValue()) * fraction;
    }

    /**
     * Get the point of intersection between circle and line.
     * 获取 通过指定圆心,斜率为lineK的直线与圆的交点。
     *
     * @param pMiddle The circle center point.
     * @param radius  The circle radius.
     * @param lineK   The slope of line which cross the pMiddle.
     * @return
     */
    public static PointF[] getIntersectionPoints(PointF pMiddle, float radius, Double lineK) {
        PointF[] points = new PointF[2];

        float radian, xOffset = 0, yOffset = 0;
        if (lineK != null) {
            radian = (float) Math.atan(lineK);//得到该角的角度
            xOffset = (float) (Math.sin(radian) * radius);//得到对边的长
            yOffset = (float) (Math.cos(radian) * radius);//得到邻边的长
        } else {
            xOffset = radius;
            yOffset = 0;
        }
        points[0] = new PointF(pMiddle.x + xOffset, pMiddle.y - yOffset);
        points[1] = new PointF(pMiddle.x - xOffset, pMiddle.y + yOffset);

        return points;
    }
}

当拖动超出保护圈-则连接部位断掉

断掉就是不绘制
定义变量当超出时不绘制连接部位

定义变量确定是否超出范围

boolean isOutOfRange = false;//是否超出最大范围

拖动时进行判断(每次都会重绘)

@Override
public boolean onTouchEvent(MotionEvent event) {
    switch (event.getAction()) {
        //按下和移动都可以改变圆的坐标
        case MotionEvent.ACTION_DOWN:
        case MotionEvent.ACTION_MOVE:
            isOutOfRange = distance > maxDistance;
            break;

重绘—当超出不绘制中间连接部位

@Override
protected void onDraw(Canvas canvas) {
    if (!isOutOfRange) {
        //绘制固定圆
        canvas.drawCircle(stickyCenter.x, stickyCenter.y, stickyRadius, paint);
        //2.绘制2圆中间连接的部分
        Path path = new Path();
        path.moveTo(stickyPoints[0].x, stickyPoints[0].y);//指定曲线的起点
        //quadTo画贝塞尔曲线
        path.quadTo(controlPoint.x, controlPoint.y, dragPoints[0].x, dragPoints[0].y);
        //连线到第2条曲线的起点
        path.lineTo(dragPoints[1].x, dragPoints[1].y);
        path.quadTo(controlPoint.x, controlPoint.y, stickyPoints[1].x, stickyPoints[1].y);
        path.close();

        canvas.drawPath(path, paint);

    }
    
}

手指抬起处理

处理圈内抬起手指的弹回动画

        case MotionEvent.ACTION_UP:
        if (isOutOfRange) {
            //在圈外抬起手指,执行爆炸动画
            execBoomAnim(dragCenter.x, dragCenter.y);

            //重置坐标
            dragCenter.set(stickyCenter);
        } else {
            //在圈内,将圆回弹到sticky的位置
            final PointF start = new PointF(dragCenter.x, dragCenter.y);
            ValueAnimator animator = ValueAnimator.ofFloat(1, 3);//--随便填
            animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    //获取动画执行的百分比--0--1
                    float fraction = animation.getAnimatedFraction();
                    //获取两点间的任一比例的值(参1起始点,参2终止点)
                    PointF p = GeometryUtil.getPointByPercent(start, stickyCenter, fraction);
                    //将drag圆的圆心设置p--只是改变点的坐标
                    dragCenter.set(p);
                    //重绘
                    invalidate();
                }
            });
            //加点弹性
            animator.setInterpolator(new OvershootInterpolator(4));
            animator.setDuration(500).start();
        }

        break;

    }

圈外抬起执行爆炸动画

画保护圈

//给Sticky画出保护圈
paint.setStyle(Paint.Style.STROKE);//只有边框线
canvas.drawCircle(stickyCenter.x, stickyCenter.y, maxDistance, paint);
paint.setStyle(Paint.Style.FILL);//填充内容--画完之后填充内容--因为每次都会重绘
case MotionEvent.ACTION_UP:
    if (isOutOfRange) {
        //在圈外抬起手指,执行爆炸动画
        //在手指抬起的位置执行爆炸
        execBoomAnim(dragCenter.x, dragCenter.y);

        //重置坐标--爆炸之后坐标回去
        dragCenter.set(stickyCenter);
    } else {
        //在圈内,将圆回弹到sticky的位置
    }
    break;

帧动画xml—动画需要view作为载体



    
    
    
    
    

创建执行爆炸

/**
 * 创建爆炸动画
 *
 * @param x
 * @param y
 */
//帧动画--将几张图片拷贝过来
private void execBoomAnim ( float x, float y){
    //创建载体view
    final ImageView imageView = new ImageView(getContext());
    //从图片取
    int w = getResources().getDrawable(R.drawable.pop1).getIntrinsicWidth();
    //设置宽高
    imageView.setLayoutParams(new ViewGroup.LayoutParams(w, w));
    //将动画--设置给背景
    imageView.setBackgroundResource(R.drawable.boom_anim);
    //播放帧动画
    AnimationDrawable drawable = (AnimationDrawable) imageView.getBackground();
    drawable.start();
    //将载体view添加到父view中(界面),--这样子就看到了
    final ViewGroup parent = (ViewGroup) getParent();//得到gooView的父view
    parent.addView(imageView);
    //移动ImageView到合适的位置
    //x-imageview 的一半
    imageView.setTranslationX(x - 34);
    imageView.setTranslationY(y - 34);

    //播放完毕后消失--因为没有动画监听器
    new Handler().postDelayed(new Runnable() {
        @Override
        public void run() {
            parent.removeView(imageView);
        }
    }, 650);


}

全码

public class GooView extends View {
    private Paint paint;

    public GooView(Context context) {
        this(context, null);
    }

    public GooView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public GooView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setColor(Color.RED);
    }

    FloatEvaluator floatEval = new FloatEvaluator();
    float dragRadius = 20;//drag圆的半径
    float stickyRadius = 20;//sticky圆的半径
    PointF dragCenter = new PointF(400, 300);//drag圆的圆心
    PointF stickyCenter = new PointF(400, 300);//sticky圆的圆心
    PointF controlPoint = new PointF(350, 300);//控制点
    //sticky圆上的2个点
    PointF[] stickyPoints = {new PointF(400, 280), new PointF(400, 320)};
    //drag圆上的2个点
    PointF[] dragPoints = {new PointF(300, 280), new PointF(300, 320)};

    double lineK;//角的斜率,就是正切值, tan
    @Override
    protected void onDraw(Canvas canvas) {
        //动态计算sticky圆的半径
        stickyRadius = calculateStickyRadius();

        //a.计算正切值
        float dy = dragCenter.y - stickyCenter.y;//2圆圆心y的差值
        float dx = dragCenter.x - stickyCenter.x;//
        if(dx!=0){
            lineK = dy / dx;
        }
        //b.动态计算4个点的坐标
        dragPoints = GeometryUtil.getIntersectionPoints(dragCenter, dragRadius, lineK);
        stickyPoints = GeometryUtil.getIntersectionPoints(stickyCenter, stickyRadius, lineK);

        //c.动态计算控制点
        controlPoint = GeometryUtil.getPointByPercent(dragCenter,stickyCenter,0.618f);


        //1.画2个静态圆
        canvas.drawCircle(dragCenter.x, dragCenter.y, dragRadius, paint);

        if(!isOutOfRange){
            canvas.drawCircle(stickyCenter.x, stickyCenter.y, stickyRadius, paint);
            //2.绘制2圆中间连接的部分
            Path path = new Path();
            path.moveTo(stickyPoints[0].x, stickyPoints[0].y);//指定曲线的起点
            path.quadTo(controlPoint.x, controlPoint.y, dragPoints[0].x, dragPoints[0].y);
            //连线到第2条曲线的起点
            path.lineTo(dragPoints[1].x, dragPoints[1].y);
            path.quadTo(controlPoint.x, controlPoint.y, stickyPoints[1].x, stickyPoints[1].y);
            path.close();

            canvas.drawPath(path, paint);
        }


        //给Sticky画出保护圈
        paint.setStyle(Paint.Style.STROKE);//只有边框线
        canvas.drawCircle(stickyCenter.x, stickyCenter.y, maxDistance, paint);
        paint.setStyle(Paint.Style.FILL);//填充内容

    }

    //2圆圆心最大的距离
    int maxDistance = 150;
    /**
     * 动态计算sticky圆的半径
     * @return
     */
    private float calculateStickyRadius() {
        //计算2圆圆心当前的距离
        float distance = GeometryUtil.getDistanceBetween2Points(dragCenter, stickyCenter);
        float fraction = distance / maxDistance;
        return floatEval.evaluate(fraction, 20, 4);
    }

    boolean isOutOfRange = false;//是否超出最大范围
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
            case MotionEvent.ACTION_MOVE:
                dragCenter.set(event.getX(), event.getY());

                float distance = GeometryUtil.getDistanceBetween2Points(dragCenter, stickyCenter);
                isOutOfRange = distance > maxDistance;
                break;
            case MotionEvent.ACTION_UP:
                if(isOutOfRange){
                    //在圈外抬起手指,执行爆炸动画
                    execBoomAnim(dragCenter.x,dragCenter.y);

                    //重置坐标
                    dragCenter.set(stickyCenter);
                }else {
                    //在圈内,将圆回弹到sticky的位置
                    final PointF start = new PointF(dragCenter.x, dragCenter.y);
                    ValueAnimator animator = ValueAnimator.ofFloat(1,3);
                    animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                        @Override
                        public void onAnimationUpdate(ValueAnimator animation) {
                            float fraction = animation.getAnimatedFraction();
                            PointF p = GeometryUtil.getPointByPercent(start, stickyCenter, fraction);
                            //将drag圆的圆心设置p
                            dragCenter.set(p);
                            invalidate();
                        }
                    });
                    animator.setInterpolator(new OvershootInterpolator(4));
                    animator.setDuration(500).start();
                }
                break;
        }
        //刷新重绘
        invalidate();
        return true;
    }

    /**
     * 创建爆炸动画
     * @param x
     * @param y
     */
    private void execBoomAnim(float x, float y) {
        final ImageView imageView = new ImageView(getContext());
        int w = getResources().getDrawable(R.drawable.pop1).getIntrinsicWidth();
        imageView.setLayoutParams(new ViewGroup.LayoutParams(w,w));
        imageView.setBackgroundResource(R.drawable.boom_anim);
        AnimationDrawable drawable = (AnimationDrawable) imageView.getBackground();
        drawable.start();

        //添加进来
        final ViewGroup parent = (ViewGroup) getParent();
        parent.addView(imageView);

        //移动ImageView到合适的位置
        imageView.setTranslationX(x-34);
        imageView.setTranslationY(y-34);

        //播放完毕后消失
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                parent.removeView(imageView);
            }
        },650);
    }
}

动画

drawable



    
    
    
    
    

pop1.png
pop2.png
pop3.png
pop4.png
pop5.png

你可能感兴趣的:(爆炸的圆)