android 方向控件 MenuView 上下左右,东南西北

该博客详细介绍了如何在Android中实现一个自定义的圆形菜单视图,包括中心按钮和围绕中心的多个扇形菜单。每个扇形菜单可以设置背景颜色、描边颜色、点击事件等属性,并且提供了点击反馈和动画效果。示例代码展示了具体实现方法和布局绘制过程。
摘要由CSDN通过智能技术生成


参考代码   原文链接:https://blog.csdn.net/qq_29194615/java/article/details/105809980

控件样式:

 源代码:RoundMenuView .java

package com.codecandy.views;

import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.RectF;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Toast;

import com.codecandy.utils.R;

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

/**
 * @Description: 圆盘控制台
 * @Author: liubing
 * @CreateDate: 2020/05/22 9:06
 * @UpdateUser: 更新者
 * @UpdateDate: 2020/05/22 9:06
 * @UpdateRemark: 更新说明
 * @Version: 1.0
 */
public class RoundMenuView extends View {

    public final static String PRESS_DOWN="down";
    public final static String PRESS_UP="up";
    /**
     * 变量
     */
    private int coreX;//中心点的坐标X
    private int coreY;//中心点的坐标Y

    private List<RoundMenu> roundMenus;//菜单列表

    private boolean isCoreMenu = false;//是否有中心按钮
    private int coreMenuColor;//中心按钮的默认背景--最好不要透明色
    private int coreMenuStrokeColor;//中心按钮描边颜色
    private int coreMenuStrokeSize;//中心按钮描边粗细
    private int coreMenuSelectColor;//中心按钮选中时的背景颜色
    private Bitmap coreBitmap;//OK图片
    private OnClickListener onCoreClickListener;//中心按钮的点击回调

    private OnMenuViewClickListener onMenuViewClickListener;

    private float deviationDegree;//偏移角度
    private int onClickState = -2;//-2是无点击,-1是点击中心圆,其他是点击菜单
    private int roundRadius;//中心圆的半径
    private double radiusDistance;//半径的长度比(中心圆半径=大圆半径*radiusDistance)
    private long touchTime;//按下时间,抬起的时候判定一下,超过300毫秒算点击
    private boolean mEnableClick = true;
    private boolean isPermission = false;
    private int rotate_flag = -1;//正在旋转的方向 -1代表没有旋转  //1左, 2上, 3右, 4下
    private Toast toast;
    private String clickEvent;

    public void setClickEvent(String clickEvent) {
        this.clickEvent = clickEvent;
    }

    public String getClickEvent() {
        return clickEvent;
    }

    public void setPermission(boolean permission) {
        isPermission = permission;
    }

    public boolean ismEnableClick() {
        return mEnableClick;
    }

    public void setmEnableClick(boolean mEnableClick) {
        this.mEnableClick = mEnableClick;
    }

    public void setRotateFlag(int flag) {
        rotate_flag = flag;
        invalidate();
    }

    public int getRotateFlag() {
        return rotate_flag;
    }

    public RoundMenuView(Context context) {
        super(context);
        initView();
    }

    public RoundMenuView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initView();
    }

    public RoundMenuView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        initView();
    }


    @Override
    protected void onDraw(Canvas canvas) {
        coreX = getWidth() / 2;
        coreY = getHeight() / 2;
        roundRadius = (int) (getWidth() / 2 * radiusDistance);//计算中心圆圈半径

        int padding = dp2px(10f);
        int paddoutside = dp2px(12f);

        RectF rect = new RectF(padding, padding, getWidth() - padding, getHeight() - padding);

        if (roundMenus != null && roundMenus.size() > 0) {
            float sweepAngle = 360 / roundMenus.size();//每个弧形的角度
            deviationDegree = sweepAngle / 2;//其实的偏移角度,如果4个扇形的时候是X形状,而非+,设为0试试就知道什么意思了
            for (int i = 0; i < roundMenus.size(); i++) {
                RoundMenu roundMenu = roundMenus.get(i);
                //填充
                Paint paint = new Paint();
                paint.setAntiAlias(true);
                if (onClickState == i) {
                    //选中
                    paint.setColor(roundMenu.selectSolidColor);
                } else {
                    //未选中
                    paint.setColor(roundMenu.solidColor);
                }
//               画点击效果
                canvas.drawArc(rect, deviationDegree + (i * sweepAngle), sweepAngle, true, paint);
                //画扇形描边
                paint = new Paint();
                paint.setAntiAlias(true);
                paint.setStrokeWidth(roundMenu.strokeSize);
                paint.setStyle(Paint.Style.STROKE);
                paint.setColor(roundMenu.strokeColor);
                canvas.drawArc(rect, deviationDegree + (i * sweepAngle), sweepAngle, roundMenu.useCenter, paint);
                paint = new Paint();
                paint.setStrokeWidth(dp2px(1f));
                paint.setStyle(Paint.Style.STROKE);
                paint.setColor(Color.parseColor("#03A9F4"));
                //画大圆
                canvas.drawArc(rect, 0, 360, false, paint);
                //画扇形图案
                Matrix matrix = new Matrix();
                matrix.postTranslate((float) ((coreX + getWidth() / 2 * roundMenu.iconDistance) - (roundMenu.icon.getWidth() / 2)), coreY - (roundMenu.icon.getHeight() / 2));
                if (rotate_flag == i) {
                    matrix.postTranslate(-dp2px(10), -dp2px(10));
                    matrix.postRotate(((i + 1) * sweepAngle), coreX, coreY);
                    canvas.drawBitmap(roundMenu.stop_icon, matrix, null);
                } else {
                    matrix.postRotate(((i + 1) * sweepAngle), coreX, coreY);
                    canvas.drawBitmap(roundMenu.icon, matrix, null);
                }
//                paint = new Paint();
//                paint.setAntiAlias(true);
//                paint.setStyle(Paint.Style.STROKE);
//                paint.setColor(Color.WHITE);
//                paint.setStrokeWidth(dp2px(2f));
//                canvas.drawArc(rect, 0, 360, false, paint);
            }
        }
        //画中心圆圈
        if (isCoreMenu) {
            //填充
            RectF rect1 = new RectF(coreX - roundRadius, coreY - roundRadius, coreX + roundRadius, coreY + roundRadius);
            Paint paint = new Paint();
            paint.setAntiAlias(true);
            paint.setStrokeWidth(coreMenuStrokeSize);
            if (onClickState == -1) {
                paint.setColor(coreMenuSelectColor);
            } else {
                paint.setColor(coreMenuColor);
            }
            canvas.drawArc(rect1, 0, 360, true, paint);
            //内圆环
//            paint = new Paint();
//            paint.setAntiAlias(true);
//            paint.setStrokeWidth(dp2px(7));
//            paint.setStyle(Paint.Style.STROKE);
//            paint.setColor(Color.parseColor("#FFFFFF"));
//            canvas.drawArc(rect1, 0, 360, true, paint);
            //画描边
            paint = new Paint();
            paint.setAntiAlias(true);
            paint.setStrokeWidth(coreMenuStrokeSize);
            paint.setStyle(Paint.Style.STROKE);
            paint.setColor(coreMenuStrokeColor);
            canvas.drawArc(rect1, 0, 360, true, paint);
            if (coreBitmap != null) {
                //画中心圆圈的“OK”图标
                canvas.drawBitmap(coreBitmap, coreX - coreBitmap.getWidth() / 2, coreY - coreBitmap.getHeight() / 2, null);//在 0,0坐标开始画入src
            }
        }
    }

    public boolean onTouchEvent(MotionEvent event) {
        OnClickListener onClickListener = null;
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                touchTime = new Date().getTime();
                float textX = event.getX();
                float textY = event.getY();
                int distanceLine = (int) getDisForTwoSpot(coreX, coreY, textX, textY);//距离中心点之间的直线距离
                if (distanceLine <= roundRadius) {
                    //点击的是中心圆;按下点到中心点的距离小于中心园半径,那就是点击中心园了
                    onClickState = -1;
                    onClickListener = onCoreClickListener;
                } else if (distanceLine <= getWidth() / 2) {
                    //点击的是某个扇形;按下点到中心点的距离大于中心圆半径小于大圆半径,那就是点击某个扇形了
                    float sweepAngle = 360 / roundMenus.size();//每个弧形的角度
                    int angle = getRotationBetweenLines(coreX, coreY, textX, textY);
                    //这个angle的角度是从正Y轴开始,而我们的扇形是从正X轴开始,再加上偏移角度,所以需要计算一下
                    angle = (angle + 360 - 90 - (int) deviationDegree) % 360;
                    onClickState = (int) (angle / sweepAngle);//根据角度得出点击的是那个扇形
                    onClickListener = roundMenus.get(onClickState).onClickListener;
                } else {
                    //点击了外面
                    onClickState = -2;
                }
                if (onClickListener != null) {
                    clickEvent = PRESS_DOWN;
                    onClickListener.onClick(this);
                }
                if (rotate_flag == -1 || (rotate_flag != -1 && rotate_flag == onClickState)) {
                    invalidate();
                }
                break;
            case MotionEvent.ACTION_UP:
                if (onClickState == -1) {
                    onClickListener = onCoreClickListener;
                } else if (onClickState >= 0 && onClickState < roundMenus.size()) {
                    onClickListener = roundMenus.get(onClickState).onClickListener;
                }
                if (onClickListener != null) {
                    clickEvent = PRESS_UP;
                    onClickListener.onClick(this);
                }
                onClickState = -2;
                invalidate();
                break;
        }
        return true;
    }

    /**
     * 添加菜单
     *
     * @param roundMenu
     */
    public void addRoundMenu(RoundMenu roundMenu) {
        if (roundMenu == null) {
            return;
        }
        if (roundMenus == null) {
            roundMenus = new ArrayList<RoundMenu>();
        }
        roundMenus.add(roundMenu);
        invalidate();
    }

    /**
     * 添加中心菜单按钮
     *
     * @param coreMenuColor
     * @param coreMenuSelectColor
     * @param onClickListener
     */
    public void setCoreMenu(int coreMenuColor, int coreMenuSelectColor, int coreMenuStrokeColor,
                            int coreMenuStrokeSize, double radiusDistance, Bitmap bitmap, OnClickListener onClickListener) {
        isCoreMenu = true;
        this.coreMenuColor = coreMenuColor;
        this.coreMenuSelectColor = coreMenuSelectColor;
        this.coreMenuStrokeColor = coreMenuStrokeColor;
        this.coreMenuStrokeSize = coreMenuStrokeSize;
        this.radiusDistance = radiusDistance;
        coreBitmap = bitmap;
        this.onCoreClickListener = onClickListener;
        invalidate();
    }

    public void setOnMenuViewClickListener(OnMenuViewClickListener onMenuViewClickListener) {
        this.onMenuViewClickListener = onMenuViewClickListener;
    }

    /**
     * 获取两条线的夹角
     *
     * @param centerX
     * @param centerY
     * @param xInView
     * @param yInView
     * @return
     */
    public static int getRotationBetweenLines(float centerX, float centerY, float xInView, float yInView) {
        double rotation = 0;

        double k1 = (double) (centerY - centerY) / (centerX * 2 - centerX);
        double k2 = (double) (yInView - centerY) / (xInView - centerX);
        double tmpDegree = Math.atan((Math.abs(k1 - k2)) / (1 + k1 * k2)) / Math.PI * 180;

        if (xInView > centerX && yInView < centerY) {  //第一象限
            rotation = 90 - tmpDegree;
        } else if (xInView > centerX && yInView > centerY) //第二象限
        {
            rotation = 90 + tmpDegree;
        } else if (xInView < centerX && yInView > centerY) { //第三象限
            rotation = 270 - tmpDegree;
        } else if (xInView < centerX && yInView < centerY) { //第四象限
            rotation = 270 + tmpDegree;
        } else if (xInView == centerX && yInView < centerY) {
            rotation = 0;
        } else if (xInView == centerX && yInView > centerY) {
            rotation = 180;
        }
        return (int) rotation;
    }

    /**
     * 求两个点之间的距离
     *
     * @return
     */
    public static double getDisForTwoSpot(float x1, float y1, float x2, float y2) {
        float width, height;
        if (x1 > x2) {
            width = x1 - x2;
        } else {
            width = x2 - x1;
        }

        if (y1 > y2) {
            height = y2 - y1;
        } else {
            height = y2 - y1;
        }
        return Math.sqrt((width * width) + (height * height));
    }

    /**
     * 扇形的对象类
     */
    public static class RoundMenu {
        public boolean useCenter = true;//扇形是否画连接中心点的直线
        public int solidColor = 0x00000000;//背景颜色,默认透明
        public int selectSolidColor = 0x00000000;//背景颜色,默认透明
        public int strokeColor = 0xFF03A9F4;//描边颜色,默认透明
        public int strokeSize = 2;//描边的宽度,默认1
        public Bitmap icon;//菜单的图片
        public Bitmap stop_icon;//菜单的图片正在操作
        public OnClickListener onClickListener;//点击监听
        public double iconDistance = 0.6;//图标距离中心点的距离
    }

    /**
     * 根据手机的分辨率从 dp 的单位 转成为 px(像素)
     */
    public static int dp2px(float dpValue) {
        return (int) (0.5f + dpValue * Resources.getSystem().getDisplayMetrics().density);
    }

    private void initView() {

        RoundMenuView.RoundMenu roundMenu = new RoundMenuView.RoundMenu();
        roundMenu.selectSolidColor = Color.GRAY;
//        roundMenu.icon = drawable2Bitmap(getResources().getDrawable(R.drawable.shape_dian));
        roundMenu.icon = drawable2Bitmap2(getResources().getDrawable(R.mipmap.ic_arrow_right_blue));
        roundMenu.stop_icon = drawable2Bitmap2(getResources().getDrawable(R.drawable.ic_blue_dort));
        roundMenu.onClickListener = new OnClickListener() {
            @Override
            public void onClick(View view) {
                if (onMenuViewClickListener != null)
                    onMenuViewClickListener.OnMenuViewClick(view, clickEvent, 4);
            }
        };
        addRoundMenu(roundMenu);

        roundMenu = new RoundMenuView.RoundMenu();
        roundMenu.selectSolidColor = Color.GRAY;
        roundMenu.icon = drawable2Bitmap2(getResources().getDrawable(R.mipmap.ic_arrow_right_blue));
        roundMenu.stop_icon = drawable2Bitmap2(getResources().getDrawable(R.drawable.ic_blue_dort));
        roundMenu.onClickListener = new OnClickListener() {
            @Override
            public void onClick(View view) {
                if (onMenuViewClickListener != null)
                    onMenuViewClickListener.OnMenuViewClick(view, clickEvent, 3);
            }
        };
        addRoundMenu(roundMenu);

        roundMenu = new RoundMenuView.RoundMenu();
        roundMenu.selectSolidColor = Color.GRAY;
        roundMenu.icon = drawable2Bitmap2(getResources().getDrawable(R.mipmap.ic_arrow_right_blue));
        roundMenu.stop_icon = drawable2Bitmap2(getResources().getDrawable(R.drawable.ic_blue_dort));
        roundMenu.onClickListener = new OnClickListener() {
            @Override
            public void onClick(View view) {
                if (onMenuViewClickListener != null)
                    onMenuViewClickListener.OnMenuViewClick(view, clickEvent, 2);
            }
        };
        addRoundMenu(roundMenu);

        roundMenu = new RoundMenuView.RoundMenu();
        roundMenu.selectSolidColor = Color.GRAY;
        roundMenu.icon = drawable2Bitmap2(getResources().getDrawable(R.mipmap.ic_arrow_right_blue));
        roundMenu.stop_icon = drawable2Bitmap2(getResources().getDrawable(R.drawable.ic_blue_dort));
        roundMenu.onClickListener = new OnClickListener() {
            @Override
            public void onClick(View view) {
                if (onMenuViewClickListener != null)
                    onMenuViewClickListener.OnMenuViewClick(view, clickEvent, 1);
            }
        };
        addRoundMenu(roundMenu);

        setCoreMenu(Color.parseColor("#FFffff"), Color.parseColor("#FFffff"), Color.parseColor("#3A8EDD")
                , 2, 0.33, drawable2Bitmap(getResources().getDrawable(R.drawable.shape_trans)), new OnClickListener() {
                    @Override
                    public void onClick(View view) {
//                    Toast.makeText(PreviewActivity.this, "点击了中心圆圈", Toast.LENGTH_SHORT).show();
                    }
                });
    }


    /**
     * Drawable to bitmap.
     *
     * @param drawable The drawable.
     * @return bitmap
     */
    public static Bitmap drawable2Bitmap(final Drawable drawable) {
        if (drawable instanceof BitmapDrawable) {
            BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
            if (bitmapDrawable.getBitmap() != null) {
                return bitmapDrawable.getBitmap();
            }
        }
        Bitmap bitmap;
        if (drawable.getIntrinsicWidth() <= 0 || drawable.getIntrinsicHeight() <= 0) {
            bitmap = Bitmap.createBitmap(1, 1,
                    drawable.getOpacity() != PixelFormat.OPAQUE
                            ? Bitmap.Config.ARGB_8888
                            : Bitmap.Config.RGB_565);
        } else {
            bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(),
                    drawable.getIntrinsicHeight(),
                    drawable.getOpacity() != PixelFormat.OPAQUE
                            ? Bitmap.Config.ARGB_8888
                            : Bitmap.Config.RGB_565);
        }
        Canvas canvas = new Canvas(bitmap);
        drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
        drawable.draw(canvas);
        return bitmap;
    }

    /**
     * Drawable to bitmap.
     *
     * @param drawable The drawable.
     * @return bitmap
     */
    private static Bitmap drawable2Bitmap2(final Drawable drawable) {
        Bitmap bitmap;
        bitmap = Bitmap.createBitmap(dp2px(25),
                dp2px(25),
                drawable.getOpacity() != PixelFormat.OPAQUE
                        ? Bitmap.Config.ARGB_8888
                        : Bitmap.Config.RGB_565);
        Canvas canvas = new Canvas(bitmap);
        drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
        drawable.draw(canvas);
        return bitmap;
    }

    /**
     * Interface definition for a callback to be invoked when a view is clicked.
     */
    public interface OnMenuViewClickListener {
        /**
         * Called when a view has been clicked.
         *
         * @param v The view that was clicked.
         */
        void OnMenuViewClick(View v, String event, int position);
    }

}

ic_blue_dort.xml:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="oval">
    <size
        android:width="20dp"
        android:height="20dp" />
    <solid android:color="#3A8EDD" />
</shape>

shape_trans.xml:

<?xml version="1.0" encoding="utf-8"?>

<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="oval">
    <size
        android:width="6dp"
        android:height="6dp" />
    <solid android:color="@color/transparent" />
</shape>
ic_arrow_right_blue:随便一个图片或是资源图片。图中的方向键头。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值