半圆形菜单按钮的简单实现
半圆菜单按钮的简单实现
回调接口代码:
一、应用场景
前段时间一位老友跟我提了一个附在边上的菜单按钮,这段时间比较忙,没有完美实现,只做了个原型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里面画出来的菜单,暂时没有加上各种阴影效果,不然得话加上层次效果会高大上很多,有时间再改。还有一个不足时按下没有效果的变化,我暂时没有想到什么好的办法叠加上去,如果有高手路过,请赐教啊,哈哈。
六、懒人下载地址
猛戳这里