在好例子网,分享、交流、成长!
您当前所在位置:首页Java 开发实例Android平台开发 → android 圆形可旋转菜单 示例代码下载

android 圆形可旋转菜单 示例代码下载

Android平台开发

下载此实例
  • 开发语言:Java
  • 实例大小:1.82M
  • 下载次数:17
  • 浏览次数:298
  • 发布时间:2017-07-07
  • 实例类别:Android平台开发
  • 发 布 人:mario521
  • 文件格式:.zip
  • 所需积分:2
 相关标签: 菜单 圆形 旋转

实例介绍

【实例简介】简洁的实现圆形可旋转菜单

【实例截图】

【核心代码】
import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.RectF; import android.text.Layout; import android.text.StaticLayout; import android.text.TextPaint; import android.text.TextUtils; import android.util.AttributeSet; import android.util.TypedValue; import android.view.MotionEvent; import android.view.View;  import java.util.List;  import cn.xmrk.bitwheel.R; import cn.xmrk.bitwheel.pojo.BitWheelInfo;  public class BitView extends View { private Object obj = new Object();   /**  * 两种种状态  * 0 表示未被选中  * 1 表示选中  **/  private static final int TYPE_UNCHECKED = 0;    private static final int TYPE_CHECKED = 1;    /**  * 控件要绘制的宽度  **/  private int mDrawWidth;   /**  * 避免重复计算宽度  **/  private boolean hasMeasured = false;   /**  * 500毫秒内的点击被认为是点击事件  **/  private long timeClick = 500;   /**  * 保存点击的时间  **/  private long nowClick;   /**  * 被选中的position,默认第0个选中  **/  private int checkPosition = 0;    /**  * 绘画区域的图片。这边会将各个小图片拼接成一张图片  **/  private Bitmap fontBitmap;    private List<BitWheelInfo> bitInfos;    /**  * 选项的个数  **/  private int mItemCount;   /**  * 保存的画布  **/  private Canvas mCanvas;   /**  * 非选中和选择的颜色色块  */  private int[] mColors = new int[]{0xffffeb8c, 0xffffe670};   /**  * 触摸点的坐标位置  **/  private float touchX;  private float touchY;   /**  * 绘制盘块的范围  */  private RectF mRange = new RectF();   /**  * 绘制盘快的画笔  */  private Paint mArcPaint;   /**  * 绘制文字的画笔  */  private TextPaint mTextPaint;   /**  * 绘制分割线的画笔  */  private Paint mLinePaint;   /**  * 圆形背景的画笔  **/  private Paint mBackColorPaint;   /**  * 圆盘角度  **/  private volatile float mStartAngle = 0;   /**  * 控件的中心位置,处于中心位置。x和y是相等的  */  private int mCenter;   /**  * 圆形背景的宽度,这边是在1080p下的780  **/  private float mBackColorWidth = 780;   /**  * 内圈画盘小圆的宽度,这边是在1080p下的730,绘画区域  **/  private float mRangeWidth = 730;   /**  * 里面的小图大小,这边是1080p下的115  **/  private float mLitterBitWidth = 115;   /**  * 文字的大小为26px  */  private float mTextSize = 26;   /**  * 中心图片信息  **/  private Bitmap mCheckBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_qiandao_lingqu);  private float mCheckBitmapWidth = 270;//1080p下的270  private RectF mCheckBitmapRect = new RectF();//图片绘制区域    public BitView(Context context) { this(context, null);  } public BitView(Context context, AttributeSet attrs) { super(context, attrs);  } /**  * 设置控件为正方形  */  @Override  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec);  int width = Math.min(getMeasuredWidth(), getMeasuredHeight());  // 中心点  mCenter = width / 2;  mDrawWidth = width;  //屏幕的宽度  setMeasuredDimension(width, width);  init();  } /**  * 当前切图的比例都是在1080p下进行的,所以这边的比例就是1080的  * <p>  * mDrawWidth 表示为控件的宽度  **/  private float getDrawWidth(float width) { return width * mDrawWidth / 1080f;  } public void setBitInfos(List<BitWheelInfo> bitInfos) { this.bitInfos = bitInfos;  mItemCount = this.bitInfos.size();  onDrawInvalidate();  } @Override  protected void onDraw(Canvas canvas) { super.onDraw(canvas);  this.mCanvas = canvas;  drawCanvas();  } private void init() { if (!hasMeasured) { //得到各个比例下的图片大小  mBackColorWidth = getDrawWidth(mBackColorWidth);  mRangeWidth = getDrawWidth(mRangeWidth);  mCheckBitmapWidth = getDrawWidth(mCheckBitmapWidth);  mLitterBitWidth = getDrawWidth(mLitterBitWidth);  mTextSize = TypedValue.applyDimension(
                    TypedValue.COMPLEX_UNIT_SP, mTextSize / 3, getResources().getDisplayMetrics());  hasMeasured = true;  } // 初始化绘制圆弧的画笔  mArcPaint = new Paint();  mArcPaint.setAntiAlias(true);  mArcPaint.setDither(true);  // 初始化绘制文字的画笔  mTextPaint = new TextPaint();  mTextPaint.setColor(0xff222222);  mTextPaint.setTextAlign(Paint.Align.CENTER);  mTextPaint.setStyle(Paint.Style.FILL_AND_STROKE);  mTextPaint.setTextSize(mTextSize);  mTextPaint.setAntiAlias(true);  mTextPaint.setDither(true);  //圆形背景的画笔  mBackColorPaint = new Paint();  mBackColorPaint.setColor(0xffae6112);  mBackColorPaint.setAntiAlias(true);  mBackColorPaint.setDither(true);  //未选中的分割线的画笔  mLinePaint = new Paint();  mLinePaint.setAntiAlias(true);  mLinePaint.setDither(true);  mLinePaint.setStrokeWidth(dip2px(1f));  mLinePaint.setStyle(Paint.Style.FILL);  mLinePaint.setColor(0xffff9406);  // 内圈画盘  mRange = new RectF(mCenter - mRangeWidth / 2, mCenter - mRangeWidth / 2, mCenter  mRangeWidth / 2, mCenter  mRangeWidth / 2);  //中心图片绘制区域  mCheckBitmapRect = new RectF(mCenter - mCheckBitmapWidth / 2, mCenter - mCheckBitmapWidth / 2, mCenter  mCheckBitmapWidth / 2, mCenter  mCheckBitmapWidth / 2);  } /**  * 对每个选项进行绘制  **/  private Bitmap getDrawItemBitmap(float tmpAngle, float sweepAngle, int position) { //是否需要重新绘制  boolean needToNew = false;  //根据状态判断是否需要重新绘制  if (checkPosition == position && bitInfos.get(position).info.bitmapType == TYPE_UNCHECKED) {//这次选中,上次没选中的要更新  needToNew = true;  bitInfos.get(position).info.bitmapType = TYPE_CHECKED;  } else if (checkPosition != position && bitInfos.get(position).info.bitmapType == TYPE_CHECKED) {//这次没选中,上次选中的要更新  needToNew = true;  bitInfos.get(position).info.bitmapType = TYPE_UNCHECKED;  } if (bitInfos.get(position).info.itemBitmap == null || needToNew) { //选择背景颜色  if (checkPosition == position) { mArcPaint.setColor(mColors[1]);  } else { mArcPaint.setColor(mColors[0]);  } //绘制每一个小块  bitInfos.get(position).info.itemBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);  Canvas itemCanvas = new Canvas(bitInfos.get(position).info.itemBitmap);  //根据角度进行同步旋转  itemCanvas.rotate(tmpAngle, mCenter, mCenter);  //绘制背景颜色,从最上边开始画  itemCanvas.drawArc(mRange, -sweepAngle / 2 - 90, sweepAngle, true, mArcPaint);  //绘制小图片和文本,因为一起画好画点  drawIconAndText(position, itemCanvas);  //绘制分割线,这里保证没一个小块都有一条分割线,分割线的位置是在最右侧  drawCanvasLine(itemCanvas);  } else {
            Canvas itemCanvas = new Canvas(bitInfos.get(position).info.itemBitmap);  //根据角度进行同步旋转  itemCanvas.rotate(tmpAngle, mCenter, mCenter);  } return bitInfos.get(position).info.itemBitmap;  } private void drawCanvas() { if (bitInfos == null || bitInfos.size() == 0) { return;  } //绘制背景图  mCanvas.drawCircle(mCenter, mCenter, mBackColorWidth / 2, mBackColorPaint);  //画前景图片  mCanvas.drawBitmap(getFontBitmap(), 0, 0, null);  //绘制中心图片  mCanvas.drawBitmap(mCheckBitmap, null, mCheckBitmapRect, null);  } //绘制前景图片,这里包含的是图片信息和文字信息,还有背景圆弧背景展示  private Bitmap getFontBitmap() { fontBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);  Canvas canvas = new Canvas(fontBitmap);  //根据角度进行同步旋转  canvas.rotate(mStartAngle, mCenter, mCenter);  float tmpAngle = 0;  float sweepAngle = (float) (360 / mItemCount);  for (int i = 0; i < mItemCount; i  ) { //这边可以得到新的bitmap  canvas.drawBitmap(getDrawItemBitmap(tmpAngle, sweepAngle, i), 0, 0, null);  tmpAngle  = sweepAngle;  } return fontBitmap;  } /**  * 绘制分割线  **/  private void drawCanvasLine(Canvas canvas) { if (mItemCount == 1) { return;  } //计算终点角度,这边从最上边为0度开始计算,则角度在0-180之间,给0.5f的偏移  float range = 360f / mItemCount / 2 - 0.5f;  //计算半径的长度  float radio = (mRange.right - mRange.left) / 2;  //计算终点坐标  float x = 0;  float y = 0;  if (range < 90) {
            x = (float) (radio * Math.sin(Math.toRadians(range)));  y = (float) (radio * Math.cos(Math.toRadians(range)));   x  = mCenter;  y = mCenter - y;  } else {
            x = (float) (radio * Math.sin(Math.toRadians(180 - range)));  y = (float) (radio * Math.cos(Math.toRadians(180 - range)));   x  = mCenter;  y  = mCenter;  }
        canvas.drawLine(mCenter, mCenter, x, y, mLinePaint);  } /**  * 根据当前旋转的mStartAngle计算当前滚动到的区域  *  * @param startAngle  */  public int calInExactArea(float startAngle) { float size = 360f / mItemCount;  // 确保rotate是正的,且在0-360度之间  float rotate = (startAngle % 360.0f  360.0f) % 360.0f;   for (int i = 0; i < mItemCount; i  ) { // 每个的中奖范围  if (i == 0) { if ((rotate > 360 - size / 2) || (rotate < size / 2)) { return i;  }
            } else { float from = size * (i - 1)   size / 2;  float to = from   size;  if ((rotate > from) && (rotate < to)) { return i;  }
            }
        } return -1;  } /**  * 绘制小图片和文字  *  * @param i  */  private void drawIconAndText(int i, Canvas canvas) { //根据的标注,比例为115/730  float rt = mLitterBitWidth / mRangeWidth;  //计算绘画区域的直径  int mRadius = (int) (mRange.right - mRange.left);  int imgWidth = (int) (mRadius * rt);  //获取中心点坐标  int x = mCenter;  //这边让图片从四分之一出开始画  int y = (int) (mCenter - mRadius / 2  (float) mRadius / 2 * 1 / 4f);  //确定小图片的区域  Rect rect = new Rect(x - imgWidth / 2, y - imgWidth / 2, x   imgWidth
                / 2, y   imgWidth / 2);  //将图片画上去  canvas.drawBitmap(bitInfos.get(i).bitmap, null, rect, null);  //绘制文本  if (!TextUtils.isEmpty(bitInfos.get(i).text)) { //最大字数限制为8个字  if (bitInfos.get(i).text.length() > 8) { bitInfos.get(i).text = bitInfos.get(i).text.substring(0, 8);  }
            StaticLayout textLayout = new StaticLayout(bitInfos.get(i).text, mTextPaint, imgWidth, Layout.Alignment.ALIGN_NORMAL, 1f, 0, false);  canvas.translate(mCenter, rect.bottom  dip2px(2));  textLayout.draw(canvas);  //画完之后移动回来  canvas.translate(-mCenter, -(rect.bottom  dip2px(2)));  }
    } @Override  public boolean onTouchEvent(MotionEvent event) { //判断按点是否在圆内  if (!mRange.contains(event.getX(), event.getY())) { return true;  } switch (event.getAction()) { case MotionEvent.ACTION_DOWN: touchX = event.getX();  touchY = event.getY();  //按下时的时间  nowClick = System.currentTimeMillis();  break;  case MotionEvent.ACTION_MOVE: float moveX = event.getX();  float moveY = event.getY();  //得到旋转的角度  float arc = getRoundArc(touchX, touchY, moveX, moveY);  //重新赋值  touchX = moveX;  touchY = moveY;  //起始角度变化下,然后进行重新绘制  mStartAngle  = arc;  onDrawInvalidate();  break;  case MotionEvent.ACTION_UP: if (System.currentTimeMillis() - nowClick <= timeClick) {//此时为点击事件  //落点的角度加上偏移量基于初始的点的位置  checkPosition = calInExactArea(getRoundArc(event.getX(), event.getY()) - mStartAngle);  onDrawInvalidate();  if (mOnWheelCheckListener != null) { mOnWheelCheckListener.onCheck(checkPosition);  }
                } break;  } return true;  } //根据落点计算角度  private float getRoundArc(float upX, float upY) { float arc = 0;  //首先计算三边的长度  float a = (float) Math.sqrt(Math.pow(mCenter - mCenter, 2)   Math.pow(0 - mCenter, 2));  float b = (float) Math.sqrt(Math.pow(upX - mCenter, 2)   Math.pow(upY - mCenter, 2));  float c = (float) Math.sqrt(Math.pow(upX - mCenter, 2)   Math.pow(upY - 0, 2));  //判断是否为三角形  if (a   b > c) {//两边之和大于第三边为三角形  /**  * 接下来计算角度  *  * acos((a2 b2-c2)/2ab)  *  * **/  arc = (float) (Math.acos((Math.pow(a, 2)   Math.pow(b, 2) - Math.pow(c, 2)) / (2 * a * b)) * 180 / Math.PI);   //判断是大于左边还是右边,也就是180以内还是以外  if (upX < mCenter) {//此时是180以外的  arc = 360 - arc;  }
        } else {//上下边界的问题  if (upX == mCenter) { if (upY < mCenter) {
                    arc = 0;  } else {
                    arc = 180;  }
            }
        } return arc;  } //根据三点的坐标计算旋转的角度  private float getRoundArc(float startX, float startY, float endX, float endY) { float arc = 0;  //首先计算三边的长度  float a = (float) Math.sqrt(Math.pow(startX - mCenter, 2)   Math.pow(startY - mCenter, 2));  float b = (float) Math.sqrt(Math.pow(endX - mCenter, 2)   Math.pow(endY - mCenter, 2));  float c = (float) Math.sqrt(Math.pow(startX - endX, 2)   Math.pow(startY - endY, 2));  //判断是否为三角形  if (a   b > c) {//两边之和大于第三边为三角形  /**  * 接下来计算角度  *  * acos((a2 b2-c2)/2ab)  *  * **/  arc = (float) (Math.acos((Math.pow(a, 2)   Math.pow(b, 2) - Math.pow(c, 2)) / (2 * a * b)) * 180 / Math.PI);   if (startX <= mCenter && endX >= mCenter && startY < mCenter && endY < mCenter) {//上边顺时针越界,不管他  } else if (startX >= mCenter && endX <= mCenter && startY < mCenter && endY < mCenter) {//上边逆时针越界  arc = -arc;  } else if (startX <= mCenter && endX >= mCenter && startY > mCenter && endY > mCenter) {//下边逆时针越界  arc = -arc;  } else if (startX <= mCenter && endX >= mCenter && startY < mCenter && endY < mCenter) {//下边顺时针越界,不管他  } else if (endX >= mCenter && startX >= mCenter) {//这个时候表示在右半区  if (startY > endY) {
                    arc = -arc;  }
            } else if (endX < mCenter && startX < mCenter) {//此时在左半区  if (startY < endY) {
                    arc = -arc;  }
            }
        } if (Math.abs(arc) >= 0 && Math.abs(arc) <= 180) {//主要解决nan的问题  return arc;  } else { return 0;  }
    } /**  * dp转像素  *  * @param dpValue  * @return  */  public final int dip2px(float dpValue) { return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpValue, getContext().getResources().getDisplayMetrics());  } /**  * 刷新画布  **/  private void onDrawInvalidate() { synchronized (obj) {
            invalidate();  }
    } public int getCheckPosition() { return checkPosition;  } private OnWheelCheckListener mOnWheelCheckListener;   public void setOnWheelCheckListener(OnWheelCheckListener mOnWheelCheckListener) { this.mOnWheelCheckListener = mOnWheelCheckListener;  } public static interface OnWheelCheckListener { void onCheck(int position);  }


} 

标签: 菜单 圆形 旋转

实例下载地址

android 圆形可旋转菜单 示例代码下载

不能下载?内容有错? 点击这里报错 + 投诉 + 提问

好例子网口号:伸出你的我的手 — 分享

网友评论

发表评论

(您的评论需要经过审核才能显示)

查看所有0条评论>>

小贴士

感谢您为本站写下的评论,您的评论对其它用户来说具有重要的参考价值,所以请认真填写。

  • 类似“顶”、“沙发”之类没有营养的文字,对勤劳贡献的楼主来说是令人沮丧的反馈信息。
  • 相信您也不想看到一排文字/表情墙,所以请不要反馈意义不大的重复字符,也请尽量不要纯表情的回复。
  • 提问之前请再仔细看一遍楼主的说明,或许是您遗漏了。
  • 请勿到处挖坑绊人、招贴广告。既占空间让人厌烦,又没人会搭理,于人于己都无利。

关于好例子网

本站旨在为广大IT学习爱好者提供一个非营利性互相学习交流分享平台。本站所有资源都可以被免费获取学习研究。本站资源来自网友分享,对搜索内容的合法性不具有预见性、识别性、控制性,仅供学习研究,请务必在下载后24小时内给予删除,不得用于其他任何用途,否则后果自负。基于互联网的特殊性,平台无法对用户传输的作品、信息、内容的权属或合法性、安全性、合规性、真实性、科学性、完整权、有效性等进行实质审查;无论平台是否已进行审查,用户均应自行承担因其传输的作品、信息、内容而可能或已经产生的侵权或权属纠纷等法律责任。本站所有资源不代表本站的观点或立场,基于网友分享,根据中国法律《信息网络传播权保护条例》第二十二与二十三条之规定,若资源存在侵权或相关问题请联系本站客服人员,点此联系我们。关于更多版权及免责申明参见 版权及免责申明

;
报警