记录一个自定View 手势旋转表盘的写法

在这里插入图片描述
其中舵轮A是一个圆形图片,指针是另一个图片

package com.kongqw.rockerlibrary.view;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

import androidx.core.content.res.ResourcesCompat;

import com.test.demo.R;


public class Clockwidget extends View {

    private final String TAG = "ClickWidget";


    private boolean isChecked = false;

    private float mCenterPointX;
    private float mCenterPointY;

    private Paint paint;
    private int height;
    private int width;

    private int radius;

    static final float[] COMMON = {-90, 180};

    private float horizontalX = 0;
    private float horizontalY = 0;
    private final float pointerStartDegrees = COMMON[0];
    private double currentRealAngle = COMMON[1];
    private float pointerShouldRotationDegrees = pointerStartDegrees;
    private Drawable mClockNormal;
    private Drawable mClockPressed;
    private int centerWheelWidth;
    private int centerWheelHeight;
    //private float rotationDegrees = -180f;
    // private float rotationDegrees = 90f;
    // private float rotationDegrees = 0f;


    public Clockwidget(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    private Bitmap aWheelBitmap, aWheelBitmapPressed;
    private Bitmap acurrentWheelBitmap;
    private Bitmap pointerBitmap;


    private void init() {
//        Drawable drawable = ResourcesCompat.getDrawable(getResources(), R.mipmap.a_wheel_normal, null);
//        aWheelBitmap = drawableToBitmap(drawable, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
//        Drawable drawableApressed = ResourcesCompat.getDrawable(getResources(), R.mipmap.a_wheel_pressed, null);
//        aWheelBitmapPressed = drawableToBitmap(drawableApressed, drawableApressed.getIntrinsicWidth(), drawableApressed.getIntrinsicHeight());

        aWheelBitmap = getChildNormalBitmap();
        aWheelBitmapPressed = getChildPressedBitmap();

        acurrentWheelBitmap = aWheelBitmap;

        centerWheelWidth = acurrentWheelBitmap.getWidth();
        centerWheelHeight = acurrentWheelBitmap.getHeight();

        Drawable drawablePointer = ResourcesCompat.getDrawable(getResources(), R.mipmap.ic_wheel_pointer, null);
        pointerBitmap = drawableToBitmap(drawablePointer, (int) (drawablePointer.getIntrinsicHeight() * 0.6), (int) (drawablePointer.getIntrinsicHeight() * 0.6));

        paint = new Paint();
        paint.setColor(getResources().getColor(R.color.black));
        paint.setAntiAlias(true);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(10);
        paint.setTextSize(100);


    }


    /**
     * 安卓drawable转bitmap 并控制大小
     *
     * @param drawable
     * @param width
     * @param height
     * @return
     */
    public static Bitmap drawableToBitmap(Drawable drawable, int width, int height) {
        Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
        drawable.draw(canvas);
        return bitmap;
    }


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        height = getMeasuredHeight();
        width = getMeasuredWidth();
        radius = (int) (Math.min(height, width) * 0.2);
        mainBgRadius = (int) (radius * 1.2);
        mCenterPointX = ((float) width / 2);
        mCenterPointY = ((float) height / 2);
        horizontalY = mCenterPointY;

        mCenterPoint = new Point((int) mCenterPointX, (int) mCenterPointY);
    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        drawMainBg(canvas);
        drawWheel(canvas);
        drawLineAndText(canvas);
        drawPointer(canvas);
    }

    private void drawWheel(Canvas canvas) {
        if (acurrentWheelBitmap != null) {

            int bitmapWidth = acurrentWheelBitmap.getWidth();
            int bitmapHeight = acurrentWheelBitmap.getHeight();

            int viewWidth = getWidth();
            int viewHeight = getHeight();
            int x = (viewWidth - bitmapWidth) / 2;
            int y = (viewHeight - bitmapHeight) / 2;


            canvas.save(); // 保存绘图状态
            canvas.rotate(0, x + bitmapWidth / 2, y + bitmapHeight / 2); // 旋转绘图坐标系
            canvas.drawBitmap(acurrentWheelBitmap, x, y, null); // 绘制Bitmap
            canvas.restore(); // 恢复绘图状态
        }
    }

    /**
     * 绘制指针
     *
     * @param canvas
     */
    private void drawPointer(Canvas canvas) {
        int bitmapWidth = pointerBitmap.getWidth();
        int bitmapHeight = pointerBitmap.getHeight();

        int x = (int) (mCenterPointX - (bitmapWidth / 2));
        int y = (int) (mCenterPointY - radius);

        canvas.save(); // 保存绘图状态
        canvas.rotate(pointerShouldRotationDegrees, mCenterPointX, mCenterPointY); // 旋转绘图坐标系
        canvas.drawBitmap(pointerBitmap, x, y, null); // 绘制Bitmap
        canvas.restore(); // 恢复绘图状态

    }


    /**
     * 绘制刻度和文字
     *
     * @param canvas
     */
    private void drawLineAndText(Canvas canvas) {
        drawScale2(canvas);
        drawScaleText(canvas);
    }

    float hourScaleHeight = 30;
    float minuteScaleHeight = 20;

    private void drawScale2(Canvas canvas) {
        paint.reset();
        paint.setAntiAlias(true);
        paint.setColor(getResources().getColor(R.color.kedu_color));
        paint.setStrokeWidth(2);
        paint.setTextSize(20);

        float cy = mCenterPointY;
        float cx = mCenterPointX;


        float radius = mainBgRadius;

        for (int i = 0; i < 60; i++) {
            double a = i * 6 * Math.PI / 180;

            float startX = (float) (cx + Math.sin(a) * radius);
            float startY = (float) (cy - Math.cos(a) * radius);
            if (i % 5 == 0) {
                float endX = (float) (cx + Math.sin(a) * (radius - hourScaleHeight));
                float endY = (float) (cy - Math.cos(a) * (radius - hourScaleHeight));
                canvas.drawLine(startX, startY, endX, endY, paint);
            } else {
                float endX = (float) (cx + Math.sin(a) * (radius - minuteScaleHeight));
                float endY = (float) (cy - Math.cos(a) * (radius - minuteScaleHeight));
                canvas.drawLine(startX, startY, endX, endY, paint);
            }
        }
    }


    String[] hourTexts = {"90", "60", "30", "0", "-30", "-60", "-90", "-60", "-30", "0", "30", "60"};

    private void drawScaleText(Canvas canvas) {
        float cy = mCenterPointY;
        float cx = mCenterPointX;
        float radius = mainBgRadius;

        paint.reset();
        paint.setTextSize(20);
        paint.setStyle(Paint.Style.FILL);
        paint.setAntiAlias(true);
        paint.setColor(getResources().getColor(R.color.color_wheel_text));
        //文字是从宽度的中间绘制的
        paint.setTextAlign(Paint.Align.CENTER);
        Paint.FontMetrics fontMetrics = paint.getFontMetrics();
        float ascent = fontMetrics.ascent;
        float descent = fontMetrics.descent;


        for (int i = 0; i < 12; i++) {
            float textWidth = paint.measureText(hourTexts[i]);
            double a = i * 30 * Math.PI / 180;
            //hourScaleHeight刻度的长度
            //cx + sina * (radius - hourScaleHeight) 就是刻度点的x坐标,也就是绘制文字的中心点,再减少文字宽度的一半就
            //可以不遮挡了
            //原来的  float baseX = (float) (cx + Math.sin(a) * (radius - hourScaleHeight - textWidth / 2));
            float baseX = (float) (cx + Math.sin(a) * (radius + hourScaleHeight - textWidth / 2));
            //cy - cosa * (radius - hourScaleHeight) 就是刻度点的y坐标,cos在圆的上半部分是正值,下半部分是负值
            //在左右圆是0,光减刻度是不行的,还要减ascent,在12点方向需要减,6点方向
            // 原来的 float baseY = (float) (cy - Math.cos(a) *  (radius - hourScaleHeight - Math.abs(ascent * Math.cos(a))));

            float baseY = (float) (cy - Math.cos(a) * (radius + hourScaleHeight - Math.abs(ascent * Math.cos(a))));


            //baseY == 刻度的y显示会不对齐,所以用中心
            //baseY 向上移动到文字的中心,middle = (|ascent| + descent)/2
            //baseY 到 middle的距离就是 middle - descent
            baseY += ((Math.abs(ascent) - descent) / 2f);
            canvas.drawText(hourTexts[i], baseX, baseY, paint);
        }


    }


    private int mainBgRadius = 0;

    private void drawMainBg(Canvas canvas) {
        paint.reset();
        paint.setAntiAlias(true);
        paint.setColor(getResources().getColor(R.color.color_wheel_bg));
        canvas.drawCircle(mCenterPointX, mCenterPointY, mainBgRadius, paint);
    }


    private Point mCenterPoint;

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        float cWheelStartX = (mCenterPointX - (centerWheelWidth / 2));
        float cWheelStartY = (mCenterPointY - (centerWheelHeight / 2));

        float cWheelEndX = (mCenterPointX + (centerWheelWidth / 2));
        float cWheelEndY = (mCenterPointY + (centerWheelHeight / 2));

        float moveX = event.getX();
        float moveY = event.getY();
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:// 按下
                // 回调 开始
                if (moveX > cWheelStartX && moveX < cWheelEndX &&
                        moveY > cWheelStartY && moveY < cWheelEndY) {
                    isChecked = !isChecked;
                    if (isChecked) {
                        setClockPressed();
                    } else {
                        setClockNormal();
                    }
                }
                if (mAngleChangedListener != null) {
                    mAngleChangedListener.onAngleCheck(isChecked);
                }
                break;
            case MotionEvent.ACTION_MOVE:// 移动
                if (moveX < cWheelStartX || moveX > cWheelEndX || moveY > cWheelEndY || moveY < cWheelStartY) {
                    if (isChecked) {
                        getRockerPositionPoint(mCenterPoint, new Point((int) moveX, (int) moveY), pointerBitmap.getWidth(), mainBgRadius);
                    }
                }
                break;
            case MotionEvent.ACTION_UP:// 抬起

                if (moveX < cWheelStartX || moveX > cWheelEndX || moveY > cWheelEndY || moveY < cWheelStartY) {
                    if (mAngleChangedListener != null) {
                        mAngleChangedListener.onAngleTouchUp(this.angleForUser, isChecked);
                    }
                }

                break;
            case MotionEvent.ACTION_CANCEL:// 移出区域


                break;
        }
        return true;
    }


    /**
     * 获取摇杆实际要显示的位置(点)
     *
     * @param centerPoint  中心点
     * @param touchPoint   触摸点
     * @param regionRadius 摇杆可活动区域半径
     * @param rockerRadius 摇杆半径
     * @return 摇杆实际显示的位置(点)
     */
    private Point getRockerPositionPoint(Point centerPoint, Point touchPoint, float regionRadius, float rockerRadius) {
        // 两点在X轴的距离
        float lenX = (float) (touchPoint.x - centerPoint.x);
        // 两点在Y轴距离
        float lenY = (float) (touchPoint.y - centerPoint.y);
        // 两点距离
        float lenXY = (float) Math.sqrt((double) (lenX * lenX + lenY * lenY));
        // 计算弧度
        double radian = Math.acos(lenX / lenXY) * (touchPoint.y < centerPoint.y ? -1 : 1);
        // 计算角度
        double angle = radian2Angle(radian);
        currentRealAngle = angle;
        Log.e(TAG, "currentRealAngle = " + currentRealAngle);
        setAngel(angle);
        // 回调 返回参数
//        callBack(angle);

//        Log.i(TAG, "getRockerPositionPoint: 角度 :" + angle + "  应该给出的angle = " + (180 - (360 - angle)));

//        if (angle >= 180 && angle < 270) {
//
//            double degreeCallBack = (180 - (360 - angle));
//            pointerShouldRotationDegrees = (float) (pointerStartDegrees + degreeCallBack);
//
//            this.angleForUser = degreeCallBack;
//            //  Log.i(TAG, "getRockerPositionPoint: 角度 :" + angle + "  应该给出的angle = " + degreeCallBack + " pointerShouldRotationDegrees =" + pointerShouldRotationDegrees);
//            invalidate();
//        } else if (angle >= 270 && angle < 360) {
//            //应该给出的是0到90度
//            double degreeCalucate = 90 - (360 - angle);
//            pointerShouldRotationDegrees = (float) (degreeCalucate);
//            double degreeCallBack = 90 - degreeCalucate;
//
//            this.angleForUser = degreeCallBack;
//            //  Log.i(TAG, "getRockerPositionPoint: 角度 :" + angle + "  应该给出的angle = " + degreeCallBack + " pointerShouldRotationDegrees =" + pointerShouldRotationDegrees);
//            invalidate();
//        } else if (angle > 0 && angle <= 90) {
//            pointerShouldRotationDegrees = (float) (90 + angle);
//            double degreeCallBack = -angle;
//            this.angleForUser = degreeCallBack;
//            //   Log.i(TAG, "getRockerPositionPoint: 角度 :" + angle + "  应该给出的angle = " + degreeCallBack + " pointerShouldRotationDegrees =" + pointerShouldRotationDegrees);
//            invalidate();
//        } else {
//            pointerShouldRotationDegrees = (float) (angle - 90 + 180);
//            double degreeCallBack = -(90 - (pointerShouldRotationDegrees - 180));
//            this.angleForUser = degreeCallBack;
//            //   Log.i(TAG, "getRockerPositionPoint: 角度 :" + angle + "  应该给出的angle = " + degreeCallBack + " pointerShouldRotationDegrees =" + pointerShouldRotationDegrees);
//            invalidate();
//        }
//        if (this.angleForUser == -180) {
//            this.angleForUser = 0;
//        }
//        if (mAngleChangedListener != null) {
//            mAngleChangedListener.onAngleChanged(this.angleForUser);
//        }

        if (lenXY + rockerRadius <= regionRadius) { // 触摸位置在可活动范围内
            return touchPoint;
        } else { // 触摸位置在可活动范围以外
            // 计算要显示的位置
            int showPointX = (int) (centerPoint.x + (regionRadius - rockerRadius) * Math.cos(radian));
            int showPointY = (int) (centerPoint.y + (regionRadius - rockerRadius) * Math.sin(radian));
            return new Point(showPointX, showPointY);
        }
    }


    private void setAngel(double angle) {
        if (angle >= 180 && angle < 270) {

            double degreeCallBack = (180 - (360 - angle));
            pointerShouldRotationDegrees = (float) (pointerStartDegrees + degreeCallBack);

            this.angleForUser = degreeCallBack;
            Log.i(TAG, "1  getRockerPositionPoint: 角度 :" + angle + "  应该给出的angle = " + degreeCallBack + " pointerShouldRotationDegrees =" + pointerShouldRotationDegrees);
            invalidate();
        } else if (angle >= 270 && angle < 360) {
            //应该给出的是0到90度
            double degreeCalucate = 90 - (360 - angle);
            pointerShouldRotationDegrees = (float) (degreeCalucate);
            double degreeCallBack = 90 - degreeCalucate;

            this.angleForUser = degreeCallBack;
            Log.i(TAG, "2 getRockerPositionPoint: 角度 :" + angle + "  应该给出的angle = " + degreeCallBack + " pointerShouldRotationDegrees =" + pointerShouldRotationDegrees);
            invalidate();
        } else if (angle > 0 && angle <= 90) {
            pointerShouldRotationDegrees = (float) (90 + angle);
            double degreeCallBack = -angle;
            this.angleForUser = degreeCallBack;
            Log.i(TAG, "3 getRockerPositionPoint: 角度 :" + angle + "  应该给出的angle = " + degreeCallBack + " pointerShouldRotationDegrees =" + pointerShouldRotationDegrees);
            invalidate();
        } else {
            pointerShouldRotationDegrees = (float) (angle - 90 + 180);
            double degreeCallBack = -(90 - (pointerShouldRotationDegrees - 180));
            this.angleForUser = degreeCallBack;
            Log.i(TAG, "4  getRockerPositionPoint: 角度 :" + angle + "  应该给出的angle = " + degreeCallBack + " pointerShouldRotationDegrees =" + pointerShouldRotationDegrees);
            invalidate();
        }
        if (this.angleForUser == -180) {
            this.angleForUser = 0;
        }
        if (mAngleChangedListener != null) {
            mAngleChangedListener.onAngleChanged(this.angleForUser);
        }
    }

    /**
     * 弧度转角度
     *
     * @param radian 弧度
     * @return 角度[0, 360)
     */
    private double radian2Angle(double radian) {
        double tmp = Math.round(radian / Math.PI * 180);
        return tmp >= 0 ? tmp : 360 + tmp;
    }

    public void setClockNormal() {

        acurrentWheelBitmap = aWheelBitmap;
        invalidate();
    }

    public void setClockPressed() {
        acurrentWheelBitmap = aWheelBitmapPressed;
        invalidate();
    }


    public Bitmap getChildNormalBitmap() {
        Drawable drawable = ResourcesCompat.getDrawable(getResources(), R.mipmap.a_wheel_normal, null);
        //return drawableToBitmap(drawable, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
        return ((BitmapDrawable) drawable).getBitmap();
    }

    public Bitmap getChildPressedBitmap() {
        Drawable drawable = ResourcesCompat.getDrawable(getResources(), R.mipmap.a_wheel_pressed, null);
        return ((BitmapDrawable) drawable).getBitmap();
    }


    /**
     * 表盘参数复位
     */
    public void reset() {
        pointerShouldRotationDegrees = pointerStartDegrees;
        this.angleForUser = 0;
        this.isChecked = false;
        invalidate();
        if (mAngleChangedListener != null) {
            mAngleChangedListener.onAngleTouchUp(this.angleForUser, this.isChecked);
        }
        setClockNormal();
    }

    public double angleForUser = 0;


    private OnAngleChangedListener mAngleChangedListener;

    public void setAngleChangedListener(OnAngleChangedListener angleChangedListener) {
        mAngleChangedListener = angleChangedListener;
    }


    public void addAuto() {
        this.currentRealAngle = currentRealAngle + 10;

        double angle = this.currentRealAngle;
        Log.e(TAG, "add auto  " + angle);
        setAngel(angle);
    }

    public void reduceAuto() {
        this.currentRealAngle = currentRealAngle - 10;
        double angle = this.currentRealAngle;
        setAngel(angle);
    }

}

半圆表盘

package com.kongqw.rockerlibrary.view;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

import androidx.core.content.res.ResourcesCompat;

import com.test.demo.R;


public class ClockwidgetHalf extends View {

    private final String TAG = "ClickWidget";


    private boolean isChecked = false;

    private float mCenterPointX;
    private float mCenterPointY;

    private Paint paint;
    private int height;
    private int width;

    private int radius;

    static final float[] COMMON = {-90, 180};

    private float horizontalX = 0;
    private float horizontalY = 0;
    private final float pointerStartDegrees = COMMON[0];
    private double currentRealAngle = COMMON[1];
    private float pointerShouldRotationDegrees = pointerStartDegrees;
   
    private int centerWheelWidth;
    private int centerWheelHeight;



    public ClockwidgetHalf(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    private Bitmap aWheelBitmap, aWheelBitmapPressed;
    private Bitmap acurrentWheelBitmap;
    private Bitmap pointerBitmap;


    private void init() {
//        Drawable drawable = ResourcesCompat.getDrawable(getResources(), R.mipmap.a_wheel_normal, null);
//        aWheelBitmap = drawableToBitmap(drawable, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
//        Drawable drawableApressed = ResourcesCompat.getDrawable(getResources(), R.mipmap.a_wheel_pressed, null);
//        aWheelBitmapPressed = drawableToBitmap(drawableApressed, drawableApressed.getIntrinsicWidth(), drawableApressed.getIntrinsicHeight());

        aWheelBitmap = getChildNormalBitmap();
        aWheelBitmapPressed = getChildPressedBitmap();

        acurrentWheelBitmap = aWheelBitmap;

        centerWheelWidth = acurrentWheelBitmap.getWidth();
        centerWheelHeight = acurrentWheelBitmap.getHeight();

        Drawable drawablePointer = ResourcesCompat.getDrawable(getResources(), R.mipmap.ic_wheel_pointer, null);
        pointerBitmap = drawableToBitmap(drawablePointer, (int) (drawablePointer.getIntrinsicHeight() * 0.6), (int) (drawablePointer.getIntrinsicHeight() * 0.6));

        paint = new Paint();
        paint.setColor(getResources().getColor(R.color.black));
        paint.setAntiAlias(true);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(10);
        paint.setTextSize(100);


    }


    /**
     * 安卓drawable转bitmap 并控制大小
     *
     * @param drawable
     * @param width
     * @param height
     * @return
     */
    public static Bitmap drawableToBitmap(Drawable drawable, int width, int height) {
        Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
        drawable.draw(canvas);
        return bitmap;
    }


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        height = getMeasuredHeight();
        width = getMeasuredWidth();
        radius = (int) (Math.min(height, width) * 0.2);
        mainBgRadius = (int) (radius * 1.2);
        mCenterPointX = ((float) width / 2);
        mCenterPointY = ((float) height / 2);
        horizontalY = mCenterPointY;

        mCenterPoint = new Point((int) mCenterPointX, (int) mCenterPointY);
    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        drawMainBg(canvas);
        drawWheel(canvas);
        drawLineAndText(canvas);
        drawPointer(canvas);
    }

    private void drawWheel(Canvas canvas) {
        if (acurrentWheelBitmap != null) {

            int bitmapWidth = acurrentWheelBitmap.getWidth();
            int bitmapHeight = acurrentWheelBitmap.getHeight();

            int viewWidth = getWidth();
            int viewHeight = getHeight();
            int x = (viewWidth - bitmapWidth) / 2;
            int y = (viewHeight - bitmapHeight) / 2;


            canvas.save(); // 保存绘图状态
            canvas.rotate(0, x + bitmapWidth / 2, y + bitmapHeight / 2); // 旋转绘图坐标系
            canvas.drawBitmap(acurrentWheelBitmap, x, y, null); // 绘制Bitmap
            canvas.restore(); // 恢复绘图状态
        }
    }

    /**
     * 绘制指针
     *
     * @param canvas
     */
    private void drawPointer(Canvas canvas) {
        int bitmapWidth = pointerBitmap.getWidth();
        int bitmapHeight = pointerBitmap.getHeight();

        int x = (int) (mCenterPointX - (bitmapWidth / 2));
        int y = (int) (mCenterPointY - radius);

        canvas.save(); // 保存绘图状态
        canvas.rotate(pointerShouldRotationDegrees, mCenterPointX, mCenterPointY); // 旋转绘图坐标系
        canvas.drawBitmap(pointerBitmap, x, y, null); // 绘制Bitmap
        canvas.restore(); // 恢复绘图状态

    }


    /**
     * 绘制刻度和文字
     *
     * @param canvas
     */
    private void drawLineAndText(Canvas canvas) {
        drawScale2(canvas);
        drawScaleText(canvas);
    }

    float hourScaleHeight = 30;
    float minuteScaleHeight = 20;

    private void drawScale2(Canvas canvas) {
        paint.reset();
        paint.setAntiAlias(true);
        paint.setColor(getResources().getColor(R.color.kedu_color));
        paint.setStrokeWidth(2);
        paint.setTextSize(20);

        float cy = mCenterPointY;
        float cx = mCenterPointX;
        
        float radius = mainBgRadius;
        
        for (int i = 0; i < 60; i++) {
            double a = i * 6 * Math.PI / 180;
            //刻度我们只绘制前30个
            if (i <= 30) {
                continue;
            }
            float startX = (float) (cx + Math.sin(a) * radius);
            float startY = (float) (cy - Math.cos(a) * radius);
            if (i % 5 == 0) {
                float endX = (float) (cx + Math.sin(a) * (radius - hourScaleHeight));
                float endY = (float) (cy - Math.cos(a) * (radius - hourScaleHeight));
                canvas.drawLine(startX, startY, endX, endY, paint);
            } else {
                float endX = (float) (cx + Math.sin(a) * (radius - minuteScaleHeight));
                float endY = (float) (cy - Math.cos(a) * (radius - minuteScaleHeight));
                canvas.drawLine(startX, startY, endX, endY, paint);
            }
        }


    }


    String[] hourTexts = {"","", "", "", "", "", "-90", "-60", "-30", "0", "30", "60", "90"};

    private void drawScaleText(Canvas canvas) {
        float cy = mCenterPointY;
        float cx = mCenterPointX;
        float radius = mainBgRadius;

        paint.reset();
        paint.setTextSize(20);
        paint.setStyle(Paint.Style.FILL);
        paint.setAntiAlias(true);
        paint.setColor(getResources().getColor(R.color.color_wheel_text));
        //文字是从宽度的中间绘制的
        paint.setTextAlign(Paint.Align.CENTER);
        Paint.FontMetrics fontMetrics = paint.getFontMetrics();
        float ascent = fontMetrics.ascent;
        float descent = fontMetrics.descent;


        for (int i = 0; i < 13; i++) {
            float textWidth = paint.measureText(hourTexts[i]);
            double a = i * 30 * Math.PI / 180;
            //hourScaleHeight刻度的长度
            //cx + sina * (radius - hourScaleHeight) 就是刻度点的x坐标,也就是绘制文字的中心点,再减少文字宽度的一半就
            //可以不遮挡了
            //原来的  float baseX = (float) (cx + Math.sin(a) * (radius - hourScaleHeight - textWidth / 2));
            float baseX = (float) (cx + Math.sin(a) * (radius + hourScaleHeight - textWidth / 2));
            //cy - cosa * (radius - hourScaleHeight) 就是刻度点的y坐标,cos在圆的上半部分是正值,下半部分是负值
            //在左右圆是0,光减刻度是不行的,还要减ascent,在12点方向需要减,6点方向
            // 原来的 float baseY = (float) (cy - Math.cos(a) *  (radius - hourScaleHeight - Math.abs(ascent * Math.cos(a))));

            float baseY = (float) (cy - Math.cos(a) * (radius + hourScaleHeight - Math.abs(ascent * Math.cos(a))));


            //baseY == 刻度的y显示会不对齐,所以用中心
            //baseY 向上移动到文字的中心,middle = (|ascent| + descent)/2
            //baseY 到 middle的距离就是 middle - descent
            baseY += ((Math.abs(ascent) - descent) / 2f);
            canvas.drawText(hourTexts[i], baseX, baseY, paint);
        }


    }


    private int mainBgRadius = 0;

    private void drawMainBg(Canvas canvas) {
        paint.reset();
        paint.setAntiAlias(true);
        paint.setColor(getResources().getColor(R.color.color_wheel_bg));
//        canvas.drawCircle(mCenterPointX, mCenterPointY, mainBgRadius, paint);
        float startX = mCenterPointX - mainBgRadius;
        float startY = mCenterPointY - mainBgRadius;
        float endX = mCenterPointX + mainBgRadius;
        float endY = mCenterPointY + mainBgRadius;
        canvas.drawArc(startX, startY, endX, endY, 90, 180, true, paint);
    }


    private Point mCenterPoint;

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        float cWheelStartX = (mCenterPointX - (centerWheelWidth / 2));
        float cWheelStartY = (mCenterPointY - (centerWheelHeight / 2));

        float cWheelEndX = (mCenterPointX + (centerWheelWidth / 2));
        float cWheelEndY = (mCenterPointY + (centerWheelHeight / 2));

        float moveX = event.getX();
        float moveY = event.getY();
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:// 按下
                // 回调 开始
                if (moveX > cWheelStartX && moveX < cWheelEndX &&
                        moveY > cWheelStartY && moveY < cWheelEndY) {
                    isChecked = !isChecked;
                    if (isChecked) {
                        setClockPressed();
                    } else {
                        setClockNormal();
                    }
                }
                if (mAngleChangedListener != null) {
                    mAngleChangedListener.onAngleCheck(isChecked);
                }
                break;
            case MotionEvent.ACTION_MOVE:// 移动
                if (moveX < cWheelStartX || moveX > cWheelEndX || moveY > cWheelEndY || moveY < cWheelStartY) {
                    if (isChecked) {
                        getRockerPositionPoint(mCenterPoint, new Point((int) moveX, (int) moveY), pointerBitmap.getWidth(), mainBgRadius);
                    }
                }
                break;
            case MotionEvent.ACTION_UP:// 抬起

                if (moveX < cWheelStartX || moveX > cWheelEndX || moveY > cWheelEndY || moveY < cWheelStartY) {
                    if (mAngleChangedListener != null) {
                        mAngleChangedListener.onAngleTouchUp(this.angleForUser, isChecked);
                    }
                }

                break;
            case MotionEvent.ACTION_CANCEL:// 移出区域


                break;
        }
        return true;
    }


    /**
     * 获取摇杆实际要显示的位置(点)
     *
     * @param centerPoint  中心点
     * @param touchPoint   触摸点
     * @param regionRadius 摇杆可活动区域半径
     * @param rockerRadius 摇杆半径
     * @return 摇杆实际显示的位置(点)
     */
    private Point getRockerPositionPoint(Point centerPoint, Point touchPoint, float regionRadius, float rockerRadius) {
        // 两点在X轴的距离
        float lenX = (float) (touchPoint.x - centerPoint.x);
        // 两点在Y轴距离
        float lenY = (float) (touchPoint.y - centerPoint.y);
        // 两点距离
        float lenXY = (float) Math.sqrt((double) (lenX * lenX + lenY * lenY));
        // 计算弧度
        double radian = Math.acos(lenX / lenXY) * (touchPoint.y < centerPoint.y ? -1 : 1);
        // 计算角度
        double angle = radian2Angle(radian);
        currentRealAngle = angle;
        Log.e(TAG, "currentRealAngle = " + currentRealAngle);
        setAngel(angle);


        if (lenXY + rockerRadius <= regionRadius) { // 触摸位置在可活动范围内
            return touchPoint;
        } else { // 触摸位置在可活动范围以外
            // 计算要显示的位置
            int showPointX = (int) (centerPoint.x + (regionRadius - rockerRadius) * Math.cos(radian));
            int showPointY = (int) (centerPoint.y + (regionRadius - rockerRadius) * Math.sin(radian));
            return new Point(showPointX, showPointY);
        }
    }


    private void setAngel(double angle) {
        
        if (angle >= 180 && angle <= 270) {//使用

            double degreeCallBack = (180 - (360 - angle));
            pointerShouldRotationDegrees = (float) (pointerStartDegrees + degreeCallBack);

            this.angleForUser = degreeCallBack;
            Log.i(TAG, "1  getRockerPositionPoint: 角度 :" + angle + "  应该给出的angle = " + degreeCallBack + " pointerShouldRotationDegrees =" + pointerShouldRotationDegrees);
            invalidate();
        }else if(angle>=90 && angle<=180){
            pointerShouldRotationDegrees = (float) (angle - 90 + 180);//使用
            double degreeCallBack = -(90 - (pointerShouldRotationDegrees - 180));
            this.angleForUser = degreeCallBack;
            Log.i(TAG, "4  getRockerPositionPoint: 角度 :" + angle + "  应该给出的angle = " + degreeCallBack + " pointerShouldRotationDegrees =" + pointerShouldRotationDegrees);
            invalidate();
        }

        if (this.angleForUser == -180) {
            this.angleForUser = 0;
        }
        if (mAngleChangedListener != null) {
            mAngleChangedListener.onAngleChanged(this.angleForUser);
        }
    }

    /**
     * 弧度转角度
     *
     * @param radian 弧度
     * @return 角度[0, 360)
     */
    private double radian2Angle(double radian) {
        double tmp = Math.round(radian / Math.PI * 180);
        return tmp >= 0 ? tmp : 360 + tmp;
    }

    public void setClockNormal() {

        acurrentWheelBitmap = aWheelBitmap;
        invalidate();
    }

    public void setClockPressed() {
        acurrentWheelBitmap = aWheelBitmapPressed;
        invalidate();
    }


    public Bitmap getChildNormalBitmap() {
        Drawable drawable = ResourcesCompat.getDrawable(getResources(), R.mipmap.a_wheel_normal, null);
        return ((BitmapDrawable) drawable).getBitmap();
    }

    public Bitmap getChildPressedBitmap() {
        Drawable drawable = ResourcesCompat.getDrawable(getResources(), R.mipmap.a_wheel_pressed, null);
        return ((BitmapDrawable) drawable).getBitmap();
    }


    /**
     * 表盘参数复位
     */
    public void reset() {
        pointerShouldRotationDegrees = pointerStartDegrees;
        this.angleForUser = 0;
        this.isChecked = false;
        invalidate();
        if (mAngleChangedListener != null) {
            mAngleChangedListener.onAngleTouchUp(this.angleForUser, this.isChecked);
        }
        setClockNormal();
    }

    public double angleForUser = 0;


    private OnAngleChangedListener mAngleChangedListener;

    public void setAngleChangedListener(OnAngleChangedListener angleChangedListener) {
        mAngleChangedListener = angleChangedListener;
    }


    public void addAuto() {
        this.currentRealAngle = currentRealAngle + 10;
        double angle = this.currentRealAngle;
        setAngel(angle);
    }

    public void reduceAuto() {
        this.currentRealAngle = currentRealAngle - 10;
        double angle = this.currentRealAngle;
        setAngel(angle);
    }

}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值