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

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

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

话不多说,先上代码:

自己定义view类:

  1. public class RoundSpinView extends View {
  2. private Paint mPaint = new Paint();
  3. private PaintFlagsDrawFilter pfd;
  4.  
  5. private int startMenu; //菜单的第一张图片的资源id
  6.  
  7. // stone列表
  8. private BigStone[] mStones;
  9. // 数目
  10. private static final int STONE_COUNT = 3;
  11.  
  12. // 圆心坐标
  13. private int mPointX = 0, mPointY = 0;
  14. // 半径
  15. private int mRadius = 0;
  16. // 每两个点间隔的角度
  17. private int mDegreeDelta;
  18.  
  19. private int menuRadius; // 菜单的半径
  20.  
  21. private int mCur = -1; // 正在被移动的menu;
  22.  
  23. private boolean[] quadrantTouched; //对每一个象限触摸情况的记录
  24.  
  25. // Touch detection
  26. private GestureDetector mGestureDetector;
  27.  
  28. private onRoundSpinViewListener mListener; //自己定义事件监听器
  29.  
  30. private final static int TO_ROTATE_BUTTON = 0; //旋转button;
  31.  
  32. private Handler handler = new Handler(){
  33. public void handleMessage(Message msg) {
  34. switch (msg.what) {
  35. case TO_ROTATE_BUTTON:
  36. float velocity = Float.parseFloat(msg.obj.toString());
  37. rotateButtons(velocity/75);
  38. velocity /= 1.0666F;
  39. new Thread(new FlingRunnable(velocity)).start();
  40. break;
  41.  
  42. default:
  43. break;
  44. }
  45. };
  46. };
  47.  
  48. public interface onRoundSpinViewListener{
  49. public void onSingleTapUp(int position); //监听每一个菜单的单击事件
  50. }
  51.  
  52. public RoundSpinView(Context context,AttributeSet attrs) {
  53. super(context,attrs);
  54. if(attrs!=null){
  55. TypedArray a = getContext().obtainStyledAttributes(attrs,
  56. R.styleable.RoundSpinView);
  57. startMenu = a.getResourceId(R.styleable.RoundSpinView_menuStart, 0);
  58. }
  59. pfd = new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG|Paint.FILTER_BITMAP_FLAG);
  60. mPaint.setColor(Color.WHITE);
  61. mPaint.setStrokeWidth(2);
  62. mPaint.setAntiAlias(true); //消除锯齿
  63. mPaint.setStyle(Paint.Style.STROKE); //绘制空心圆
  64. PathEffect effects = new DashPathEffect(new float[]{5,5,5,5},1);
  65. mPaint.setPathEffect(effects);
  66.  
  67. quadrantTouched = new boolean[] { false, false, false, false, false };
  68. mGestureDetector = new GestureDetector(getContext(),
  69. new MyGestureListener());
  70.  
  71. setupStones();
  72. }
  73.  
  74. @Override
  75. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  76. // TODO Auto-generated method stub
  77. super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  78. mPointX = this.getMeasuredWidth()/2;
  79. mPointY = this.getMeasuredHeight()/2;
  80.  
  81. //初始化半径和菜单半径
  82. mRadius = mPointX-mPointX/5;
  83. menuRadius = (int)(mPointX/5.5);
  84.  
  85. computeCoordinates();
  86. }
  87.  
  88. /**
  89. * 初始化每一个点
  90. */
  91. private void setupStones() {
  92. mStones = new BigStone[STONE_COUNT];
  93. BigStone stone;
  94. int angle = 270;
  95. mDegreeDelta = 360 / STONE_COUNT;
  96.  
  97. for (int index = 0; index < STONE_COUNT; index++) {
  98. stone = new BigStone();
  99. if (angle >= 360) {
  100. angle -= 360;
  101. }else if(angle < 0){
  102. angle += 360;
  103. }
  104. stone.angle = angle;
  105. stone.bitmap = BitmapFactory.decodeResource(getResources(),
  106. startMenu + index);
  107. angle += mDegreeDelta;
  108.  
  109. mStones[index] = stone;
  110. }
  111. }
  112.  
  113. /**
  114. * 又一次计算每一个点的角度
  115. */
  116. private void resetStonesAngle(float x, float y) {
  117. int angle = computeCurrentAngle(x, y);
  118. Log.d("RoundSpinView", "angle:" + angle);
  119. for (int index = 0; index < STONE_COUNT; index++) {
  120. mStones[index].angle = angle;
  121. angle += mDegreeDelta;
  122. }
  123. }
  124.  
  125. /**
  126. * 计算每一个点的坐标
  127. */
  128. private void computeCoordinates() {
  129. BigStone stone;
  130. for (int index = 0; index < STONE_COUNT; index++) {
  131. stone = mStones[index];
  132. stone.x = mPointX
  133. + (float) (mRadius * Math.cos(Math.toRadians(stone.angle)));
  134. stone.y = mPointY
  135. + (float) (mRadius * Math.sin(Math.toRadians(stone.angle)));
  136. }
  137. }
  138.  
  139. /**
  140. * 计算某点的角度
  141. *
  142. * @param x
  143. * @param y
  144. * @return
  145. */
  146. private int computeCurrentAngle(float x, float y) {
  147. float distance = (float) Math
  148. .sqrt(((x - mPointX) * (x - mPointX) + (y - mPointY)
  149. * (y - mPointY)));
  150. int degree = (int) (Math.acos((x - mPointX) / distance) * 180 / Math.PI);
  151. if (y < mPointY) {
  152. degree = -degree;
  153. }
  154.  
  155. Log.d("RoundSpinView", "x:" + x + ",y:" + y + ",degree:" + degree);
  156. return degree;
  157. }
  158.  
  159. private double startAngle;
  160.  
  161. @Override
  162. public boolean dispatchTouchEvent(MotionEvent event) {
  163. // resetStonesAngle(event.getX(), event.getY());
  164. // computeCoordinates();
  165. // invalidate();
  166.  
  167. int x, y;
  168. if (event.getAction() == MotionEvent.ACTION_DOWN) {
  169. x = (int) event.getX();
  170. y = (int) event.getY();
  171. mCur = getInCircle(x, y);
  172. if (mCur == -1) {
  173. startAngle = computeCurrentAngle(x, y);
  174. }
  175. } else if (event.getAction() == MotionEvent.ACTION_MOVE) {
  176. x = (int) event.getX();
  177. y = (int) event.getY();
  178. if (mCur != -1) {
  179. mStones[mCur].x = x;
  180. mStones[mCur].y = y;
  181. invalidate();
  182. } else {
  183. double currentAngle = computeCurrentAngle(x, y);
  184. rotateButtons(startAngle - currentAngle);
  185. startAngle = currentAngle;
  186. }
  187. } else if (event.getAction() == MotionEvent.ACTION_UP) {
  188. x = (int) event.getX();
  189. y = (int) event.getY();
  190. if (mCur != -1) {
  191. computeCoordinates();
  192. int cur = getInCircle(x, y);
  193. if (cur != mCur && cur != -1) {
  194. int angle = mStones[mCur].angle;
  195. mStones[mCur].angle = mStones[cur].angle;
  196. mStones[cur].angle = angle;
  197. }
  198. computeCoordinates();
  199. invalidate();
  200. mCur = -1;
  201. }
  202. }
  203.  
  204. // set the touched quadrant to true
  205. quadrantTouched[getQuadrant(event.getX() - mPointX,
  206. mPointY - event.getY())] = true;
  207. mGestureDetector.onTouchEvent(event);
  208. return true;
  209. }
  210.  
  211. private class MyGestureListener extends SimpleOnGestureListener {
  212. @Override
  213. public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
  214. float velocityY) {
  215. // get the quadrant of the start and the end of the fling
  216. int q1 = getQuadrant(e1.getX() - mPointX, mPointY - e1.getY());
  217. int q2 = getQuadrant(e2.getX() - mPointX, mPointY - e2.getY());
  218.  
  219. // the inversed rotations
  220. if ((q1 == 2 && q2 == 2 && Math.abs(velocityX) < Math
  221. .abs(velocityY))
  222. || (q1 == 3 && q2 == 3)
  223. || (q1 == 1 && q2 == 3)
  224. || (q1 == 4 && q2 == 4 && Math.abs(velocityX) > Math
  225. .abs(velocityY))
  226. || ((q1 == 2 && q2 == 3) || (q1 == 3 && q2 == 2))
  227. || ((q1 == 3 && q2 == 4) || (q1 == 4 && q2 == 3))
  228. || (q1 == 2 && q2 == 4 && quadrantTouched[3])
  229. || (q1 == 4 && q2 == 2 && quadrantTouched[3])) {
  230.  
  231. // CircleLayout.this.post(new FlingRunnable(-1
  232. // * (velocityX + velocityY)));
  233. new Thread(new FlingRunnable(velocityX+velocityY)).start();
  234. } else {
  235. // the normal rotation
  236. // CircleLayout.this
  237. // .post(new FlingRunnable(velocityX + velocityY));
  238. new Thread(new FlingRunnable(-(velocityX+velocityY))).start();
  239. }
  240.  
  241. return true;
  242.  
  243. }
  244.  
  245. @Override
  246. public boolean onSingleTapUp(MotionEvent e) {
  247.  
  248. int cur = getInCircle((int)e.getX(),(int)e.getY());
  249. if(cur!=-1){
  250. if(mListener!=null){
  251. mListener.onSingleTapUp(cur);
  252. }
  253. // Toast.makeText(getContext(), "position:"+cur, 0).show();
  254. return true;
  255. }
  256. return false;
  257. }
  258.  
  259. }
  260.  
  261. private class FlingRunnable implements Runnable{
  262.  
  263. private float velocity;
  264.  
  265. public FlingRunnable(float velocity){
  266. this.velocity = velocity;
  267. }
  268.  
  269. @Override
  270. public void run() {
  271. // TODO Auto-generated method stub
  272. if(Math.abs(velocity)>=200){
  273. Message message = Message.obtain();
  274. message.what = TO_ROTATE_BUTTON;
  275. message.obj = velocity;
  276. handler.sendMessage(message);
  277. }
  278. }
  279.  
  280. }
  281.  
  282. /**
  283. * @return The selected quadrant.
  284. */
  285. private static int getQuadrant(double x, double y) {
  286. if (x >= 0) {
  287. return y >= 0 ? 1 : 4;
  288. } else {
  289. }
  290. return y >= 0 ?
  291.  
  292. 2 : 3;
  293. }
  294.  
  295. /*
  296. * 旋转菜单button
  297. */
  298. private void rotateButtons(double degree) {
  299. for (int i = 0; i < STONE_COUNT; i++) {
  300. mStones[i].angle -= degree;
  301. if (mStones[i].angle < 0) {
  302. mStones[i].angle += 360;
  303. }else if(mStones[i].angle >=360){
  304. mStones[i].angle -= 360;
  305. }
  306. }
  307.  
  308. computeCoordinates();
  309. invalidate();
  310. }
  311.  
  312. @Override
  313. public void onDraw(Canvas canvas) {
  314. //画一个白色的圆环
  315. canvas.drawCircle(mPointX, mPointY, mRadius, mPaint);
  316.  
  317. //将每一个菜单画出来
  318. for (int index = 0; index < STONE_COUNT; index++) {
  319. if (!mStones[index].isVisible)
  320. continue;
  321. drawInCenter(canvas, mStones[index].bitmap, mStones[index].x,
  322. mStones[index].y);
  323. }
  324. }
  325.  
  326. /**
  327. * 把中心点放到中心处
  328. *
  329. * @param canvas
  330. * @param bitmap
  331. * @param left
  332. * @param top
  333. */
  334. private void drawInCenter(Canvas canvas, Bitmap bitmap, float left,
  335. float top) {
  336. Rect dst = new Rect();
  337. dst.left = (int) (left - menuRadius);
  338. dst.right = (int) (left + menuRadius);
  339. dst.top = (int) (top - menuRadius);
  340. dst.bottom = (int) (top + menuRadius);
  341. canvas.setDrawFilter(pfd);
  342. canvas.drawBitmap(bitmap, null, dst, mPaint);
  343. }
  344.  
  345. private int getInCircle(int x, int y) {
  346. for (int i = 0; i < STONE_COUNT; i++) {
  347. BigStone stone = mStones[i];
  348. int mx = (int) stone.x;
  349. int my = (int) stone.y;
  350. if (((x - mx) * (x - mx) + (y - my) * (y - my)) < menuRadius
  351. * menuRadius) {
  352. return i;
  353. }
  354. }
  355. return -1;
  356. }
  357.  
  358. public void setOnRoundSpinViewListener(onRoundSpinViewListener listener){
  359. this.mListener = listener;
  360. }
  361.  
  362. class BigStone {
  363.  
  364. // 图片
  365. Bitmap bitmap;
  366.  
  367. // 角度
  368. int angle;
  369.  
  370. // x坐标
  371. float x;
  372.  
  373. // y坐标
  374. float y;
  375.  
  376. // 是否可见
  377. boolean isVisible = true;
  378. }
  379. }

layout文件代码:

  1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2. xmlns:app="http://schemas.android.com/apk/res-auto"
  3. android:layout_width="match_parent"
  4. android:layout_height="match_parent"
  5. android:orientation="vertical"
  6. >
  7.  
  8. <com.example.roundspinviewdemo.view.RoundSpinView
  9. android:id="@+id/rsv_test"
  10. android:layout_width="match_parent"
  11. android:layout_height="match_parent"
  12. android:layout_gravity="center"
  13. android:background="@drawable/menubkground"
  14. app:menuStart="@drawable/menu1" />
  15.  
  16. </LinearLayout>

注意:必须加上这一条 :

  1. xmlns:app="http://schemas.android.com/apk/res-auto"

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

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

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

  1. public class MainActivity extends Activity implements onRoundSpinViewListener {
  2.  
  3. private RoundSpinView rsv_test;
  4.  
  5. @Override
  6. protected void onCreate(Bundle savedInstanceState) {
  7. super.onCreate(savedInstanceState);
  8. setContentView(R.layout.activity_main);
  9. initView();
  10. }
  11.  
  12. private void initView(){
  13. rsv_test = (RoundSpinView)this.findViewById(R.id.rsv_test);
  14. rsv_test.setOnRoundSpinViewListener(this);
  15. }
  16.  
  17. @Override
  18. public void onSingleTapUp(int position) {
  19. // TODO Auto-generated method stub
  20. switch (position) {
  21. case 0:
  22. Toast.makeText(MainActivity.this, "place:0", 0).show();
  23. break;
  24. case 1:
  25. Toast.makeText(MainActivity.this, "place:1", 0).show();
  26. break;
  27. case 2:
  28. Toast.makeText(MainActivity.this, "place:2", 0).show();
  29. break;
  30. default:
  31. break;
  32. }
  33. }
  34.  
  35. }

注意:

  1. rsv_test.setOnRoundSpinViewListener(this);

对自己定义view的自己定义监听器进行赋值

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

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvc3A2NjQ1NTk3/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">



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

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

android圆形旋转菜单,而对于移动转换功能支持的更多相关文章

  1. [Android阅读代码]圆形旋转菜单CircleMenu

    项目名称:圆形旋转菜单CircleMenu 原版项目代码下载 感谢原作者开源

  2. android简洁饼状图组件、圆形Menu菜单、画板画笔应用、答题应用等源码

    Android精选源码 android自动监听复制内容源码 Android上简洁轻量级的饼图控件 好看的 Android 圆形 Menu 菜单效果 android画笔.画板功能效果的实现 Androi ...

  3. Android 自定义圆形旋转进度条,仿微博头像加载效果

    微博 App 的用户头像有一个圆形旋转进度条的加载效果,看上去效果非常不错,如图所示: 据说 Instagram 也采用了这种效果.最近抽空研究了一下,最后实现的效果是这样: 基本上能模拟出个大概,代 ...

  4. 3D旋转菜单

    今天来个3D旋转菜单,是纯css3实现的,主要用到transform,transition,backface-visibility. 主要是transform这个变换,它是今天猪脚. transfor ...

  5. android 屏幕旋转

    转自:http://blog.csdn.net/oyzhizhong/article/details/8131799 屏是LANDSCAPE的,要让它默认显示为PORTRAIT. 1.kernel里要 ...

  6. Android 3D滑动菜单完全解析,实现推拉门式的立体特效

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/10471245 在上一篇文章中,我们学习了Camera的基本用法,并借助它们编写了一 ...

  7. Android中轴旋转特效实现,制作别样的图片浏览器

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/10766017 Android API Demos中有很多非常Nice的例子,这些例 ...

  8. Android立体旋转动画实现与封装(支持以X、Y、Z三个轴为轴心旋转)

    本文主要介绍Android立体旋转动画,或者3D旋转,下图是我自己实现的一个界面 立体旋转分为以下三种: 1. 以X轴为轴心旋转 2. 以Y轴为轴心旋转 3. 以Z轴为轴心旋转--这种等价于andro ...

  9. Android 屏幕旋转 处理 AsyncTask 和 ProgressDialog 的最佳方案

    的最佳方案 标签: Android屏幕旋转AsyncTaskProgressDialog 2014-07-19 09:25 39227人阅读 评论(46) 收藏 举报 分类: [android 进阶之 ...

随机推荐

  1. STATUS CODE: 91, occurs when trying to move media from one volume pool to another.

    Overview:Symantec NetBackup (tm) will not allow a tape with active images to be moved from one volum ...

  2. 【p092】分数线划定

    Time Limit: 1 second Memory Limit: 128 MB [问题描述] 世博会志愿者的选拔工作正在A市如火如荼地进行.为了选拔最合适的人才,A市对所有报名的选手进行了笔试,笔 ...

  3. [Preact] Use State and Props in the Component Render Function

    Preact offers, in addition to the regular component API from React, the ability to access both props ...

  4. js中json法创建对象(json里面的:相当于js里面的=)

    js中json法创建对象(json里面的:相当于js里面的=) 一.总结 json里面的:相当于js里面的= 4.json创建js对象解决命名冲突:多个人为同一个页面写js的话,命名冲突就有可能发生, ...

  5. (十)RabbitMQ消息队列-高可用集群部署实战

    原文:(十)RabbitMQ消息队列-高可用集群部署实战 前几章讲到RabbitMQ单主机模式的搭建和使用,我们在实际生产环境中出于对性能还有可用性的考虑会采用集群的模式来部署RabbitMQ. Ra ...

  6. 洛谷 P2197 nim游戏

    洛谷 P2197 nim游戏 题目描述 甲,乙两个人玩Nim取石子游戏. nim游戏的规则是这样的:地上有n堆石子(每堆石子数量小于10000),每人每次可从任意一堆石子里取出任意多枚石子扔掉,可以取 ...

  7. 【u245】机房病毒

    Time Limit: 1 second Memory Limit: 128 MB [问题描述] 我们机房中了病毒,因此几乎什么都无法正常进入.为了解决这个病毒,我们花了好几天.终于在大家的共同努力下 ...

  8. linux中竖线'|',双竖线‘||’,&和&&的意思

    对于初学者来说这几个意思可能只知道其中几个的意思,下面我们来看一下. 1.竖线'|' ,在linux中是作为管道符的,将'|'前面命令的输出作为'|'后面的输入.举个例子 [18066609@root ...

  9. [Angular Unit Testing] Shallow Pipe Testing

    import { TestBed, ComponentFixture } from '@angular/core/testing'; import { BrowserDynamicTestingMod ...

  10. 稀疏编码(sparse code)与字典学习(dictionary learning)

    Dictionary Learning Tools for Matlab. 1. 简介 字典 D∈RN×K(其中 K>N),共有 k 个原子,x∈RN×1 在字典 D 下的表示为 w,则获取较为 ...