IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Android环形菜单(CircleMenuView )实现 -> 正文阅读

[移动开发]Android环形菜单(CircleMenuView )实现

Android环形菜单实现

参考https://github.com/Hitomis/CircleMenu ,在原自定义View中修改了一些代码

效果图:

在这里插入图片描述

使用方式:

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";

    private CircleMenuView circleMenu;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        circleMenu = (CircleMenuView) findViewById(R.id.circle_menu);
        circleMenu.setMainMenu(Color.parseColor("#CDCDCD"), R.mipmap.icon_menu, R.mipmap.icon_cancel)
                .addSubMenu(Color.parseColor("#258CFF"), R.mipmap.p1)
                .addSubMenu(Color.parseColor("#30A400"), R.mipmap.icon_search)
                .addSubMenu(Color.parseColor("#FF4B32"), R.mipmap.icon_notify)
                .addSubMenu(Color.parseColor("#8A39FF"), R.mipmap.icon_setting)
                .addSubMenu(Color.parseColor("#FF6A00"), R.mipmap.icon_gps)
            // 这里开启返回键拦截,主要就是按下返回键关闭菜单
                .setInterceptBackPressedEnable(true)
                .setFeedbackListener(new CircleMenuView.OnCircleMenuFeedbackListener() {
                    @Override
                    public void onMenuSelected(int index) {
                        Snackbar.make(circleMenu, "点击了 [" + index + "]", Snackbar.LENGTH_SHORT).show();
                    }

                    @Override
                    public void onMenuCenterClicked() {
                        Snackbar.make(circleMenu, "点击了 [中心]", Snackbar.LENGTH_SHORT).show();
                    }

                    @Override
                    public void onMenuOpened() {

                    }

                    @Override
                    public void onMenuClosed() {

                    }
                });
    }
}

必要的注释都在代码里了,包括返回键的处理

主要是Path绘制I的时候需要旋转Canvas

public class CircleMenuView extends View  {

    private static final String TAG = "CircleMenuView";

    private static final int STATUS_MENU_OPEN = 1;

    private static final int STATUS_MENU_OPENED = 1 << 1;

    private static final int STATUS_MENU_CLOSE = 1 << 2;

    private static final int STATUS_MENU_CLOSE_CLEAR = 1 << 3;

    private static final int STATUS_MENU_CLOSED = 1 << 4;

    private static final int STATUS_MENU_CANCEL = 1 << 5;

    /**
     * 最多8个菜单
     */
    private static final int MAX_SUBMENU_NUM = 8;

    private final int shadowRadius = 5;

    private int partSize;

    private int iconSize;

    private float circleMenuRadius;

    private int itemNum;

    private float itemMenuRadius;

    private float fraction, rFraction;

    private float pathLength;

    private int mainMenuColor;

    private Drawable openMenuIconDrawable, closeMenuIconDrawable;

    private List<Integer> subMenuColorList;

    private List<Drawable> subMenuDrawableList;

    private List<RectF> menuRectFList;

    private int centerX, centerY;

    private int clickIndex;

    private int rotateAngle;

    private int itemIconSize;

    private int pressedColor;

    /**
     * view此时的状态
     */
    private int state;

    private boolean pressed;

    private Paint oPaint, cPaint, sPaint;

    private PathMeasure pathMeasure;

    private Path path, dstPath;

    private OnCircleMenuFeedbackListener mFeedbackListener;

    public CircleMenuView(Context context) {
        this(context, null);
    }

    public CircleMenuView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public CircleMenuView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        state = STATUS_MENU_CLOSED;
        init();

        setFocusable(true);
        setFocusableInTouchMode(true);
    }

    private void init() {
        initTool();

        mainMenuColor = Color.parseColor("#CDCDCD");

        openMenuIconDrawable = new GradientDrawable();
        closeMenuIconDrawable = new GradientDrawable();

        subMenuColorList = new ArrayList<>();
        subMenuDrawableList = new ArrayList<>();
        menuRectFList = new ArrayList<>();

    }

    private void initTool() {
        oPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        oPaint.setStyle(Paint.Style.FILL_AND_STROKE);

        cPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        cPaint.setStyle(Paint.Style.STROKE);
        // 设置画笔头部和尾部的形状
        cPaint.setStrokeCap(Paint.Cap.ROUND);

        sPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        sPaint.setStyle(Paint.Style.FILL);

        path = new Path();
        dstPath = new Path();
        pathMeasure = new PathMeasure();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);

        int width = MeasureSpec.getSize(widthMeasureSpec);
        int height = MeasureSpec.getSize(heightMeasureSpec);

        int measureWidthSize = width, measureHeightSize = height;

        if (widthMode == MeasureSpec.AT_MOST) {
            measureWidthSize = dip2px(20) * 10;
        }

        if (heightMode == MeasureSpec.AT_MOST) {
            measureHeightSize = dip2px(20) * 10;
        }
        setMeasuredDimension(measureWidthSize, measureHeightSize);
        // super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);

        int minSize = Math.min(getMeasuredWidth(), getMeasuredHeight());

        // 中心图标所占区域大小
        partSize = minSize / 10;
        // 子菜单图标大小
        iconSize = partSize * 4 / 5;
        circleMenuRadius = partSize * 3;

        centerX = getMeasuredWidth() / 2;
        centerY = getMeasuredHeight() / 2;

        // 设置中心图标drawable大小
        resetMainDrawableBounds();

        // 关闭的路径设置,顺时针绘制
        path.addCircle(centerX, centerY, circleMenuRadius, Path.Direction.CW);
        // 闭环
        pathMeasure.setPath(path, true);
        pathLength = pathMeasure.getLength();

        RectF mainMenuRectF = new RectF(
                centerX - partSize,
                centerY - partSize,
                centerX + partSize,
                centerY + partSize);
        menuRectFList.add(mainMenuRectF);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        switch (state) {
            case STATUS_MENU_CLOSED:
                drawMainMenu(canvas);
                break;
            case STATUS_MENU_OPEN:
                drawMainMenu(canvas);
                drawSubMenu(canvas);
                break;
            case STATUS_MENU_OPENED:
                drawMainMenu(canvas);
                drawSubMenu(canvas);
                break;
            case STATUS_MENU_CLOSE:
                drawMainMenu(canvas);
                drawSubMenu(canvas);
                drawCircleMenu(canvas);
                break;
            case STATUS_MENU_CLOSE_CLEAR:
                drawMainMenu(canvas);
                drawCircleMenu(canvas);
                break;
            case STATUS_MENU_CANCEL:
                drawMainMenu(canvas);
                drawSubMenu(canvas);
                break;
        }
    }

    /**
     * 绘制周围子菜单环绕的圆环路径
     *
     * @param canvas
     */
    private void drawCircleMenu(Canvas canvas) {
        if (state == STATUS_MENU_CLOSE) {
            drawCirclePath(canvas);
            drawCircleIcon(canvas);
        } else {
            cPaint.setStrokeWidth(partSize * 2 + partSize * .5f * fraction);
            cPaint.setColor(calcAlphaColor(getClickMenuColor(), true));
            canvas.drawCircle(centerX, centerY, circleMenuRadius + partSize * .5f * fraction, cPaint);
        }
    }

    private int getClickMenuColor() {
        return clickIndex == 0 ? mainMenuColor : subMenuColorList.get(clickIndex - 1);
    }

    /**
     * 绘制子菜单转动时的图标
     *
     * @param canvas
     */
    private void drawCircleIcon(Canvas canvas) {
        canvas.save();
        Drawable selDrawable = subMenuDrawableList.get(clickIndex - 1);
        if (selDrawable == null) return;
        int startAngle = (clickIndex - 1) * (360 / itemNum);
        int endAngle = 360 + startAngle;
        int itemX = (int) (centerX + Math.sin(Math.toRadians((endAngle - startAngle) * fraction + startAngle)) * circleMenuRadius);
        int itemY = (int) (centerY - Math.cos(Math.toRadians((endAngle - startAngle) * fraction + startAngle)) * circleMenuRadius);
        canvas.rotate(360 * fraction, itemX, itemY);
        selDrawable.setBounds(itemX - iconSize / 2,
                itemY - iconSize / 2,
                itemX + iconSize / 2,
                itemY + iconSize / 2);
        selDrawable.draw(canvas);
        canvas.restore();
    }

    /**
     * 绘制子菜单项转动时的轨迹路径
     *
     * @param canvas
     */
    private void drawCirclePath(Canvas canvas) {
        canvas.save();
        // 旋转画布,旋转到点击子菜单的位置
        Log.d(TAG, "drawCirclePath: rotateAngle = " + rotateAngle);
        canvas.rotate(rotateAngle, centerX, centerY);
        dstPath.reset();
        dstPath.lineTo(0, 0);
        pathMeasure.getSegment(0, pathLength * fraction, dstPath, true);
        cPaint.setStrokeWidth(partSize * 2);
        cPaint.setColor(getClickMenuColor());
        canvas.drawPath(dstPath, cPaint);
        canvas.restore();
    }

    /**
     * 绘制周围子菜单项按钮
     *
     * @param canvas
     */
    private void drawSubMenu(Canvas canvas) {
        int itemX, itemY, angle;
        final float offsetRadius = 1.5f;
        RectF menuRectF;
        for (int i = 0; i < itemNum; i++) {
            angle = i * (360 / itemNum);

            // 确定itemX itemY
            if (state == STATUS_MENU_OPEN) {
                Log.d(TAG, "drawSubMenu: ");
                itemX = (int) (centerX + Math.sin(Math.toRadians(angle)) * (circleMenuRadius - (1 - fraction) * partSize * offsetRadius));
                itemY = (int) (centerY - Math.cos(Math.toRadians(angle)) * (circleMenuRadius - (1 - fraction) * partSize * offsetRadius));
                oPaint.setColor(calcAlphaColor(subMenuColorList.get(i), false));
                sPaint.setColor(calcAlphaColor(subMenuColorList.get(i), false));
            } else if (state == STATUS_MENU_CANCEL) {
                itemX = (int) (centerX + Math.sin(Math.toRadians(angle)) * (circleMenuRadius - fraction * partSize * offsetRadius));
                itemY = (int) (centerY - Math.cos(Math.toRadians(angle)) * (circleMenuRadius - fraction * partSize * offsetRadius));
                oPaint.setColor(calcAlphaColor(subMenuColorList.get(i), true));
                sPaint.setColor(calcAlphaColor(subMenuColorList.get(i), true));
            } else {
                itemX = (int) (centerX + Math.sin(Math.toRadians(angle)) * circleMenuRadius);
                itemY = (int) (centerY - Math.cos(Math.toRadians(angle)) * circleMenuRadius);
                oPaint.setColor(subMenuColorList.get(i));
                sPaint.setColor(subMenuColorList.get(i));
            }

            if (pressed && clickIndex - 1 == i) {
                oPaint.setColor(pressedColor);
            }

            drawMenuShadow(canvas, itemX, itemY, itemMenuRadius);

            canvas.drawCircle(itemX, itemY, itemMenuRadius, oPaint);

            drawSubMenuIcon(canvas, itemX, itemY, i);

            menuRectF = new RectF(itemX - partSize,
                    itemY - partSize,
                    itemX + partSize,
                    itemY + partSize);

            if (menuRectFList.size() - 1 > i) {
                menuRectFList.remove(i + 1);
            }

            menuRectFList.add(i + 1, menuRectF);
        }
    }

    /**
     * 绘制子菜单项图标
     *
     * @param canvas
     * @param centerX
     * @param centerY
     * @param index
     */
    private void drawSubMenuIcon(Canvas canvas, int centerX, int centerY, int index) {
        // 子菜单图标drawable大小,为子菜单大小的一半
        int diff;
        if (state == STATUS_MENU_OPEN || state == STATUS_MENU_CANCEL) {
            diff = itemIconSize / 2;
        } else {
            diff = iconSize / 2;
        }
        resetBoundsAndDrawIcon(canvas, subMenuDrawableList.get(index), centerX, centerY, diff);
    }

    private void resetBoundsAndDrawIcon(Canvas canvas, Drawable drawable, int centerX, int centerY, int diff) {
        if (drawable == null) return;
        drawable.setBounds(centerX - diff, centerY - diff, centerX + diff, centerY + diff);
        drawable.draw(canvas);
    }

    /**
     * 绘制中间的菜单开关按钮
     *
     * @param canvas
     */
    private void drawMainMenu(Canvas canvas) {
        float centerMenuRadius, realFraction;
        if (state == STATUS_MENU_CLOSE) {
            // 中心主菜单按钮以两倍速度缩小
            realFraction = (1 - fraction * 2) == 0 ? 0 : (1 - fraction * 2);
            centerMenuRadius = partSize * realFraction;
        } else if (state == STATUS_MENU_CLOSE_CLEAR) {
            // 中心主菜单按钮以四倍速度扩大
            realFraction = fraction * 4 >= 1 ? 1 : fraction * 4;
            centerMenuRadius = partSize * realFraction;
        } else if (state == STATUS_MENU_CLOSED || state == STATUS_MENU_CANCEL) {
            centerMenuRadius = partSize;
        } else {
            centerMenuRadius = partSize;
        }

        if (state == STATUS_MENU_OPEN || state == STATUS_MENU_OPENED || state == STATUS_MENU_CLOSE) {
            // 菜单已经打开了,然后点击中间图标时按压的颜色
            oPaint.setColor(calcPressedEffectColor(0, .5f));
        } else if (pressed && clickIndex == 0) {
            // 设置点击中间按钮的按压颜色
            oPaint.setColor(pressedColor);
        } else {
            oPaint.setColor(mainMenuColor);
            sPaint.setColor(mainMenuColor);
        }

        drawMenuShadow(canvas, centerX, centerY, centerMenuRadius);

        // 绘制中间图标
        canvas.drawCircle(centerX, centerY, centerMenuRadius, oPaint);
        drawMainMenuIcon(canvas);
    }

    private void drawMainMenuIcon(Canvas canvas) {
        canvas.save();
        switch (state) {
            case STATUS_MENU_CLOSED:
                if (openMenuIconDrawable != null)
                    openMenuIconDrawable.draw(canvas);
                break;
            case STATUS_MENU_OPEN:
                canvas.rotate(45 * (fraction - 1), centerX, centerY);
                resetBoundsAndDrawIcon(canvas, closeMenuIconDrawable, centerX, centerY, iconSize / 2);
                break;
            case STATUS_MENU_OPENED:
                resetBoundsAndDrawIcon(canvas, closeMenuIconDrawable, centerX, centerY, iconSize / 2);
                break;
            case STATUS_MENU_CLOSE:
                resetBoundsAndDrawIcon(canvas, closeMenuIconDrawable, centerX, centerY, itemIconSize / 2);
                break;
            case STATUS_MENU_CLOSE_CLEAR:
                canvas.rotate(90 * (rFraction - 1), centerX, centerY);
                resetBoundsAndDrawIcon(canvas, openMenuIconDrawable, centerX, centerY, itemIconSize / 2);
                break;
            case STATUS_MENU_CANCEL:
                canvas.rotate(-45 * fraction, centerX, centerY);
                if (closeMenuIconDrawable != null)
                    closeMenuIconDrawable.draw(canvas);
                break;
        }
        canvas.restore();
    }

    /**
     * 绘制菜单按钮阴影
     *
     * @param canvas
     * @param centerX
     * @param centerY
     */
    private void drawMenuShadow(Canvas canvas, int centerX, int centerY, float radius) {
        if (radius + shadowRadius > 0) {
            sPaint.setShader(new RadialGradient(centerX, centerY, radius + shadowRadius,
                    Color.BLACK, Color.TRANSPARENT, Shader.TileMode.CLAMP));
            canvas.drawCircle(centerX, centerY, radius + shadowRadius, sPaint);
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (state == STATUS_MENU_CLOSE || state == STATUS_MENU_CLOSE_CLEAR) return true;
        int index = clickWhichRectF(event.getX(), event.getY());
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                pressed = true;
                if (index != -1) {
                    clickIndex = index;
                    updatePressEffect(index, pressed);
                }
                break;
            case MotionEvent.ACTION_MOVE:
                if (index == -1) {
                    pressed = false;
                    invalidate();
                }
                break;
            case MotionEvent.ACTION_UP:
                pressed = false;
                if (index != -1) {
                    clickIndex = index;
                    updatePressEffect(index, pressed);
                }
                if (index == 0) {
                    // 点击的是中间的按钮
                    if (state == STATUS_MENU_CLOSED) {
                        state = STATUS_MENU_OPEN;
                        if (mFeedbackListener != null) {
                            mFeedbackListener.onMenuCenterClicked();
                        }
                        startOpenMenuAnima();
                    } else if (state == STATUS_MENU_OPENED) {
                        state = STATUS_MENU_CANCEL;
                        if (mFeedbackListener != null) {
                            mFeedbackListener.onMenuCenterClicked();
                        }
                        startCancelMenuAnima();
                    }
                } else {
                    // 点击的是周围子菜单项按钮
                    if (state == STATUS_MENU_OPENED && index != -1) {
                        state = STATUS_MENU_CLOSE;
                        if (mFeedbackListener != null) {
                            mFeedbackListener.onMenuSelected(index - 1);
                        }
                        // 这里最后再减去90度的原因是:画布坐标系是普通数学坐标系顺时针旋转90度得到的
                        // 不减去90度的话则会从画布坐标系的第一象限算起
                        rotateAngle = clickIndex * (360 / itemNum) - (360 / itemNum) - 90;
                        startCloseMeunAnima();
                    }
                }
                break;
        }
        return true;
    }

    /**
     * 更新按钮的状态
     *
     * @param menuIndex
     * @param press
     */
    private void updatePressEffect(int menuIndex, boolean press) {
        if (press) {
            pressedColor = calcPressedEffectColor(menuIndex, .15f);
        }
        invalidate();
    }

    /**
     * 获取按钮被按下的颜色
     *
     * @param menuIndex
     * @param depth     取值范围为[0, 1].值越大,颜色越深
     * @return
     */
    private int calcPressedEffectColor(int menuIndex, float depth) {
        int color = menuIndex == 0 ? mainMenuColor : subMenuColorList.get(menuIndex - 1);
        float[] hsv = new float[3];
        Color.colorToHSV(color, hsv);
        hsv[2] *= (1.f - depth);
        return Color.HSVToColor(hsv);
    }

    /**
     * 用于完成在 View 中的圆环逐渐扩散消失的动画效果 <br/>
     * <p>
     * 根据 fraction 调整 color 的 Alpha 值
     *
     * @param color   被调整 Alpha 值的颜色
     * @param reverse true : 由不透明到透明的顺序调整,否则就逆序
     * @return
     */
    private int calcAlphaColor(int color, boolean reverse) {
        int alpha;
        if (reverse) { // 由不透明到透明
            alpha = (int) (255 * (1.f - fraction));
        } else { // 由透明到不透明
            alpha = (int) (255 * fraction);
        }
        if (alpha >= 255) alpha = 255;
        if (alpha <= 0) alpha = 0;
        return ColorUtils.setAlphaComponent(color, alpha);
    }

    /**
     * 启动打开菜单动画
     */
    private void startOpenMenuAnima() {
        ValueAnimator openAnima = ValueAnimator.ofFloat(1.f, 100.f);
        openAnima.setDuration(500);
        openAnima.setInterpolator(new OvershootInterpolator());
        openAnima.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                fraction = valueAnimator.getAnimatedFraction();
                itemMenuRadius = fraction * partSize;
                itemIconSize = (int) (fraction * iconSize);
                invalidate();
            }
        });
        openAnima.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                state = STATUS_MENU_OPENED;
                if (mFeedbackListener != null) {
                    mFeedbackListener.onMenuOpened();
                }
            }
        });
        openAnima.start();
    }

    /**
     * 启动取消动画
     */
    private void startCancelMenuAnima() {
        ValueAnimator cancelAnima = ValueAnimator.ofFloat(1.f, 100.f);
        cancelAnima.setDuration(500);
        cancelAnima.setInterpolator(new AnticipateInterpolator());
        cancelAnima.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                fraction = valueAnimator.getAnimatedFraction();
                itemMenuRadius = (1 - fraction) * partSize;
                itemIconSize = (int) ((1 - fraction) * iconSize);
                invalidate();
            }
        });
        cancelAnima.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                state = STATUS_MENU_CLOSED;
                if (mFeedbackListener != null)
                    mFeedbackListener.onMenuClosed();
            }
        });
        cancelAnima.start();
    }

    /**
     * 开启关闭菜单动画 </br>
     * <p>关闭菜单动画分为三部分</p>
     * <ur>
     * <li>选中菜单项转动一周</li>
     * <li>环状轨迹扩散消失</li>
     * <li>主菜单按钮旋转</li>
     * </ur>
     */
    private void startCloseMeunAnima() {
        // 选中菜单项转动一周动画驱动
        ValueAnimator aroundAnima = ValueAnimator.ofFloat(1.f, 100.f);
        aroundAnima.setDuration(500);
        aroundAnima.setInterpolator(new AccelerateDecelerateInterpolator());
        aroundAnima.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                fraction = valueAnimator.getAnimatedFraction();
                // 中心主菜单图标以两倍速度缩小
                float animaFraction = fraction * 2 >= 1 ? 1 : fraction * 2;
                itemIconSize = (int) ((1 - animaFraction) * iconSize);
                invalidate();
            }
        });
        aroundAnima.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                state = STATUS_MENU_CLOSE_CLEAR;
            }
        });

        // 环状轨迹扩散消失动画驱动
        ValueAnimator spreadAnima = ValueAnimator.ofFloat(1.f, 100.f);
        spreadAnima.setInterpolator(new LinearInterpolator());
        spreadAnima.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                fraction = valueAnimator.getAnimatedFraction();
            }
        });

        // 主菜单转动动画驱动
        ValueAnimator rotateAnima = ValueAnimator.ofFloat(1.f, 100.f);
        rotateAnima.setInterpolator(new OvershootInterpolator());
        rotateAnima.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                rFraction = valueAnimator.getAnimatedFraction();
                itemIconSize = (int) (rFraction * iconSize);
                invalidate();
            }
        });

        AnimatorSet closeAnimaSet = new AnimatorSet();
        closeAnimaSet.setDuration(500);
        closeAnimaSet.play(spreadAnima).with(rotateAnima);
        closeAnimaSet.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                state = STATUS_MENU_CLOSED;
                if (mFeedbackListener != null) {
                    mFeedbackListener.onMenuClosed();
                }
            }
        });

        AnimatorSet animatorSet = new AnimatorSet();
        animatorSet.play(aroundAnima).before(closeAnimaSet);
        animatorSet.start();
    }

    /**
     * 获取当前点击的是哪一个菜单按钮 <br/>
     * 中心菜单下标为0,周围菜单从正上方顺时针计数1~5
     *
     * @param x
     * @param y
     * @return
     */
    private int clickWhichRectF(float x, float y) {
        int which = -1;
        for (RectF rectF : menuRectFList) {
            if (rectF.contains(x, y)) {
                which = menuRectFList.indexOf(rectF);
                break;
            }
        }
        return which;
    }

    private Drawable convertDrawable(int iconRes) {
        return getResources().getDrawable(iconRes);
    }

    private Drawable convertBitmap(Bitmap bitmap) {
        return new BitmapDrawable(getResources(), bitmap);
    }

    private void resetMainDrawableBounds() {
        openMenuIconDrawable.setBounds(centerX - iconSize / 2, centerY - iconSize / 2,
                centerX + iconSize / 2, centerY + iconSize / 2);
        closeMenuIconDrawable.setBounds(centerX - iconSize / 2, centerY - iconSize / 2,
                centerX + iconSize / 2, centerY + iconSize / 2);
    }

    /**
     * 设置主菜单的背景色,以及打开/关闭的图标
     *
     * @param mainMenuColor 主菜单背景色
     * @param openMenuRes   菜单打开图标,Resource 格式
     * @param closeMenuRes  菜单关闭图标,Resource 格式
     * @return
     */
    public CircleMenuView setMainMenu(int mainMenuColor, int openMenuRes, int closeMenuRes) {
        openMenuIconDrawable = convertDrawable(openMenuRes);
        closeMenuIconDrawable = convertDrawable(closeMenuRes);
        this.mainMenuColor = mainMenuColor;
        return this;
    }

    /**
     * 设置主菜单的背景色,以及打开/关闭的图标
     *
     * @param mainMenuColor   主菜单背景色
     * @param openMenuBitmap  菜单打开图标,Bitmap 格式
     * @param closeMenuBitmap 菜单关闭图标,Bitmap 格式
     * @return
     */
    public CircleMenuView setMainMenu(int mainMenuColor, Bitmap openMenuBitmap, Bitmap closeMenuBitmap) {
        openMenuIconDrawable = convertBitmap(openMenuBitmap);
        closeMenuIconDrawable = convertBitmap(closeMenuBitmap);
        this.mainMenuColor = mainMenuColor;
        return this;
    }

    /**
     * 设置主菜单的背景色,以及打开/关闭的图标
     *
     * @param mainMenuColor     主菜单背景色
     * @param openMenuDrawable  菜单打开图标,Drawable 格式
     * @param closeMenuDrawable 菜单关闭图标,Drawable 格式
     * @return
     */
    public CircleMenuView setMainMenu(int mainMenuColor, Drawable openMenuDrawable, Drawable closeMenuDrawable) {
        openMenuIconDrawable = openMenuDrawable;
        closeMenuIconDrawable = closeMenuDrawable;
        this.mainMenuColor = mainMenuColor;
        return this;
    }

    /**
     * 添加一个子菜单项,包括子菜单的背景色以及图标
     *
     * @param menuColor 子菜单的背景色
     * @param menuRes   子菜单图标,Resource 格式
     * @return
     */
    public CircleMenuView addSubMenu(int menuColor, int menuRes) {
        if (subMenuColorList.size() < MAX_SUBMENU_NUM && subMenuDrawableList.size() < MAX_SUBMENU_NUM) {
            subMenuColorList.add(menuColor);
            subMenuDrawableList.add(convertDrawable(menuRes));
            itemNum = Math.min(subMenuColorList.size(), subMenuDrawableList.size());
        } else {
            throw new IllegalStateException("注意!最多添加8个子菜单!MAX_SUBMENU_NUM = 8");
        }
        return this;
    }

    /**
     * 添加一个子菜单项,包括子菜单的背景色以及图标
     *
     * @param menuColor  子菜单的背景色
     * @param menuBitmap 子菜单图标,Bitmap 格式
     * @return
     */
    public CircleMenuView addSubMenu(int menuColor, Bitmap menuBitmap) {
        if (subMenuColorList.size() < MAX_SUBMENU_NUM && subMenuDrawableList.size() < MAX_SUBMENU_NUM) {
            subMenuColorList.add(menuColor);
            subMenuDrawableList.add(convertBitmap(menuBitmap));
            itemNum = Math.min(subMenuColorList.size(), subMenuDrawableList.size());
        }
        return this;
    }

    /**
     * 添加一个子菜单项,包括子菜单的背景色以及图标
     *
     * @param menuColor    子菜单的背景色
     * @param menuDrawable 子菜单图标,Drawable 格式
     * @return
     */
    public CircleMenuView addSubMenu(int menuColor, Drawable menuDrawable) {
        if (subMenuColorList.size() < MAX_SUBMENU_NUM && subMenuDrawableList.size() < MAX_SUBMENU_NUM) {
            subMenuColorList.add(menuColor);
            subMenuDrawableList.add(menuDrawable);
            itemNum = Math.min(subMenuColorList.size(), subMenuDrawableList.size());
        }
        return this;
    }

    /**
     * 打开菜单
     * Open the CircleMenu
     */
    public void openMenu() {
        if (state == STATUS_MENU_CLOSED) {
            state = STATUS_MENU_OPEN;
            startOpenMenuAnima();
        }
    }

    /**
     * 关闭菜单
     * Close the CircleMenu
     */
    public void closeMenu() {
        if (state == STATUS_MENU_OPENED) {
            state = STATUS_MENU_CANCEL;
            startCancelMenuAnima();
        }
    }

    /**
     * 菜单是否关闭
     * Returns whether the menu is alread open
     *
     * @return
     */
    public boolean isOpened() {
        return state == STATUS_MENU_OPENED;
    }

    private int dip2px(float dpValue) {
        final float scale = getContext().getResources().getDisplayMetrics().density;
        return (int) (dpValue * scale + 0.5f);
    }

    public CircleMenuView setFeedbackListener(OnCircleMenuFeedbackListener listener) {
        mFeedbackListener = listener;
        return this;
    }

    /**
     * 反馈点击选择,view状态改变的接口
     */
    public interface OnCircleMenuFeedbackListener {
        /**
         * 点击选择子菜单
         *
         * @param index
         */
        void onMenuSelected(int index);

        /**
         * 点击中心图标
         *
         */
        void onMenuCenterClicked();

        /**
         * 菜单打开
         */
        void onMenuOpened();

        /**
         * 菜单关闭
         */
        void onMenuClosed();
    }

    /**
     * 获取当前menu的状态
     *
     * @return
     */
    public int getCurrentMenuState() {
        return state;
    }

    /**
     * 是否拦截点击手机返回键
     */
    public CircleMenuView setInterceptBackPressedEnable(boolean isEnable) {
        if (isEnable) {
            requestFocus();
            setFocusable(true);
            setFocusableInTouchMode(true);
        }
        return this;
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK && isOpened()) {
            // 点击返回键的时候,如果菜单是打开状态的话,关闭
            closeMenu();
            return true;
        }
       return super.onKeyDown(keyCode, event);
    }
}
  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-10-18 17:30:27  更:2021-10-18 17:30:36 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 22:12:24-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码