Android floating animation

I. Introduction

The requirement is for a control to do a floating and bumping animation in a specified area, and it will bounce when it touches the edge. This animation is simply done here, but the movement rate inside is fixed, and no elastic animation is done. The movement angle is fixed, no random calculation is done. Just made a simple bounce animation. However, there is a bug in this code. If the animation lasts for more than an hour, there will be a problem of horizontal movement back and forth in one position.

2. Related codes

How to use:

 BouncingUtil bouncingUtil = new BouncingUtil(childView, parentView);
                    bouncingUtil.startAnim();

//停止动画
 if (bouncingUtil != null) bouncingUtil.stopAnim();
/**
 * 一个用来控制控件在指定范围内弹跳的类
 *
 * @author YM
 */
public class BouncingUtil {
    
    
    private final int motionEvent = 0x001;
    /**
     * 移动步长
     */
    private final int STEP = 8;
    private final int DURATION = 15; //每次运动的时间间隔
    /**
     * 运动角度
     */
    private double angle = Math.PI;
    private double angleOffset = 1; //处于某些特殊情况下对角度进行修正
    private float startMulti = 0.5f; // 起始倍数
    private final View bouncingView; //用来弹跳的控件
    private final View scopeView; //用来运动的区域
    private int scopeViewLeft; //区域左边距
    private int scopeViewRight; //区域右边距
    private int scopeViewTop; //区域上边距
    private int scopeViewBottom; //区域底边距
    private boolean isRunning = false;//动画是否运行中

    private final Handler uiHandler = new Handler(Looper.getMainLooper()) {
    
    
        @Override
        public void handleMessage(@NonNull Message msg) {
    
    
            super.handleMessage(msg);
            if (msg.what == motionEvent) {
    
    
                updateLocation();
            }
        }
    };

    public BouncingUtil(View bouncingView, View scopeView) {
    
    
        this.bouncingView = bouncingView;
        this.scopeView = scopeView;
        if (bouncingView.getWidth() == 0) {
    
    
            throw new IllegalStateException("运动控件大小范围不能为0");
        }
        if (scopeView.getWidth() == 0) {
    
    
            throw new IllegalStateException("运动的区域范围不能为0");
        }
        initData();
    }

    private void initData() {
    
    
        scopeViewLeft = scopeView.getLeft();
        scopeViewRight = scopeView.getRight();
        scopeViewTop = scopeView.getTop();
        scopeViewBottom = scopeView.getBottom();
//        Log.e("YM---->","BouncingUtil--->scopeViewLeft:"+scopeViewLeft+ " -->scopeViewRight:"+scopeViewRight+" ----->scopeViewTop:"+scopeViewTop+"--->scopeViewBottom:"+scopeViewBottom);
        computerAngle();
    }

    //如果角度刚好是水平或者垂直的就会出现水平或垂直运动,这里进行下调整
    // 在 0.5 * PI - 1 * PI - 1.5 * PI - 2 * PI - .... - ... - 9.5 * PI - ... 之间进行 向上垂直 - 向左水平 - 向下垂直 - 向右水平 - ... - 向下垂直 - ....
    // 综上所述,0.75 向右上
    private void computerAngle() {
    
    
//        angle = 2 + Math.PI * Math.random();
//        angle = Math.PI; //水平
//        angle = 0.5 * Math.PI; //垂直
//        if (angle % Math.PI == 0) {
    
    
//            angle += angleOffset;
//        }
//        if (angle % (Math.PI * 0.5) == 0) {
    
    
//            angle += angleOffset;
//        }
//        angle = 5.165469871174528;
        angle = startMulti * Math.PI;
        angle = fixNum(angle);
//        angle = 5.09;
        Log.e("YM---->", "--->漂浮动画运动的角度:" + angle);
    }

    //修正下数字,只保留两位小数
    //这里需要金属保留两位小数量,否则小数位数太多会导致问题,具体可以尝试angle = 5.165469871174528
    private double fixNum(double num) {
    
    
        BigDecimal two = new BigDecimal(num);
        return two.setScale(2, RoundingMode.HALF_UP).doubleValue();//保留两位小数
    }

    public void startAnim() {
    
    
        if (isRunning) return;
        isRunning = true;
        new Thread(new Runnable() {
    
    
            @Override
            public void run() {
    
    
                while (isRunning) {
    
    
                    if (bouncingView.getWidth() > 0){
    
    
                        uiHandler.sendEmptyMessage(motionEvent);
                    }
                    try {
    
    
                        Thread.sleep(DURATION);
                    } catch (InterruptedException e) {
    
    
                        e.printStackTrace();
                    }
                }
            }
        }).start();
    }

    //停止动画
    public void stopAnim() {
    
    
        isRunning = false;
        uiHandler.removeCallbacksAndMessages(null);
    }

    private void updateLocation() {
    
    
        float bouncingViewX = bouncingView.getX();
        float bouncingViewY = bouncingView.getY();
        float bouncingViewW = bouncingView.getWidth();
        float bouncingViewH = bouncingView.getHeight();
        double left = fixNum(bouncingViewX);
        double right = fixNum(bouncingViewX + bouncingViewW);
        double top = fixNum(bouncingViewY);
        double bottom = fixNum(bouncingViewY + bouncingViewH);
        /* 触碰到边界,发生镜像反射 */
//        Log.e("YM---->", "BouncingUtil--bouncingView->left:" + left + " -->right:" + right + " ----->top:" + top + "--->bottom:" + bottom);
        int direction = -1; // -1 无边界情况 0 right,1 left 2 top 3 bottom
        if (right >= scopeViewRight) {
    
     // 碰右边界
            angle = Math.PI - angle;
            direction = 0;
//            Log.e("YM--->", "--->右边界:" + angle);
        } else if (left <= scopeViewLeft) {
    
     // 碰左边界
            angle = Math.PI - angle;
            direction = 1;
//            Log.e("YM--->", "--->左边界:" + angle);
        } else if (top <= scopeViewTop) {
    
     // 碰上边界
            angle = -angle;
            direction = 2;
//            Log.e("YM--->", "--->上边界:" + angle);
        } else if (bottom >= scopeViewBottom) {
    
     // 碰下边界
            angle = -angle;
            direction = 3;
//            Log.e("YM--->", "--->下边界:" + angle);
        }

        /* 计算新的新坐标 */
//        double cosStep = STEP * Math.cos(angle); //余弦值在-1.0 和 1.0 之间进行获取,这个值与angle的正负值无关
//        double sinStep = STEP * Math.sin(angle); //正弦值在-1.0 和 1.0 之间进行获取,这个值与angle的正负值无关
        //由于正弦和余弦的正负值无法确定,所以下面将正负值进行统一处理
        double cosAngle = Math.cos(angle);
        double sinAngle = Math.sin(angle);
        if (direction == 0){
    
    
            cosAngle = -Math.abs(cosAngle);
        }
        if (direction == 1){
    
    
            cosAngle = Math.abs(cosAngle);
        }
        if (direction == 2){
    
    
            cosAngle = Math.abs(sinAngle);
        }
        if (direction == 3){
    
    
            cosAngle = -Math.abs(sinAngle);
        }

        double cosStep = STEP * cosAngle;
        double sinStep = STEP * sinAngle;
        float newX = (float) fixNum(left + cosStep);
        float newY = (float) fixNum(top - sinStep);
        //设置新的移动位置
        bouncingView.setX(newX);
        bouncingView.setY(newY);
    }

    public boolean isRunning() {
    
    
        return isRunning;
    }

}

Guess you like

Origin blog.csdn.net/Mr_Tony/article/details/127090479