一、應用情境
前段時間一位老友跟我提了一個附在邊上的功能表按鈕,這段時間比較忙,沒有完美實現,只做了個原型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;@Overrideprotected 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);}/** * 畫一個透明的園然後描上一個很大的邊,就變成了一個環 */@Overrideprotected 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();}}}@Overridepublic 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裡面畫出來的菜單,暫時沒有加上各種陰影製作效果,不然得話加上層次效果會高大上很多,有時間再改。還有一個不足時按下沒有效果的變化,我暫時沒有想到什麼好的辦法疊加上去,如果有高手路過,請賜教啊,哈哈。
六、懶人
猛戳這裡