示例:
java代码:
package com.chy.mytestmap;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PathMeasure;
import android.graphics.RadialGradient;
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.AnticipateInterpolator;
import android.view.animation.LinearInterpolator;
import android.view.animation.OvershootInterpolator;
import androidx.core.graphics.ColorUtils;
import java.util.ArrayList;
import java.util.List;
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);// 按钮环绕中心按钮
angle = i * (-240 / 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;
}*/
/**
* 修改版本
* */
@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_CANCEL;
if (mFeedbackListener != null) {
mFeedbackListener.onMenuSelected(index - 1);
}
// 这里最后再减去90度的原因是:画布坐标系是普通数学坐标系顺时针旋转90度得到的
// 不减去90度的话则会从画布坐标系的第一象限算起
//rotateAngle = clickIndex * (360 / itemNum) - (360 / itemNum) - 90;
startCancelMenuAnima();
}
}
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);
}
}
xml代码:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:background="@color/colorPrimary"
tools:context=".MainActivity">
<com.chy.mytestmap.CircleMenuView
android:id="@+id/circle_menu"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/colorAccent">
</com.chy.mytestmap.CircleMenuView>
</RelativeLayout>
使用方法:
private void initControls(){
circleMenu = findViewById(R.id.circle_menu);
circleMenu.setMainMenu(Color.parseColor("#ffffff"), R.mipmap.ic_launcher, R.mipmap.ic_launcher)
.addSubMenu(Color.parseColor("#ffffff"), R.mipmap.ic_launcher)
.addSubMenu(Color.parseColor("#ffffff"), R.mipmap.ic_launcher)
.addSubMenu(Color.parseColor("#ffffff"), R.mipmap.ic_launcher)
.addSubMenu(Color.parseColor("#ffffff"), R.mipmap.ic_launcher)
// 这里开启返回键拦截,主要就是按下返回键关闭菜单
.setInterceptBackPressedEnable(true)
.setFeedbackListener(new CircleMenuView.OnCircleMenuFeedbackListener() {
@Override
public void onMenuSelected(int index) {
Toast.makeText(getApplicationContext(), "点击了 [" + index + "]", Toast.LENGTH_SHORT).show();
}
@Override
public void onMenuCenterClicked() {
Toast.makeText(getApplicationContext(), "点击了 [中心]", Toast.LENGTH_SHORT).show();
}
@Override
public void onMenuOpened() {
}
@Override
public void onMenuClosed() {
}
});
}