自定义万向轮

基础知识部分

  • Android的View的坐标系是从左到右,从上到下。左上角坐标(0,0)
  • 自定义style样式
  • onSizeChanged
  • onDraw
  • OnTouchEvent

Code

  • style
    
        
        
        
    

   // 处理
    TypedArray arr = getContext().obtainStyledAttributes(attrs, R.styleable.EmbeddedCompassView);
    mOutSideRadiusStrokeColor = arr.getColor(R.styleable.EmbeddedCompassView_outSideRadiusStrokerColor, Color.DKGRAY);
    mArrowColor = arr.getColor(R.styleable.EmbeddedCompassView_arrowColor, Color.DKGRAY);
    mStickColor = arr.getColor(R.styleable.EmbeddedCompassView_stickColor, Color.LTGRAY);
    arr.recycle();
  • onSizeChanged
    mWidth = w; 
    mHeight = h;
    int contW = w - getPaddingLeft() - getPaddingRight();
    int contH = h - getPaddingTop() - getPaddingBottom();
    mCenterX = getPaddingLeft() + contW/2; // 中心点x
    mCenterY = getPaddingTop() + contH/2;  // 中心点y
    mOutsideRadius = (int)(Math.min(w, h) * 0.5 - 4); // 外圆半径
    mThresholdRadius = (int) (mOutsideRadius * 0.98); // 万向轮活动半径
    mStickRadius = (int) (mOutsideRadius * 0.685);    // 万向轮半径

  • onDraw
    canvas.drawCircle(mCenterX, mCenterY, mOutsideRadius, mOutSideCirclePaint); // 绘制外圆

    if (isSticking) { // 滑动
        int offsetMaxDistance = (mThresholdRadius-mStickRadius);
        int offsetCurr = Math.max(Math.abs(positionX-mCenterX), Math.abs(positionY-mCenterY));

        int alpha = Math.max((int)(255*offsetCurr*1.0/offsetMaxDistance), 10);  //万向轮按下色渐变
        mStickCirclePaint.setAlpha(alpha);
        canvas.drawCircle(positionX, positionY, mStickRadius, mStickCirclePaint); // 绘制万向轮
        onDrawArrow(canvas, positionX, positionY); // 绘制箭头
    } else {
        onDrawArrow(canvas, mCenterX, mCenterY); // 绘制箭头
    }

   // onDrawArrow
    Path path = new Path();
    int arrowA = 15;
    int arrowB = 30;

    // left
    path.moveTo(positionX-mStickRadius+arrowB, positionY-arrowA);
    path.lineTo(positionX-mStickRadius+arrowA, positionY);
    path.lineTo(positionX-mStickRadius+arrowB, positionY+arrowA);
    canvas.drawPath(path, mOutSideCirclePaint);

    // right
    path.moveTo(positionX+mStickRadius-arrowB, positionY-arrowA);
    path.lineTo(positionX+mStickRadius-arrowA, positionY);
    path.lineTo(positionX+mStickRadius-arrowB, positionY+arrowA);
    canvas.drawPath(path, mOutSideCirclePaint);

    //top
    path.moveTo(positionX-arrowA, positionY-mStickRadius+arrowB);
    path.lineTo(positionX, positionY-mStickRadius+arrowA);
    path.lineTo(positionX+arrowA, positionY-mStickRadius+arrowB);
    canvas.drawPath(path, mOutSideCirclePaint);

    //bottom
    path.moveTo(positionX-arrowA, positionY+mStickRadius-arrowB);
    path.lineTo(positionX, positionY+mStickRadius-arrowA);
    path.lineTo(positionX+arrowA, positionY+mStickRadius-arrowB);
    canvas.drawPath(path, mOutSideCirclePaint);

滑动点范围[0~(mThresholdRadius-mStickRadius)] & TouchEvent

    // 判断是否落在圆内.
    private boolean isInOutSideCircle(int positionX, int positionY) {
        int pow2 = (int)( Math.pow((positionX-mCenterX), 2) + Math.pow((positionY-mCenterY), 2));
        int distance = (int) Math.sqrt(pow2);
        return ((distance+mStickRadius) <= mThresholdRadius);
    }

    // OnTouchEvent
    // 仅初始点在万向轮活动范围内才做处理,外界进入忽略.
    int downX = (int)event.getX();
    int downY = (int)event.getY();
    super.onTouchEvent(event);

    switch (event.getActionMasked()) {
        case MotionEvent.ACTION_DOWN:
            if (isInOutSideCircle(downX, downY)) {
                isSticking = true;
                positionX = downX;
                positionY = downY;
            }
            break;
        case MotionEvent.ACTION_MOVE:
            if (isSticking) {
                if (isInOutSideCircle(downX, downY)) {
                    positionX = downX;
                    positionY = downY;
                } else {
                    double dist = Math.sqrt((downX - mCenterX) * (downX - mCenterX) + (downY - mCenterY) * (downY - mCenterY));
                    // 格局角度cos来计算,找到最近合适的点.
                    positionX = (int) (((downX - mCenterX) * (mThresholdRadius - mStickRadius)) / dist + mCenterX);
                    positionY = (int) (((downY - mCenterY) * (mThresholdRadius - mStickRadius)) / dist + mCenterY);
                }
            }
            break;
        case MotionEvent.ACTION_UP:
        case MotionEvent.ACTION_CANCEL:
            if (isSticking) {
                isSticking = false;
                invalidate();
            }
            break;
    }

    if (isSticking) {
        invalidate();

        if (listener != null) {
            double dist = Math.sqrt((positionX - mCenterX) * (positionX - mCenterX) + (positionY - mCenterY) * (positionY - mCenterY));
            double angle = Math.acos((positionX-mCenterX)/dist);
            listener.onAngleChanged(angle); // 角度变化通知.[0~pi]
        }
    }
    return true;

已上传GitHub: https://github.com/shuqianlan/weather/tree/master/app/src/main/java/com/ilifesmart/compass

你可能感兴趣的:(自定义万向轮)