Android漂浮碰壁动画

一、前言

需求是一个控件在指定区域内做漂浮碰壁动画,当触碰到边缘时候会反弹。这里简单做了下这个动画,不过里面移动速率是固定的,没有做弹性动画。移动角度是固定的,没有做随机运算。只是做了下简单的反弹动画。不过这个代码有bug,动画持续一个小时以上会出现一直在一个位置来回水平运动的问题

二、相关代码

使用方式:

 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;
    }

}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值