【Android】自定义环形菜单View

实现的效果图:六个小图片可以跟随手指滑动绕中心点旋转

代码:

package com.example.test_canvas;

import java.util.ArrayList;
import java.util.List;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffXfermode;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceHolder.Callback;
import android.view.SurfaceView;

public class MyView extends SurfaceView {
    private final String TAG = "MyView";
    private final int WHAT_MOVE = 0X013;
    private final int WHAT_CLICK = 0X014;
    private final int TIME_CLICK_INTERVAL = 200;// 按下抬起的间隔时间
    //
    private Handler mThreadHandler = null;
    MyDrawableThread mThread = null;
    private MyViewClickListener mListener = null;
    private int Radius = 0;// 半径
    Bitmap[] mItems = null;
    private int viewWidth = 0;
    private int viewHeight = 0;
    private int centerX = 0;
    private int centerY = 0;
    private List<Point> mPoints = new ArrayList<Point>();
    private Handler mMainHandler = null;

    public MyView(Context context, AttributeSet attrs) { 
        super(context, attrs);
        mMainHandler = new Handler(context.getMainLooper());
        SurfaceHolder holder = this.getHolder();
        holder.addCallback(mCallback);
        mThread = new MyDrawableThread(holder);
        mThread.start();
    }

    /**
     * 设置图片
     *
     * @param items
     */
    public void setItem(Bitmap[] items, MyViewClickListener listener) {
        Log.d(TAG, "--->setItem");
        if (items != null) {
            mItems = items;
        }
        mListener = listener;
    }

    float x = 0;
    float y = 0;
    private float mBuildDegree = 0;
    private long time_start = 0;

    @Override
    public boolean onTouchEvent(MotionEvent event) { 
        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            Log.d(TAG, "ACTION_DOWN");
            x = event.getX();
            y = event.getY();
            time_start = System.currentTimeMillis();
            break;
        case MotionEvent.ACTION_UP: {
            Log.d(TAG, "ACTION_UP");
            float dstx = event.getX();
            float dsty = event.getY();
            if ((System.currentTimeMillis() - time_start) <= TIME_CLICK_INTERVAL) {
                Log.d(TAG, "--->TIME_CLICK_INTERVAL");
                mThreadHandler.sendMessage(mThreadHandler.obtainMessage(
                        WHAT_CLICK, (int) dstx, (int) dsty));
            }
            time_start = 0;
            x = 0;
            y = 0;
            break;

        }
        case MotionEvent.ACTION_MOVE:
            Log.d(TAG, "ACTION_MOVE");
            float dstx = event.getX();
            float dsty = event.getY();

            Log.d(TAG, "src x: " + x + "y: " + y);
            Log.d(TAG, "dst x: " + dstx + "y: " + dsty);
            float degree = 0;
            degree = (float) getActionDegrees(centerX, centerY, dstx, dsty, x,
                    y);
            Log.d(TAG, "degree: " + degree);
            x = dstx;
            y = dsty;
            mBuildDegree -= degree;
            mThreadHandler.sendMessage(mThreadHandler.obtainMessage(WHAT_MOVE,
                    mBuildDegree));
            break;
        default:
            break;
        }
        return true;

    }

    private class MyDrawableThread extends Thread {
        private SurfaceHolder mHolder;

        public MyDrawableThread(SurfaceHolder holder) {
            mHolder = holder;
        }

        @Override
        public void run() {
            Log.d(TAG, "--->MyThread run");
            Looper.prepare();
            mThreadHandler = new Handler() {
                public void handleMessage(Message msg) {
                    switch (msg.what) {
                    case WHAT_MOVE:
                        float degree = (Float) msg.obj;
                        drawImgByRoate(degree);
                        break;
                    case WHAT_CLICK:
                        if (mListener != null && mItems != null
                                && mItems.length > 0 && mItems[0] != null) {
                            int dstx = msg.arg1;
                            int dsty = msg.arg2;
                            Bitmap firstBitmap = mItems[0];
                            int interval = (firstBitmap.getHeight() > firstBitmap
                                    .getWidth()) ? firstBitmap.getWidth() / 2
                                    : firstBitmap.getHeight() / 2;
                            Log.d(TAG, "--->TIME_CLICK_INTERVAL tempdistance: "
                                    + interval);
                            if (mPoints != null) {
                                for (int i = 0; i < mPoints.size(); ++i) {
                                    Point point = mPoints.get(i);
                                    double tempx = Math.abs(point.x - dstx);
                                    double tempy = Math.abs(point.y - dsty);
                                    double distance = Math.sqrt(tempx * tempx
                                            + tempy * tempy);
                                    Log.d(TAG, "--->TIME_CLICK_INTERVAL for: "
                                            + i + "  " + distance);
                                    if (distance <= interval) {
                                        final int index = i;
                                        mMainHandler.post(new Runnable() {

                                            @Override
                                            public void run() {
                                                Log.d(TAG,
                                                        "--->TIME_CLICK_INTERVAL onItemClick: "
                                                                + index);
                                                mListener.onItemClick(index);
                                            }
                                        });
                                        break;
                                    }
                                }
                            }
                        }
                        break;
                    default:
                        super.handleMessage(msg);
                    }
                }
            };
            Looper.loop();
        }

        private void drawImgByRoate(float degree) {
            Log.d(TAG, "mdegree>> " + degree);
            Canvas canvas = null;
            Paint paint = new Paint();
            int count = mItems.length;
            try {
                synchronized (mHolder) {
                    canvas = mHolder.lockCanvas();
                    clear(canvas);
                    mPoints.clear();
                    for (int i = 0; i < count; i++) {
                        Bitmap bitmap = mItems[i];
                        // 计算x,y
                        float reaote = degree + (360 / count) * i;
                        Log.d(TAG, i + "  >> " + reaote);
                        float x = (float) (Radius
                                * Math.cos(Math.toRadians(reaote)) + centerX);
                        float y = (float) (Radius
                                * Math.sin(Math.toRadians(reaote)) + centerY);
                        Log.d(TAG, "x>> " + x);
                        Log.d(TAG, "y>> " + y);
                        // draw
                        int tempX = (int) (x - bitmap.getWidth() / 2);
                        int tempY = (int) (y - bitmap.getHeight() / 2);
                        mPoints.add(new Point((int) x, (int) y)); 
                        canvas.drawBitmap(bitmap, tempX, tempY, paint);
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (canvas != null) {
                    Log.d(TAG, "--->unlockCanvasAndPost");
                    mHolder.unlockCanvasAndPost(canvas);// 结束锁定画图,并提交改变。
                }
            }
        }

        private void clear(Canvas canvas) {
            Paint paint = new Paint();
            paint.setXfermode(new PorterDuffXfermode(Mode.CLEAR));
            canvas.drawPaint(paint);
            paint.setXfermode(new PorterDuffXfermode(Mode.SRC));
        }
    };

    SurfaceHolder.Callback mCallback = new Callback() {

        @Override
        public void surfaceDestroyed(SurfaceHolder holder) {
            // TODO Auto-generated method stub
            Log.d(TAG, "--->surfaceDestroyed");
        }

        @Override
        public void surfaceCreated(SurfaceHolder holder) {
            // TODO Auto-generated method stub
            Log.d(TAG, "--->surfaceCreated");
            viewWidth = getWidth();
            viewHeight = getHeight();
            Radius = (viewWidth / 2) - 100;
            Log.d(TAG, "viewWidth: " + viewWidth);
            Log.d(TAG, "viewHeight: " + viewHeight);
            centerX = viewWidth / 2;
            centerY = viewHeight / 2;
            mThreadHandler.sendMessage(mThreadHandler.obtainMessage(WHAT_MOVE,
                    0f));
        }

        @Override
        public void surfaceChanged(SurfaceHolder holder, int format, int width,
                int height) {
            Log.d(TAG, "--->surfaceChanged");
        }
    };

    public void release() {
        if (mItems != null) {
            for (Bitmap bitmap : mItems) {
                bitmap = null;
            }
        }
        mItems = null;
        mListener = null;
    }

    /**
     * 获取两点到第三点的夹角。
     *
     * @param centerX
     * @param centerY
     * @param nowX
     * @param nowY
     * @param oldX
     * @param oldY
     * @return
     */
    private double getActionDegrees(float centerX, float centerY, float nowX,
            float nowY, float oldX, float oldY) {

        double a = Math.sqrt((nowX - oldX) * (nowX - oldX) + (nowY - oldY)
                * (nowY - oldY));
        double b = Math.sqrt((centerX - oldX) * (centerX - oldX)
                + (centerY - oldY) * (centerY - oldY));
        double c = Math.sqrt((nowX - centerX) * (nowX - centerX)
                + (nowY - centerY) * (nowY - centerY));
        // 余弦定理
        double cosA = (b * b + c * c - a * a) / (2 * b * c);
        // 返回余弦值为指定数字的角度,Math函数为我们提供的方法
        double arcA = Math.acos(cosA);
        double degree = arcA * 180 / Math.PI;

        // 接下来我们要讨论正负值的关系了,也就是求出是顺时针还是逆时针。
        // 第1、2象限
        if (nowY < centerY && oldY < centerY) {
            if (nowX < centerX && oldX > centerX) {// 由2象限向1象限滑动
                return degree;
            }
            // 由1象限向2象限滑动
            else if (nowX >= centerX && oldX <= centerX) {
                return -degree;
            }
        }
        // 第3、4象限
        if (nowY > centerY && oldY > centerY) {
            // 由3象限向4象限滑动
            if (nowX < centerX && oldX > centerX) {
                return -degree;
            }
            // 由4象限向3象限滑动
            else if (nowX > centerX && oldX < centerX) {
                return degree;
            }

        }
        // 第2、3象限
        if (nowX < centerX && oldX < centerX) {
            // 由2象限向3象限滑动
            if (nowY < centerY && oldY > centerY) {
                return -degree;
            }
            // 由3象限向2象限滑动
            else if (nowY > centerY && oldY < centerY) {
                return degree;
            }
        }
        // 第1、4象限
        if (nowX > centerX && oldX > centerX) {
            // 由4向1滑动
            if (nowY > centerY && oldY < centerY) {
                return -degree;
            }
            // 由1向4滑动
            else if (nowY < centerY && oldY > centerY) {
                return degree;
            }
        }

        // 在特定的象限内
        float tanB = (nowY - centerY) / (nowX - centerX); 
        float tanC = (oldY - centerY) / (oldX - centerX);
        if ((nowX > centerX && nowY > centerY && oldX > centerX
                && oldY > centerY && tanB > tanC)// 第一象限
                || (nowX > centerX && nowY < centerY && oldX > centerX
                        && oldY < centerY && tanB > tanC)// 第四象限
                || (nowX < centerX && nowY < centerY && oldX < centerX
                        && oldY < centerY && tanB > tanC)// 第三象限
                || (nowX < centerX && nowY > centerY && oldX < centerX
                        && oldY > centerY && tanB > tanC))// 第二象限
            return -degree;
        return degree;
    }

    public interface MyViewClickListener {
        public void onItemClick(int itemIndex);
    }
}
原文地址:https://www.cnblogs.com/afluy/p/4324430.html