zoukankan      html  css  js  c++  java
  • 【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);
        }
    }
  • 相关阅读:
    【LeetCode】在排序数组中查找元素的第一个和最后一个位置
    【LeetCode】搜索旋转排序数组
    【LeetCode】组合总和
    【LeetCode】电话号码的字母组合
    【LeetCode】对称二叉树
    【LeetCode】验证二叉搜索树
    【LeetCode】分发糖果
    Go学习笔记
    mybatis
    redis
  • 原文地址:https://www.cnblogs.com/afluy/p/4324430.html
Copyright © 2011-2022 走看看