The idea of adding an item in the menu is to first pin it to the leftmost of the interface, half the width of the item to the left of the screen. Then, in draw, set the rotate value for each item according to its own angle, not only to the position where the item itself should be located outside (the rotate should be set as the origin in the lower left corner ), it also needs to rotate to a correct upward angle based on the offset angle. In fact, they were offset during initialization.
3-of course, the built-in effects on 6.0 and 7.0 are much more beautiful than mine, but I have to write this to complete the design draft provided by the designer. So light spray.
4-The following po implementation steps.
A. Define a class ArcMenu to inherit from FrameLayout. overload three constructor methods.
public class ArcMenu extends FrameLayout {
public ArcMenu(Context context) {super(context);}
public ArcMenu(Context context, AttributeSet attrs) {super(context, attrs);}
B. Override the onSizeChanged method to determine the height and width of the control and initialize some parameters in the control.
private void init() { if (!isInEditMode()) { for (int i = 0; i < ICON_COUNT; i++) {
//Initialize bitmap of default and clicked Images M_iconGrayBitmaps[I] = DrawableManager. instance (). getAssetBitmap (S_iconNamePrefixes[I] +"_Gray.png");M_iconWhiteBitmaps[I] = DrawableManager. instance (). getAssetBitmap (S_iconNamePrefixes[I] +"_White.png");// M_tangent [I] = Math. tan (90.0f/ICON_COUNT * I ); }}M_arcWidth=M_height/4 ;//Arc width// M_itemIndex = 0; M_startDegree= 90.0f/ICON_COUNT/2 ;//The offset angle of the position where the first item should be waiting during initialization M_stepDegree= 90.0f/ICON_COUNT;//The offset angle between each item and the previous item
// Create a new rectangle with the center in the lower left corner
m_arcRect = new RectF(-(m_width - m_arcWidth / 2), m_arcWidth / 2, m_width - m_arcWidth / 2, 2 * m_height - m_arcWidth / 2); int shadow_arc_width = Utils.dp2px(SHADOW_ARC_WIDTH); m_arcShadow = new RectF(-(m_width - m_arcWidth - shadow_arc_width / 2), m_arcWidth + shadow_arc_width / 2, (m_width - m_arcWidth - shadow_arc_width / 2), 2 * m_height - m_arcWidth - shadow_arc_width / 2); m_arcbgPaint = new Paint(Paint.ANTI_ALIAS_FLAG); m_arcbgPaint.setStyle(Paint.Style.STROKE); m_arcbgPaint.setStrokeWidth(m_arcWidth); m_arcbgPaint.setColor(getResources().getColor(R.color.bg_menu_arc)); m_arcfgPaint = new Paint(Paint.ANTI_ALIAS_FLAG); m_arcfgPaint.setStyle(Paint.Style.STROKE); m_arcfgPaint.setStrokeWidth(m_arcWidth); m_arcfgPaint.setColor(getResources().getColor(R.color.fg_menu_arc)); m_shadowPaint = new Paint(Paint.ANTI_ALIAS_FLAG); m_shadowPaint.setStyle(Paint.Style.STROKE); m_shadowPaint.setStrokeWidth(shadow_arc_width); m_shadowPaint.setColor(getResources().getColor(R.color.default_black)); setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN) { int index = pointInArc(event.getX(), event.getY()); if (index >= 0) { Logger.debug("click on icon " + index); m_itemIndex = index; invalidate(); m_itemClickedListener.onMenuItemClicked(index); return true; } } return false; } }); }
The picture above is incorrect .. The Green Box is actually the m_arcRect
ShadowArc is actually the black arc .. I am too lazy to draw it again ..
The next step is to draw a picture. To draw those items, you have to draw these two arcs.
DipatchDraw
@Override protected void dispatchDraw(Canvas canvas) { Path path = new Path(); /// draw out arc path.arcTo(m_arcRect, 270.0f, 90.0f); canvas.drawPath(path, m_arcbgPaint); canvas.drawArc(m_arcRect, 270.0f + m_stepDegree * m_itemIndex, m_stepDegree, false, m_arcfgPaint); /// draw shadow arc canvas.drawArc(m_arcShadow, 270.0f, 90.0f, false, m_shadowPaint); /// draw bitmaps for (int i = 0; i < ICON_COUNT; i++) { Bitmap bitmap; if (i == m_itemIndex) { /// selected icon bitmap = m_iconWhiteBitmaps[i]; } else { /// not selected bitmap = m_iconGrayBitmaps[i]; } if (bitmap != null) { Matrix matrix = new Matrix(); matrix.postTranslate(-bitmap.getWidth() / 2, (m_arcWidth - bitmap.getHeight()) / 2); matrix.postRotate(-m_startDegree - m_stepDegree * i, 0, m_arcWidth / 2); matrix.postRotate(m_startDegree + m_stepDegree * i, 0, m_height); canvas.drawBitmap(bitmap, matrix, null); } } super.dispatchDraw(canvas); }
I don't know what to say here .. Simply paste the code.
/** * check if point (x, y) is within arc icon * * @param x * @param y * @return index of the icon clicked, or -1 */ private int pointInArc(float x, float y) { float[] pts = {x, y}; float[] target_pts = new float[2]; Matrix matrix = new Matrix(); matrix.postRotate(-m_startDegree, 0, m_height); for (int i = 0; i < ICON_COUNT; i++) { matrix.mapPoints(target_pts, pts); if (pointInStartRectOfIcon(target_pts)) { return i; } matrix.postRotate(-m_stepDegree, 0, m_height); } return -1; } /** * check if (target_pts[0], target_pts[1]) is in the start position of icons * * @param target_pts * @return */ private boolean pointInStartRectOfIcon(float[] target_pts) { return target_pts[0] > -m_arcWidth / 2 && target_pts[0] < m_arcWidth / 2 && target_pts[1] > 0 && target_pts[1] < m_arcWidth; }
/** * Sets the click listener for menu items. */public void setOnItemClickedListener(ArcMenuItemClickedListener itemClickedListener) { this.m_itemClickedListener = itemClickedListener;}public void setSelectedIcon(int index, boolean bInvalidate) { m_itemIndex = index; if(bInvalidate) { invalidate(); }}
Let's get the sauce... It's annoying to write a blog.