8:Math.atan2、PathMeasure、点赞飘心效果、点赞数字滚动+1效果、集成支付

1、Math.atan()与Math.atan2()的区别

angle = Math.atan(slope)
复制代码

slope斜率值为y/x,返回值angle为一个角度的弧度制,因为角度的周期性,此方法不能很好的反应角度,应使用Math.atan2替换。

angle = Math.atan2(y,x)
复制代码

x是临边边长,y是对边边长。

弧度转角度方法:

    弧度 = 角度 * Math.PI / 180;
    角度 = 弧度 * 180 / Math.PI;
复制代码

计算两点间连线的倾斜角:

Math.atan2()函数返回的是点(x,y)和原点(0,0)之间直线的倾斜角,计算任意两点的切斜角,应使用Math.atan2(y2-y1,x2-x1)。

2、PathMeasure结合ValueAnimator实现轨迹绘制效果

关键方法:

PathMeasure#getLength():获取轨迹总长度

PathMeasure#getSegment(float startD, float stopD, Path dst, boolean startWithMoveTo):startD、stopD分别是轨迹长度值,dst是保存到的片段路径。

public class CirclePathView extends View {

    private int mWidth;
    private int mHeight;
    private PathMeasure mPathMeasure;
    private float mLength;
    private Path mDst;
    private float animatedValue;
    private Paint mPaint;
    private Path mPath;

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

    public CirclePathView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs,0);
    }

    public CirclePathView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView();
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mWidth = w;
        mHeight = h;
        mPath = new Path();
        mPath.addCircle(mWidth / 2,mHeight / 2,mWidth / 4,Path.Direction.CW);
        mPathMeasure = new PathMeasure();
        mPathMeasure.setPath(mPath,true);
        mLength = mPathMeasure.getLength();
    }

    private void initView() {
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(5);
        mPaint.setColor(Color.BLACK);

        mDst = new Path();

        ValueAnimator animator = ValueAnimator.ofFloat(1, 0);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                animatedValue = (float) animation.getAnimatedValue();
                invalidate();
            }
        });
        animator.setDuration(2000);
        animator.setRepeatCount(ValueAnimator.INFINITE);
        animator.start();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        mDst.reset();
        mDst.lineTo(0,0);
        float stop = mLength * animatedValue;
        float start = (animatedValue - 0.5f > 0 ? (animatedValue - 0.5f) * 2 : 0f) * mLength;
        mPathMeasure.getSegment(start,stop,mDst,true);
        canvas.drawPath(mDst,mPaint);
    }
}

复制代码

轨迹效果也可使用DashPathEffect,通过控制实线的偏移量来实现。

3、点赞-飘心效果

  • 获取一个间于min和max之间的随机值:
int randomNum = new Random().nextInt(max - min + 1) + min;
//int randomNum = new Random().nextInt(max) % (max - min + 1) + min; //好傻噢
复制代码
  • AS打开工程报错ssl peer shut down incorrectly:gradle版本问题。
  • GitHub飘心点赞项目:github.com/trc1993/And…

核心逻辑:确认红心起点、终点坐标(高度随机),创建2阶贝塞尔曲线,通过PathMeasure.getPosTan()和ValueAnimator,获取路径上每个点的坐标,再通过View#setTranslationX()和View#setTranslationY()移动红心。

    private void addFHeart(ImageView startLocationIv, Drawable floatDrawable) {
        int min = animMinWidth;
        int max = animMaxWidth;
        Random random = new Random();

        animWidth = random.nextInt(max - min + 1) + min;
        int min1 = animMinHeight;
        int max1 = animMaxHeight;
        Random random1 = new Random();
        animHeight = random1.nextInt(max1 - min1 + 1) + min1;


        final ImageView _floatIv = new ImageView(context);
        _floatIv.setImageDrawable(floatDrawable);
        RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(floatIconWidth, floatIconHeight);
        params.width = DisplayUtil.dip2px(context, floatIconWidth);
        params.height = DisplayUtil.dip2px(context, floatIconHeight);
        FRl.addView(_floatIv, params);

        int[] parentLocation = new int[2];
        FRl.getLocationInWindow(parentLocation);

        //得到起始图片的坐标(用于计算动画开始的坐标)
        int startLoc[] = new int[2];
        startLocationIv.getLocationInWindow(startLoc);

//        三、正式开始计算动画开始/结束的坐标
        //起始点:图片起始点-父布局起始点+该图片的一半
        startX = startLoc[0] - parentLocation[0];
        startY = startLoc[1] - parentLocation[1];

        endX = startX;
        endY = startY - animHeight;
        firstControlX = startX - animWidth;
        firstControlY = startY - animHeight * 3 / 8;
        secondControlX = startX + animWidth;
        secondControlY = startY - animHeight * 5 / 8;
        Path path = new Path();
        path.moveTo(startX, startY);
        path.cubicTo(firstControlX, firstControlY, secondControlX, secondControlY, endX, endY);


        final PathMeasure mPathMeasure = new PathMeasure(path, false);

        //★★★属性动画实现(从0到贝塞尔曲线的长度之间进行插值计算,获取中间过程的距离值)

        AnimatorSet bouncer = new AnimatorSet();
        ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, mPathMeasure.getLength());
        ObjectAnimator anim = ObjectAnimator.ofFloat(_floatIv, "alpha", 1f, 0.1f);
        // 匀速线性插值器
        bouncer.setInterpolator(new LinearInterpolator());
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                // 当插值计算进行时,获取中间的每个值,
                // 这里这个值是中间过程中的曲线长度(下面根据这个值来得出中间点的坐标值)
                float value = (Float) animation.getAnimatedValue();
                Log.d("FloatHeartView", "value:" + value);
                // ★★★★★获取当前点坐标封装到mCurrentPosition
                // boolean getPosTan(float distance, float[] pos, float[] tan) :
                // 传入一个距离distance(0<=distance<=getLength()),然后会计算当前距
                // 离的坐标点和切线,pos会自动填充上坐标,这个方法很重要。
                mPathMeasure.getPosTan(value, mCurrentPosition, null);//mCurrentPosition此时就是中间距离点的坐标值
                // 移动的图片(动画图片)的坐标设置为该中间点的坐标
                _floatIv.setTranslationX(mCurrentPosition[0]);
                _floatIv.setTranslationY(mCurrentPosition[1]);
            }
        });

        bouncer.play(valueAnimator).with(anim);
        bouncer.setDuration(animDuration);
        bouncer.start();

        bouncer.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animation) {

            }

            //当动画结束后:
            @Override
            public void onAnimationEnd(Animator animation) {
//                // 把移动的图片imageview从父布局里移除
                FRl.removeView(_floatIv);
            }

            @Override
            public void onAnimationCancel(Animator animation) {

            }

            @Override
            public void onAnimationRepeat(Animator animation) {

            }
        });
    }
复制代码

4、点赞-数字滚动+1效果

juejin.im/post/5c2190…

5、支付宝、微信、银联支付

juejin.im/post/596d97…

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值