半圆形菜单按钮的简单实现

半圆菜单按钮的简单实现

一、应用场景

前段时间一位老友跟我提了一个附在边上的菜单按钮,这段时间比较忙,没有完美实现,只做了个原型Demo出来,先发出来记录一下,到有时间在优化这个View,如果有高手能修改优化分享出来的话,小弟在此谢过了。也赶在这个月底要出一个博文,不能荒废了这个技能~_~。成功在于坚持!废话又吹了半天,来看下需求原型图。

半圆形菜单按钮的简单实现

二、分析

个人认为这种按钮一般可以放两个到四个选择,应该是比较好的体验,超出这个范围的话,我觉得用这个方式就不行了。而且Android里面的空间都是方形的,只是加了透明的背景让我们看不出来而已 。技术上的话,这个按钮用到了一个环形,我们分拆一下的话就是几个圆的叠加。上个效果图大家看下先。

半圆形菜单按钮的简单实现

三、实现拆分图

半圆形菜单按钮的简单实现

1、画一个小圆和一个大一点的圆就可以变成1图所示,然后我们给小圆描一个大边,描边是什么不懂的去搜一下,给大圆秒一个小一点的边,就可以变成图3所示

2、图2就是两个圆的描边的效果,把图2的两种效果叠加起来就是图3的效果,有的图3我们的感觉就出来了

3、我们有了图3的效果后裁剪一下画布,截个1/4出来就有了图4的效果,然后我们在根据背景颜色画上分割线,整个菜单按钮就O了


四、代码实现

上干货,代码都是根据上面的思路画出来的,加上注解问题应该不大了

主要的View代码:

package com.spring.circle;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.Paint.Align;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.View;
/**
 * 半圆的菜单
 * @author spring
 *
 */
public class CircleButton extends View {

	public CircleButton(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		init(context);
	}
	public CircleButton(Context context, AttributeSet attrs) {
		super(context, attrs);
		init(context);
	}
	public CircleButton(Context context) {
		super(context);
		init(context);
	}
	/**
	 * 画笔
	 */
	private Paint paint;
	/**
	 * View的大小
	 */
	private RectF viewRectF;
	/**
	 * 小圆
	 */
	private float radius = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 95, getResources().getDisplayMetrics());
	/**
	 * 大圆
	 */
	private float bgRadius = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 95, getResources().getDisplayMetrics());
	/**
	 * 描边
	 */
	private float strokeWidth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 40, getResources().getDisplayMetrics());
	/**
	 * 描边 
	 */
	private float bgStrokeWidth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 60, getResources().getDisplayMetrics());
	/**
	 * 线的大小
	 */
	private float line = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 2, getResources().getDisplayMetrics());
	/**
	 * 菜单
	 */
	private String[] menu = null;
	/**
	 * 背景颜色
	 */
	private int bgColor = Color.YELLOW;
	/**
	 * 前景颜色
	 */
	private int color = Color.GREEN;
	/**
	 * 字体的大小
	 */
	private float textSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 18, getResources().getDisplayMetrics());
	/**
	 * 字体颜色
	 */
	private int textColor = Color.WHITE;
	/**
	 * View的区域
	 */
	private float size = bgRadius+bgStrokeWidth;
	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		setMeasuredDimension((int)size, (int)size);
	}
	/**
	 * init
	 * @param context
	 */
	private void init(Context context){
		paint = new Paint();
		paint.setAntiAlias(true);
		paint.setStyle(Paint.Style.STROKE);
		paint.setColor(Color.GREEN);
		paint.setStrokeWidth(radius);
		viewRectF = new RectF(0, 0, size, size);
	}
	/**
	 * 画一个透明的园然后描上一个很大的边,就变成了一个环
	 */
	@Override
	protected void onDraw(Canvas canvas) {
		super.onDraw(canvas);
		//画背景透明
		canvas.drawColor(Color.TRANSPARENT);
		//裁剪画布
		canvas.clipRect(viewRectF);
		paint.setColor(bgColor);
		paint.setAntiAlias(true);
		paint.setStyle(Paint.Style.STROKE);
		//加上描边
		paint.setStrokeWidth(bgStrokeWidth);
		//画园
		canvas.drawCircle(0, size, bgRadius, paint);
		paint.setColor(color);
		paint.setStrokeWidth(strokeWidth);
		canvas.drawCircle(0, size, radius, paint);
		if(menu!=null && menu.length>0){
			int tmp = (int)(90)/menu.length;
			//画文字
			for (int i=0;i<menu.length;i++) {
				canvas.save();
				canvas.rotate(tmp/2+tmp*i,0,size);
				paint.reset();
				paint.setTextAlign(Align.CENTER);
				paint.setColor(textColor);
				paint.setTextSize(textSize);
				canvas.drawText(menu[i], 0, size-radius, paint);
				canvas.restore();
			}
			//画分隔线
			for (int i=0;i<menu.length;i++) {
				canvas.save();
				canvas.rotate(tmp*i,0,size);
				paint.reset();
				paint.setAntiAlias(true);
				paint.setColor(bgColor);
				paint.setStrokeWidth(line);
				canvas.drawLine(0, size-bgRadius+bgStrokeWidth/2, 0, size-bgRadius-bgStrokeWidth/2+line*2, paint);
				canvas.restore();
			}
			
		}
		
	}

	@Override
	public boolean onTouchEvent(MotionEvent event) {
		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
			break;
		case MotionEvent.ACTION_MOVE:
			break;
		case MotionEvent.ACTION_UP:
			//检查点击是否在环上
			check(event);
			break;
		}
		
		return true;
		
	}
	/**
	 * 计算长度
	 * @param x
	 * @param y
	 */
	private boolean checkdis(float x, float y) {
		 double dis = Math.sqrt(x*x+(y-size)*(y-size));  
		 if(dis<radius+strokeWidth/2 && dis>radius-strokeWidth/2)
			 return true;
		 else
			 return false;
	}
	/**
	 * 回调接口
	 */
	private CircleClickListener listener = null;
	public void setOnClick(CircleClickListener listener){
		this.listener = listener;
	}
	private void check(MotionEvent event) {
		double d = Math.toDegrees(Math.atan((size-event.getY())/(event.getX())));
		if(!checkdis(event.getX(),event.getY()))
			return ;
		int tmp = 90-(int)d;
		//区间大小
		int dis = 90/menu.length;
		for (int i = 0; i < menu.length; i++) {
			if(tmp>dis*i&&tmp<dis*(i+1))
			{
				if(listener !=null){
					listener.onClick(menu[i], i);
				}
				
			}
		}
	}
	/**
	 * 设置菜单
	 * @param menus
	 */
	public void setMenu(String[] menus){
		this.menu = menus;
		invalidate();
	}
	public int getBgColor() {
		return bgColor;
	}
	/**
	 * 设置背景的颜色
	 * @param bgColor
	 */
	public void setBgColor(int bgColor) {
		this.bgColor = bgColor;
	}
	public int getColor() {
		return color;
	}
	/**
	 * 设置前景色
	 * @param color
	 */
	public void setColor(int color) {
		this.color = color;
	}
	public float getTextSize() {
		return textSize;
	}
	/**
	 * 设置字体的大小
	 * @param textSize
	 */
	public void setTextSize(float textSize) {
		this.textSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, textSize, getResources().getDisplayMetrics());
	}
	public int getTextColor() {
		return textColor;
	}
	/**
	 * 设置字体的颜色
	 * @param textColor
	 */
	public void setTextColor(int textColor) {
		this.textColor = textColor;
	}
	
}

回调接口代码:

package com.spring.circle;

public interface CircleClickListener {

	public void onClick(String str,int position);
}


五、不足

这是在一个View里面画出来的菜单,暂时没有加上各种阴影效果,不然得话加上层次效果会高大上很多,有时间再改。还有一个不足时按下没有效果的变化,我暂时没有想到什么好的办法叠加上去,如果有高手路过,请赐教啊,哈哈。


六、懒人下载地址

猛戳这里