效果:

使用了自定义布局实现触摸旋转功能

1.创建item布局circle_menu_item.xml

<?xml version="1.0" encoding="UTF-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="wrap_content"android:layout_height="wrap_content"android:gravity="center"android:orientation="vertical"><!--圆盘菜单图标--><ImageViewandroid:id="@+id/id_circle_menu_item_image"android:layout_width="wrap_content"android:layout_height="wrap_content"android:visibility="gone" /><!--圆盘菜单文字--><TextViewandroid:id="@+id/id_circle_menu_item_text"android:layout_width="wrap_content"android:layout_height="wrap_content"android:textColor="@android:color/white"android:textSize="14.0dip"android:visibility="gone" />
</LinearLayout>

2.创建CricleMenuLayout自定义view

public class CricleMenuLayout extends ViewGroup {private int mRadius;/*** 该容器内child item的默认尺寸*/private static final float RADIO_DEFAULT_CHILD_DIMENSION = 1 / 4f;/*** 菜单的中心child的默认尺寸*/private float RADIO_DEFAULT_CENTERITEM_DIMENSION = 1 / 3f;/*** 该容器的内边距,无视padding属性,如需边距请用该变量*/private static final float RADIO_PADDING_LAYOUT = 1 / 12f;/*** 该容器的内边距,无视padding属性,如需边距请用该变量*/private float mPadding;/*** 当每秒移动角度达到该值时,认为是快速移动*/private static final int FLINGABLE_VALUE = 300;/*** 如果移动角度达到该值,则屏蔽点击*/private static final int NOCLICK_VALUE = 3;/*** 当每秒移动角度达到该值时,认为是快速移动*/private int mFlingableValue = FLINGABLE_VALUE;/*** 布局时的开始角度*/private double mStartAngle = 0;/*** 菜单项的文本*/private String[] mItemTexts;/*** 菜单项的图标*/private int[] mItemImgs;/*** 菜单的个数*/private int mMenuItemCount;/*** 检测按下到抬起时旋转的角度*/private float mTmpAngle;/*** 检测按下到抬起时使用的时间*/private long mDownTime;/*** 判断是否正在自动滚动*/private boolean isFling;private int mMenuItemLayoutId = R.layout.circle_menu_item;//单个按钮布局public CricleMenuLayout(Context context, AttributeSet attrs){super(context, attrs);// 无视边距setPadding(0, 0, 0, 0);}/*** 设置圆盘布局的宽高,并设置菜单按钮宽高*/@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {int resWidth = 0;int resHeight = 0;// 根据传入的参数,分别获取测量模式和测量值int width = MeasureSpec.getSize(widthMeasureSpec);int widthMode = MeasureSpec.getMode(widthMeasureSpec);int height = MeasureSpec.getSize(heightMeasureSpec);int heightMode = MeasureSpec.getMode(heightMeasureSpec);//如果宽或者高的测量模式非精确值if (widthMode != MeasureSpec.EXACTLY|| heightMode != MeasureSpec.EXACTLY) {// 主要设置为背景图的高度resWidth = getSuggestedMinimumWidth();// 如果未设置背景图片,则设置为屏幕宽高的默认值resWidth = resWidth == 0 ? getDefaultWidth() : resWidth;resHeight = getSuggestedMinimumHeight();// 如果未设置背景图片,则设置为屏幕宽高的默认值resHeight = resHeight == 0 ? getDefaultWidth() : resHeight;} else {// 如果都设置为精确值,则直接取小值;resWidth = resHeight = Math.min(width, height);}setMeasuredDimension(resWidth, resHeight);//设置布局宽高// 获得半径mRadius = Math.max(getMeasuredWidth(), getMeasuredHeight());// 菜单按钮数量final int count = getChildCount();// 菜单按钮尺寸int childSize = (int) (mRadius * RADIO_DEFAULT_CHILD_DIMENSION);// 菜单按钮为精确模式int childMode = MeasureSpec.EXACTLY;// 迭代测量for (int i = 0; i < count; i++) {final View child = getChildAt(i);if (child.getVisibility() == GONE) {continue;}// 计算menu item的尺寸;以及和设置好的模式,去对item进行测量int makeMeasureSpec = -1;if (child.getId() == R.id.id_circle_menu_item_center) {makeMeasureSpec = MeasureSpec.makeMeasureSpec((int) (mRadius * RADIO_DEFAULT_CENTERITEM_DIMENSION),childMode);} else {makeMeasureSpec = MeasureSpec.makeMeasureSpec(childSize,childMode);}child.measure(makeMeasureSpec, makeMeasureSpec);}mPadding = RADIO_PADDING_LAYOUT * mRadius;}/*** MenuItem的点击事件接口*/public interface OnMenuItemClickListener {void itemClick(View view, int pos);void itemCenterClick(View view);}/*** MenuItem的点击事件接口*/private OnMenuItemClickListener mOnMenuItemClickListener;/*** 设置MenuItem的点击事件接口** @param mOnMenuItemClickListener*/public void setOnMenuItemClickListener(OnMenuItemClickListener mOnMenuItemClickListener) {this.mOnMenuItemClickListener = mOnMenuItemClickListener;}/*** 设置menu item的位置*/@Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) {int layoutRadius = mRadius;// 获取子布局final int childCount = getChildCount();int left, top;// menu item 的尺寸int cWidth = (int) (layoutRadius * RADIO_DEFAULT_CHILD_DIMENSION);// 根据menu item的个数,计算角度float angleDelay = 360 / (getChildCount() - 1);// 遍历去设置menuitem的位置for (int i = 0; i < childCount; i++) {final View child = getChildAt(i);if (child.getId() == R.id.id_circle_menu_item_center)continue;if (child.getVisibility() == GONE) {continue;}mStartAngle %= 360;// 计算,中心点到menu item中心的距离float tmp = layoutRadius / 2f - cWidth / 2 - mPadding;// tmp cosa 即menu item中心点的横坐标left = layoutRadius/ 2+ (int) Math.round(tmp* Math.cos(Math.toRadians(mStartAngle)) - 1 / 2f* cWidth);// tmp sina 即menu item的纵坐标top = layoutRadius/ 2+ (int) Math.round(tmp* Math.sin(Math.toRadians(mStartAngle)) - 1 / 2f* cWidth);child.layout(left, top, left + cWidth, top + cWidth);// 叠加尺寸mStartAngle += angleDelay;}// 找到中心的view,如果存在设置onclick事件View cView = findViewById(R.id.id_circle_menu_item_center);if (cView != null) {cView.setOnClickListener(new OnClickListener() {public void onClick(View v) {if (mOnMenuItemClickListener != null) {mOnMenuItemClickListener.itemCenterClick(v);}}});// 设置center item位置int cl = layoutRadius / 2 - cView.getMeasuredWidth() / 2;int cr = cl + cView.getMeasuredWidth();cView.layout(cl, cl, cr, cr);}}/*** 记录上一次的x,y坐标*/private float mLastX;private float mLastY;/*** 自动滚动的Runnable*/private AutoFlingRunnable mFlingRunnable;/*** 菜单选择触摸监听 手指触摸事件*/@Overridepublic boolean dispatchTouchEvent(MotionEvent event) {float x = event.getX();float y = event.getY();switch (event.getAction()) {case MotionEvent.ACTION_DOWN:                   //手指按下事件mLastX = x;                                 //记录x坐标mLastY = y;                                 //记录y坐标mDownTime = System.currentTimeMillis();     //记录按下的时间mTmpAngle = 0;                              //记录按下到抬起时旋转的角度if (isFling) {                              // 如果当前已经在快速滚动removeCallbacks(mFlingRunnable);       // 移除快速滚动的回调isFling = false;                       //标记为没有快速滚动return true;}break;case MotionEvent.ACTION_MOVE:                    //手指移动事件float start = getAngle(mLastX, mLastY);     //获得开始的角度float end = getAngle(x, y);                  //获得当前的角度// 如果是一、四象限,则直接end-start,角度值都是正值if (getQuadrant(x, y) == 1 || getQuadrant(x, y) == 4) {mStartAngle += end - start;         //布局时的开始角度mTmpAngle += end - start;           //按下到抬起时旋转的角度} else// 二、三象限,色角度值是负值{mStartAngle += start - end;mTmpAngle += start - end;}// 重新布局requestLayout();mLastX = x; //记录x坐标mLastY = y; //记录y坐标break;case MotionEvent.ACTION_UP://手指抬起事件// 计算,每秒移动的角度float anglePerSecond = mTmpAngle * 1000/ (System.currentTimeMillis() - mDownTime);// 如果达到该值认为是快速移动if (Math.abs(anglePerSecond) > mFlingableValue && !isFling)   {// post一个任务,去自动滚动post(mFlingRunnable = new AutoFlingRunnable(anglePerSecond));return true;}// 如果当前旋转角度超过NOCLICK_VALUE屏蔽点击if (Math.abs(mTmpAngle) > NOCLICK_VALUE) {return true;}break;}return super.dispatchTouchEvent(event);}/*** 根据触摸的位置,计算角度*/private float getAngle(float xTouch, float yTouch) {double x = xTouch - (mRadius / 2d);double y = yTouch - (mRadius / 2d);return (float) (Math.asin(y / Math.hypot(x, y)) * 180 / Math.PI);}/*** 根据当前位置计算象限*/private int getQuadrant(float x, float y) {int tmpX = (int) (x - mRadius / 2);int tmpY = (int) (y - mRadius / 2);if (tmpX >= 0) {return tmpY >= 0 ? 4 : 1;} else {return tmpY >= 0 ? 3 : 2;}}/*** 设置菜单条目的图标和文本** @param resIds*/public void setMenuItemIconsAndTexts(int[] resIds, String[] texts) {mItemImgs = resIds;mItemTexts = texts;// 参数检查if (resIds == null && texts == null) {throw new IllegalArgumentException("菜单项文本和图片至少设置其一");}// 初始化mMenuCountmMenuItemCount = resIds == null ? texts.length : resIds.length;if (resIds != null && texts != null) {mMenuItemCount = Math.min(resIds.length, texts.length);}addMenuItems();}/*** 添加菜单项*/private void addMenuItems() {LayoutInflater mInflater = LayoutInflater.from(getContext());/*** 根据用户设置的参数,初始化view*/for (int i = 0; i < mMenuItemCount; i++) {final int j = i;View view = mInflater.inflate(mMenuItemLayoutId, this, false);ImageView iv = (ImageView) view.findViewById(R.id.id_circle_menu_item_image);TextView tv = (TextView) view.findViewById(R.id.id_circle_menu_item_text);if (iv != null) {iv.setVisibility(View.VISIBLE);iv.setImageResource(mItemImgs[i]);iv.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {if (mOnMenuItemClickListener != null) {mOnMenuItemClickListener.itemClick(v, j);}}});}if (tv != null) {tv.setVisibility(View.VISIBLE);tv.setText(mItemTexts[i]);}// 添加view到容器中addView(view);}}/*** 获得默认该layout的尺寸*/private int getDefaultWidth() {WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);DisplayMetrics outMetrics = new DisplayMetrics();wm.getDefaultDisplay().getMetrics(outMetrics);return Math.min(outMetrics.widthPixels, outMetrics.heightPixels);}/*** 自动滚动的任务*/private class AutoFlingRunnable implements Runnable {private float angelPerSecond;public AutoFlingRunnable(float velocity) {this.angelPerSecond = velocity;}public void run() {// 如果小于20,则停止if ((int) Math.abs(angelPerSecond) < 20) {isFling = false;return;}isFling = true;// 不断改变mStartAngle,让其滚动,/30为了避免滚动太快mStartAngle += (angelPerSecond / 30);// 逐渐减小这个值angelPerSecond /= 1.0666F;postDelayed(this, 30);// 重新布局requestLayout();}}
}

3.CricleMenuLayout的使用

先在布局中添加CricleMenuLayout:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:background="@drawable/bg"android:gravity="center_vertical"android:orientation="vertical"><!--自定义圆盘菜单--><com.example.criclemenudemo.CricleMenuLayoutandroid:id="@+id/id_menulayout"android:layout_width="match_parent"android:layout_height="match_parent"android:background="@drawable/circle_bg"android:padding="100dp"><!--圆盘中间位置布局--><RelativeLayoutandroid:id="@+id/id_circle_menu_item_center"android:layout_width="wrap_content"android:layout_height="wrap_content"><!--指南针图片--><ImageViewandroid:id="@+id/iv_znz"android:layout_width="104.0dip"android:layout_height="104.0dip"android:layout_centerInParent="true"android:background="@drawable/znz" /><!--指南针外框--><ImageViewandroid:layout_width="116.0dip"android:layout_height="116.0dip"android:layout_centerInParent="true"android:background="@drawable/turnplate_mask_unlogin_normal" /></RelativeLayout></com.example.criclemenudemo.CricleMenuLayout>
</LinearLayout>

菜单的实现:

public class MainActivity extends AppCompatActivity {private CricleMenuLayout mCircleMenuLayout;//自定义圆盘菜单private String[] mItemTexts = new String[]{"放大镜 ", "尺子", "分贝测试仪", "手电筒","计算器", "SOS"};//圆盘菜单显示文字private int[] mItemImgs = new int[]{R.drawable.home_mbank_1_normal,R.drawable.home_mbank_2_normal, R.drawable.home_mbank_3_normal,R.drawable.home_mbank_4_normal, R.drawable.home_mbank_5_normal,R.drawable.home_mbank_6_normal};//圆盘菜单显示图片@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);//初始化圆盘控件mCircleMenuLayout = (CricleMenuLayout) findViewById(R.id.id_menulayout);//初始化圆盘控件菜单mCircleMenuLayout.setMenuItemIconsAndTexts(mItemImgs, mItemTexts);//点击事件mCircleMenuLayout.setOnMenuItemClickListener(new CricleMenuLayout.OnMenuItemClickListener() {@Overridepublic void itemClick(View view, int pos) {Toast.makeText(MainActivity.this,mItemTexts[pos],Toast.LENGTH_SHORT).show();}@Overridepublic void itemCenterClick(View view) {}});}
}

android---转轮菜单相关推荐

  1. android圆形菜单

    实习第三天开发在android圆形菜单 知识参考:http://www.cnblogs.com/zwl12549/archive/2011/04/13/2015366.html和http://www. ...

  2. android开发菜单,Android学习指南之十六:Android菜单详解

    Android用户界面的组成除了View外,还包括菜单和对话框,本节我们先来学习一下菜单的使用方法. 菜单Menu是Android用户界面中最常见的元素之一,使用非常频繁.Android菜单主要分为三 ...

  3. android选项菜单源代码,Android应用程序----UI界面控件(菜单menu)

    菜单是应用程序中非常重要的组成部分,能够在不占用界面空间的前提下,为应用程序提供了统一的功能和设置界面,并为程序开发人员提供了易于使用的编程接口 Android系统支持三种菜单 选项菜单(Option ...

  4. android 按钮 菜单,(期末要考啊)Android的menu(菜单)按钮的使用

    (期末要考啊)Android的menu(菜单)按钮的使用 安卓 Android的菜单按钮 使用xml的方式创建菜单项在res下的menu目录下新建一个main.xml文件,以menu为根节点,添加一个 ...

  5. Android用表格布局做菜单栏,Android开发菜单布局之表格布局示例

    本文实例讲述了android开发菜单布局之表格布局.分享给大家供大家参考,具体如下: 多用于静态菜单页面 xml代码 代码内带详细解释 xmlns:android="http://schem ...

  6. android菜单功能,Android“设置”菜单

    本部分介绍了实现和自定义 Android"设置"菜单可以使用的选项. 设置主屏幕 在 Android 7.0 及更高版本中,"设置主屏幕"页面因增添了建议的设置 ...

  7. Android 悬浮菜单(按钮) BoomMenu(样式大合集)

    Android 悬浮菜单 BoomMenu(样式大合集) 一.样式大集合:(总有一款适合你) BoomMenu 使用 一.Demo 效果演示: 二.导入引用 dependencies { - comp ...

  8. android悬浮 按钮列表,Android悬浮菜单按钮FloatingActionButton实现

    [实例简介] Android悬浮菜单按钮FloatingActionButton实现,可直接拿到项目中集成使用.希望大家多多关注我的博客:https://blog.csdn.net/k57103983 ...

  9. Android滑动菜单框架完全解析,教你如何一分钟实现滑动菜单特效

    转载请注明出处:http://blog.csdn.net/sinyu890807/article/details/8744400 之前我向大家介绍了史上最简单的滑动菜单的实现方式,相信大家都还记得.如 ...

  10. Android 上下文菜单实现 context Menu

    Android 上下文菜单实现 2011年3月31日9:25 来源:Android中文网 我有话说(2人参与) 首先解释一下什么是上下文菜单,Android中上下文菜单就是ContextMenu.他的 ...

最新文章

  1. Nat. Commun | 基于网络的药物组合预测
  2. [置顶] 深入浅出Javascript(三)创建自定义对象以及属性、方法
  3. 机房收费系统----可行性分析报告
  4. Hibernate一对多关联映射及cascade级联操作
  5. 删除msconfig启动项不打勾的东西
  6. 一次看完2019技术好文,快收藏!
  7. Nginx在多层代理下获取真实客户端IP地址
  8. React开发(189):ant design textarea允许清除allowClear
  9. Github项目|几行代码即可实现人脸检测、目标检测的开源计算机视觉库
  10. lodash 常用的方法总结(持续更新)
  11. ak和sk怎么认证 海康威视_公有云API的认证方式:AK/SK 简介
  12. mysql 比较一个字符串_比较MySQL中的两个字符串?
  13. 【软件测试】简述自顶向下和自底向上两种集成测试方法
  14. contos7改分辨率_centos7分辨率怎么设置-修改centos7分辨率的方法 - 河东软件园
  15. React native连续按两次退出app
  16. 别人重构了整套代码,汝还不知道哪里有问题,相差几个等级?
  17. 网页顶部广告展开与收起
  18. KUKA机器人视觉1
  19. MySQL索引(最左匹配查询规则)
  20. 某网页在线视频有声音无图像

热门文章

  1. stm32心率监测系统(心率监测,wifi上传,APP显示,上位机显示)
  2. numpy pandas matplotlib 学习笔记
  3. 苹果手机usb共享网络连接ubuntu连接不上解决方案
  4. ios html 编码转换,IOS中编码转换方法(转)
  5. 三春过后诸芳尽. 荼蘼
  6. day---06 文件的操作
  7. python五边形的代码_python正五边形代码
  8. 介绍120 个相见恨晚的神器网站--学习、搜索、图片、视频样样不少!
  9. 阿里云大数据分析师(ACP)认证,该如何备考?
  10. SQL语句把一个表中的a字段的值赋值到另一个字段b