当前位置: 首页 > 编程日记 > 正文

android圆形旋转菜单,而对于移动转换功能支持

LZ该公司最近接手一个项目,需要写一个圆形旋转菜单,和菜单之间的移动换位支持,我本来以为这样的demo如若互联网是非常。想想你妈妈也帮不了我,空旋转,但它不能改变位置,所以LZ我们只能靠自己摸索。

最后LZ参考代码的在线部分。了一个自己定义的view最终实现了这个看似非常吊。却没有实际意义的功能。

在此贡献出来给广大码农们共享。

话不多说,先上代码:

自己定义view类:

public class RoundSpinView extends View {private Paint mPaint = new Paint();private PaintFlagsDrawFilter pfd;private int startMenu;   //菜单的第一张图片的资源id// stone列表private BigStone[] mStones;// 数目private static final int STONE_COUNT = 3;// 圆心坐标private int mPointX = 0, mPointY = 0;// 半径private int mRadius = 0;// 每两个点间隔的角度private int mDegreeDelta;private int menuRadius; // 菜单的半径private int mCur = -1; // 正在被移动的menu;private boolean[] quadrantTouched;   //对每一个象限触摸情况的记录// Touch detectionprivate GestureDetector mGestureDetector;private onRoundSpinViewListener mListener;  //自己定义事件监听器private final static int TO_ROTATE_BUTTON = 0;  //旋转button;private Handler handler = new Handler(){public void handleMessage(Message msg) {switch (msg.what) {case TO_ROTATE_BUTTON:float velocity = Float.parseFloat(msg.obj.toString());rotateButtons(velocity/75);velocity /= 1.0666F;new Thread(new FlingRunnable(velocity)).start();break;default:break;}};};public interface onRoundSpinViewListener{public void onSingleTapUp(int position);  //监听每一个菜单的单击事件}public RoundSpinView(Context context,AttributeSet attrs) {super(context,attrs);if(attrs!=null){TypedArray a = getContext().obtainStyledAttributes(attrs,R.styleable.RoundSpinView);startMenu = a.getResourceId(R.styleable.RoundSpinView_menuStart, 0);}pfd = new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG|Paint.FILTER_BITMAP_FLAG); mPaint.setColor(Color.WHITE);mPaint.setStrokeWidth(2);mPaint.setAntiAlias(true); //消除锯齿  mPaint.setStyle(Paint.Style.STROKE); //绘制空心圆 PathEffect effects = new DashPathEffect(new float[]{5,5,5,5},1);  mPaint.setPathEffect(effects);quadrantTouched = new boolean[] { false, false, false, false, false };mGestureDetector = new GestureDetector(getContext(),new MyGestureListener());setupStones();}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {// TODO Auto-generated method stubsuper.onMeasure(widthMeasureSpec, heightMeasureSpec);mPointX = this.getMeasuredWidth()/2;mPointY = this.getMeasuredHeight()/2;//初始化半径和菜单半径mRadius = mPointX-mPointX/5;menuRadius = (int)(mPointX/5.5);computeCoordinates();}/*** 初始化每一个点*/private void setupStones() {mStones = new BigStone[STONE_COUNT];BigStone stone;int angle = 270;mDegreeDelta = 360 / STONE_COUNT;for (int index = 0; index < STONE_COUNT; index++) {stone = new BigStone();if (angle >= 360) {angle -= 360;}else if(angle < 0){angle += 360;}stone.angle = angle;stone.bitmap = BitmapFactory.decodeResource(getResources(),startMenu + index);angle += mDegreeDelta;mStones[index] = stone;}}/*** 又一次计算每一个点的角度*/private void resetStonesAngle(float x, float y) {int angle = computeCurrentAngle(x, y);Log.d("RoundSpinView", "angle:" + angle);for (int index = 0; index < STONE_COUNT; index++) {mStones[index].angle = angle;angle += mDegreeDelta;}}/*** 计算每一个点的坐标*/private void computeCoordinates() {BigStone stone;for (int index = 0; index < STONE_COUNT; index++) {stone = mStones[index];stone.x = mPointX+ (float) (mRadius * Math.cos(Math.toRadians(stone.angle)));stone.y = mPointY+ (float) (mRadius * Math.sin(Math.toRadians(stone.angle)));}}/*** 计算某点的角度* * @param x* @param y* @return*/private int computeCurrentAngle(float x, float y) {float distance = (float) Math.sqrt(((x - mPointX) * (x - mPointX) + (y - mPointY)* (y - mPointY)));int degree = (int) (Math.acos((x - mPointX) / distance) * 180 / Math.PI);if (y < mPointY) {degree = -degree;}Log.d("RoundSpinView", "x:" + x + ",y:" + y + ",degree:" + degree);return degree;}private double startAngle;@Overridepublic boolean dispatchTouchEvent(MotionEvent event) {// resetStonesAngle(event.getX(), event.getY());// computeCoordinates();// invalidate();int x, y;if (event.getAction() == MotionEvent.ACTION_DOWN) {x = (int) event.getX();y = (int) event.getY();mCur = getInCircle(x, y);if (mCur == -1) {startAngle = computeCurrentAngle(x, y);}} else if (event.getAction() == MotionEvent.ACTION_MOVE) {x = (int) event.getX();y = (int) event.getY();if (mCur != -1) {mStones[mCur].x = x;mStones[mCur].y = y;invalidate();} else {double currentAngle = computeCurrentAngle(x, y);rotateButtons(startAngle - currentAngle);startAngle = currentAngle;}} else if (event.getAction() == MotionEvent.ACTION_UP) {x = (int) event.getX();y = (int) event.getY();if (mCur != -1) {computeCoordinates();int cur = getInCircle(x, y);if (cur != mCur && cur != -1) {int angle = mStones[mCur].angle;mStones[mCur].angle = mStones[cur].angle;mStones[cur].angle = angle;}computeCoordinates();invalidate();mCur = -1;}}// set the touched quadrant to truequadrantTouched[getQuadrant(event.getX() - mPointX,mPointY - event.getY())] = true;mGestureDetector.onTouchEvent(event);return true;}private class MyGestureListener extends SimpleOnGestureListener {@Overridepublic boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,float velocityY) {// get the quadrant of the start and the end of the flingint q1 = getQuadrant(e1.getX() - mPointX, mPointY - e1.getY());int q2 = getQuadrant(e2.getX() - mPointX, mPointY - e2.getY());// the inversed rotationsif ((q1 == 2 && q2 == 2 && Math.abs(velocityX) < Math.abs(velocityY))|| (q1 == 3 && q2 == 3)|| (q1 == 1 && q2 == 3)|| (q1 == 4 && q2 == 4 && Math.abs(velocityX) > Math.abs(velocityY))|| ((q1 == 2 && q2 == 3) || (q1 == 3 && q2 == 2))|| ((q1 == 3 && q2 == 4) || (q1 == 4 && q2 == 3))|| (q1 == 2 && q2 == 4 && quadrantTouched[3])|| (q1 == 4 && q2 == 2 && quadrantTouched[3])) {// CircleLayout.this.post(new FlingRunnable(-1// * (velocityX + velocityY)));new Thread(new FlingRunnable(velocityX+velocityY)).start();} else {// the normal rotation// CircleLayout.this// .post(new FlingRunnable(velocityX + velocityY));new Thread(new FlingRunnable(-(velocityX+velocityY))).start();}return true;}@Overridepublic boolean onSingleTapUp(MotionEvent e) {int cur = getInCircle((int)e.getX(),(int)e.getY());if(cur!=-1){if(mListener!=null){mListener.onSingleTapUp(cur);}
//				Toast.makeText(getContext(), "position:"+cur, 0).show();return true;}return false;}}private class FlingRunnable implements Runnable{private float velocity;public FlingRunnable(float velocity){this.velocity = velocity;}@Overridepublic void run() {// TODO Auto-generated method stubif(Math.abs(velocity)>=200){Message message = Message.obtain();message.what = TO_ROTATE_BUTTON;message.obj = velocity;handler.sendMessage(message);}}}/*** @return The selected quadrant.*/private static int getQuadrant(double x, double y) {if (x >= 0) {return y >= 0 ? 1 : 4;} else {}return y >= 0 ?

2 : 3; } /* * 旋转菜单button */ private void rotateButtons(double degree) { for (int i = 0; i < STONE_COUNT; i++) { mStones[i].angle -= degree; if (mStones[i].angle < 0) { mStones[i].angle += 360; }else if(mStones[i].angle >=360){ mStones[i].angle -= 360; } } computeCoordinates(); invalidate(); } @Override public void onDraw(Canvas canvas) { //画一个白色的圆环 canvas.drawCircle(mPointX, mPointY, mRadius, mPaint); //将每一个菜单画出来 for (int index = 0; index < STONE_COUNT; index++) { if (!mStones[index].isVisible) continue; drawInCenter(canvas, mStones[index].bitmap, mStones[index].x, mStones[index].y); } } /** * 把中心点放到中心处 * * @param canvas * @param bitmap * @param left * @param top */ private void drawInCenter(Canvas canvas, Bitmap bitmap, float left, float top) { Rect dst = new Rect(); dst.left = (int) (left - menuRadius); dst.right = (int) (left + menuRadius); dst.top = (int) (top - menuRadius); dst.bottom = (int) (top + menuRadius); canvas.setDrawFilter(pfd); canvas.drawBitmap(bitmap, null, dst, mPaint); } private int getInCircle(int x, int y) { for (int i = 0; i < STONE_COUNT; i++) { BigStone stone = mStones[i]; int mx = (int) stone.x; int my = (int) stone.y; if (((x - mx) * (x - mx) + (y - my) * (y - my)) < menuRadius * menuRadius) { return i; } } return -1; } public void setOnRoundSpinViewListener(onRoundSpinViewListener listener){ this.mListener = listener; } class BigStone { // 图片 Bitmap bitmap; // 角度 int angle; // x坐标 float x; // y坐标 float y; // 是否可见 boolean isVisible = true; } }

layout文件代码:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><com.example.roundspinviewdemo.view.RoundSpinViewandroid:id="@+id/rsv_test"android:layout_width="match_parent"android:layout_height="match_parent"android:layout_gravity="center"android:background="@drawable/menubkground"app:menuStart="@drawable/menu1" /></LinearLayout>
注意:必须加上这一条 :
xmlns:app="http://schemas.android.com/apk/res-auto"


此外必须加入attr文件设置相应的自己定义属性:

<?xml version="1.0" encoding="utf-8"?>
<resources><!-- 设置旋转菜单相应的第一张图片 --><declare-styleable name="RoundSpinView"><attr name="menuStart" format="reference" /></declare-styleable>
</resources>


接下来就是activity中的应用了:

public class MainActivity extends Activity implements onRoundSpinViewListener {private RoundSpinView rsv_test;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);initView();}private void initView(){rsv_test = (RoundSpinView)this.findViewById(R.id.rsv_test);rsv_test.setOnRoundSpinViewListener(this);}@Overridepublic void onSingleTapUp(int position) {// TODO Auto-generated method stubswitch (position) {case 0:Toast.makeText(MainActivity.this, "place:0", 0).show();break;case 1:Toast.makeText(MainActivity.this, "place:1", 0).show();break;case 2:Toast.makeText(MainActivity.this, "place:2", 0).show();break;default:break;}}}
注意:
rsv_test.setOnRoundSpinViewListener(this);
对自己定义view的自己定义监听器进行赋值


至此。你的项目就能够拥有看上去非常高大上的旋转换位菜单功能可怜

这里附上此demo相应的资源链接:点击打开链接


版权声明:本文博客原创文章,博客,未经同意,不得转载。

相关文章:

微信9年:张小龙指明方向,微信AI全面开放NLP能力

作者 | 夕颜责编 | 王金许出品 | AI科技大本营&#xff08;ID:rgznai100&#xff09;一年一度的微信公开课 Pro 在广州保利世贸博览馆如期举行。一大早&#xff0c;同在博览馆举办的广州年货促展会参会者&#xff0c;夹杂着参加腾讯公开课的与会者&#xff0c;让这里变得人流攒…

实现HTTP协议Get、Post和文件上传功能——使用WinHttp接口实现

在《使用WinHttp接口实现HTTP协议Get、Post和文件上传功能》一文中&#xff0c;我已经比较详细地讲解了如何使用WinHttp接口实现各种协议。在最近的代码梳理中&#xff0c;我觉得Post和文件上传模块可以得到简化&#xff0c;于是几乎重写了这两个功能的代码。因为Get、Post和文…

第一篇文章,做个纪念

第一篇文章,做个纪念,这个blog好吗?拭目以待!转载于:https://blog.51cto.com/197536/88241

Maven工程引入jar包(转)

Maven项目引入jar包的方法&#xff0c;希望能帮助有需要的朋友们 法一.手动导入&#xff1a;项目右键—>Build Path—>Configure Build Path—>选中Libraries—>点击Add External Jars—>选中已事先下好的Jar包导入即可。 法二.通过pom.xml文件的Dependencies标…

实现HTTP协议Get、Post和文件上传功能——使用libcurl接口实现

之前我们已经详细介绍了WinHttp接口如何实现Http的相关功能。本文我将主要讲解如何使用libcurl库去实现相关功能。&#xff08;转载请指明出于breaksoftware的csdn博客&#xff09; libcurl在http://curl.haxx.se/libcurl/有详细的介绍&#xff0c;有兴趣的朋友可以去读下。本文…

32岁程序员,补偿N+2:“谢谢裁我,让我翻倍!” 网友:榜样!

2019年的冬天&#xff0c;“冷”的有些频繁。12月19日&#xff0c;《马蜂窝被曝裁员40% UGC模式变现难&#xff1f;》爆火&#xff0c;据悉马蜂窝将裁员40%&#xff0c;交易中心成了“重灾区”&#xff0c;赔偿N2&#xff0c;留下的除搜索推荐、内容中心等核心部门外&#xff0…

山有木兮木有枝,心悦君兮君不知

《越人歌》今夕何夕兮&#xff0c;搴舟中流。 今日何日兮&#xff0c;得与王子同舟 蒙羞被好兮&#xff0c;不訾诟耻 心几烦而不绝兮&#xff0c;得知王子 山有木兮木有枝&#xff0c;心悦君兮君不知。本是《夜宴》中的&#xff0c;"山有木兮木有枝&#xff0c;心悦君兮君…

浅析电商、社区、游戏常用的 MySQL 架构

一般、或者必须是这样、MySQL 架构一定要结合业务来分析、设计、优化 所以不管是那种架构、根据业务要求组合成符合需求的即是最好的、不能泛泛而谈 同时、也必须注意数据的安全&#xff08;如ipsec,ssh&#xff0c;vpn传输&#xff09; 常见的架构都是进行业务切…

基于Co-Attention和Co-Excitation的少样本目标检测 | NeurIPS 2019

「免费学习 60 节公开课&#xff1a;投票页面&#xff0c;点击讲师头像」作者 | VincentLee来源 | 晓飞的算法工程笔记&#xff08;ID: gh_084c810bc839&#xff09;导读&#xff1a;论文提出CoAE少样本目标检测算法&#xff0c;该算法使用non-local block来提取目标图片与查询…

服务器架设笔记——搭建用户注册和验证功能

之前介绍的Apache Httpd相关内容&#xff0c;都是些零散的知识点。而实际运用中&#xff0c;我们要根据不同的业务&#xff0c;将这些知识点连接起来以形成各种组合&#xff0c;来满足我们的需求。&#xff08;转载请指明出于breaksoftware的csdn博客&#xff09; 本文我将以用…

项目管理过程中应注意的问题

软件项目从角色分工方面可以划分为研发、开发和实施三类&#xff0c;每个类型的项目有各自的管理过程。下面笔者就公司实施类项目的经历&#xff0c;从项目经理的角度谈一谈实施类项目管理过程中应该注意的一些问题&#xff0c;希望大家共勉。确定项目概况俗话说&#xff1a;“…

原创jQuery移动设备弹出框插件——msgalert.js

最近开发经常会用到顶部弹出框&#xff0c;虽然有现成的&#xff08;bootstrap等&#xff09;&#xff0c;但是都很臃肿&#xff0c;对于有些时候移动端活动页面有点大材小用。所以今晚花了20分钟写了一个通用的插件,我将其命名为msgalert.js。因为定位是jQuery插件&#xff0c…

AbutionGraph:构建以知识图谱为核心的下一代数据中台

「免费学习 60 节公开课&#xff1a;投票页面&#xff0c;点击讲师头像」作者 | 图特摩斯科技创始人闭雨哲出品 | AI科技大本营&#xff08;ID:rgznai100&#xff09;前言图特摩斯科技&#xff08;Thutmose&#xff09;基于自研的图形数据库AbutionGraph&#xff08;实时多维数…

服务器架设笔记——多模块和全局数据

随着项目工程的发展&#xff0c;多模块设计和性能优化是在所难免的。本文我将基于一些现实中可能遇到的需求&#xff0c;讲解如何在Apache的Httpd插件体系中实现这些功能。&#xff08;转载请指明出于breaksoftware的csdn博客&#xff09; 之前我碰到两个需求&#xff1a; 需要…

JSP学习笔记(七):使用JavaBean

bean.java publicclassB1 { publicString getString() { return"content"; } }page.jsp <%B1 b1 newB1(); out.print(b1.getString());%>

折返(Reentrancy)VS线程安全(Thread safety)

在Wiki上&#xff0c;折返例如&#xff0c;下面的定义&#xff08;接&#xff09; In computing, a computer program or subroutine is called reentrant if it can be interrupted in the middle of its execution and then safely called again ("re-entered") be…

服务器架设笔记——httpd插件支持mysql字符集选择

mysql数据库默认的字符集是latin1。默认情况下&#xff0c;我们编译的httpd插件是可以正常读取该类型的数据库&#xff0c;并且不会出现乱码。但是&#xff0c;如果我们的数据库变成其他格式&#xff0c;比如UTF8&#xff0c;那么默认读取出来的数据就是乱码&#xff0c;且无论…

只需3行代码自动生成高性能模型,支持4项任务,亚马逊发布开源库AutoGluon

作者 | KYLE WIGGERS编译 | AI科技大本营&#xff08;ID:rgznai100&#xff09;构建涉及图像、文本和表格数据集的机器学习应用并不容易。它需要特征工程或使用数据领域知识来创建使AI算法起作用的特征&#xff0c;还需要进行大量数据预处理&#xff0c;以确保训练模型时不会出…

在客户端(IE中)无法登录Citrix MetaFrame server的原因

当在IE中登录服务器时如果出现这面的错误提示&#xff1a; ERROR: The Citrix MetaFrame servers cannot process your request at this time. The Citrix XML Service object was not found. [404 Not Found] 我的原因是IE使用了8080端口&#xff0c;而我的Citrix XML servic…

虚拟化--015 配置VMware View Event database失败:

015 配置VMware View Event database失败&#xff1a;参考链接http://url.cn/VTq4zN 转载于:https://blog.51cto.com/williamliuwen/1686536

以金山界面库(openkui)为例思考和分析界面库的设计和实现——代码结构(完)

三年前&#xff0c;准备将金山界面库做一个全面的剖析。后来由于种种原因&#xff0c;这个系列被中断而一直没有更新。时过境迁&#xff0c;现在在windows上从事开发的人员越来越少&#xff0c;关注这块的技术的朋友也很少了。本以为这系列也随着技术的没落而不再被人所关注&am…

一包烟钱买到电动剃须刀,小米有品告诉你什么叫性价比

男人身上长得最快的是什么&#xff1f;答案是胡须。一名健康男性的胡须每天都要生长超过 0.4mm&#xff0c;比咱们头发的生长速度还快&#xff0c;这也是男人隔三差五就要剃须的原因之一。男人的一生是与胡子战斗一生&#xff0c;也是被剃须刀拖累的一生。出差办事儿&#xff0…

Label控件属性AssociatedControlID

可以使用Label控件来标注一个HTML表单字段。Label控件拥有属性AssociatedControlID,可以设置此属性来指向表示表单字段的ASP.NET控件。 例如,代码清单2-3中的页面含有一个简单的表单,表单包含两个字段用于输入名和姓。Label控件用于标注这两个TextBox控件。 Code<% Page Lan…

2015_8_21作业——有自翻译有复制他人的英语太差

date作用&#xff1a;打印或设置系统日期和时间格式:date [OPTION]...[FORMAT]date [u|--utc|--universal] [MMDDhhmm[[CC]YY][.ss]]|是或 多选一的选项 ...代表同类内容可多次选项&#xff1a;注意短选项后不可加号-d,--dateSTRING显示时间字符串但不是立即&#xff1a;如date…

WMI技术介绍和应用——接收事件

时隔两三年&#xff0c;再次更新WMI系列博文。好在功能在三年前就已经实现了&#xff0c;现在只要补充些实例即可。 之前介绍的基本都是查询静态数据&#xff0c;而本文将要介绍非常有意思的事件接收功能。&#xff08;转载请指明出于breaksoftware的csdn博客&#xff09; 监控…

WML标签速查手册

WML标签速查手册 结构相关标签语法及属性<wml><wml xml:lang"lang" >    content</wml> WML元素的共有属性主要有3个&#xff0c;即id、class和xml:lang属性。WML的所有元素都有两个核心属性&#xff0c;即标识(id)和类(class)属性。它们主要用…

Python 三十大实践、建议和技巧

所有参与投票的 CSDN 用户都参加抽奖活动群内公布奖项&#xff0c;还有更多福利赠送作者 | Erik-Jan van Baaren译者 | 凯隐编辑 | Jane出品 | AI科技大本营&#xff08;ID&#xff1a;rgznai100&#xff09;【导读】2020年&#xff0c;你又立了什么新的 Flag&#xff1f;新一年…

点击通知栏后打开Activity,并传参

为什么80%的码农都做不了架构师&#xff1f;>>> Reciver中intent new Intent(context, MessageDetailsaActivity_.class);intent.putExtra("freshMessageId", String.valueOf(push.getObid()));intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Inten…

WMI技术介绍和应用——执行方法

在之前的博文中&#xff0c;我们主要介绍了如何使用WMI查询信息和接收事件。本文将介绍WMI的另一种用法——执行方法。&#xff08;转载请指明出于breaksoftware的csdn博客&#xff09; 这块的内容在msdn中有详细的介绍&#xff0c;如果想看原版的可以参阅《Example: Calling a…

动态指定超链接参数的几种方法(Passing a JavaScript variable into href of )

情景&#xff1a;有些环境下我们需要根据页面中JavaScript变量的值来确定某个&#xff08;某些&#xff09;超链接的参数&#xff0c;如“http://www.bla.com/test.aspx?var1”中&#xff0c;究竟var1等于多少&#xff0c;要根据JavaScript变量来判定 方法一&#xff1a;很简单…