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. MySQLDump在使用之前一定要想到的事情

    MySQLDump经常用于迁移数据和备份. 下面创建实验数据,两个数据库和若干表create database db1 ;use db1;create table t1(id int primary ...

  2. 【例题5-1 UVA 10474 】Where is the Marble?

    [链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] 排序 用lower_bound找就可以了. ->lower_bound,如果里面所有的数字都比x小,那么它的返回值会越界! [ ...

  3. 关于重定向RedirectAttributes的用法(转)

    原文地址:https://zhuanlan.zhihu.com/p/21353217?refer=pengsong-java RedirectAttributes 是Spring mvc 3.1版本之 ...

  4. [CSS] Use Generated Content to Augment Information

    When you create a pseudo element, you have access to the parent HTML attributes. They can be used in ...

  5. oracle数据库未打开解决的方法

    Microsoft Windows [版本号 6.1.7601] 版权全部 (c) 2009 Microsoft Corporation.保留全部权利. C:\Users\Administrator& ...

  6. Swift3.0 功能二 (表情键盘与图文混排)

    随着iOS越来越多表情键盘以及图文混排的需求,本文运用Swift3.0系统的实现其功能以及封装调用方法,写的不好,如有错误望各位提出宝贵意见,多谢 项目源码地址: 相关知识点都有标识 项目源码地址 废 ...

  7. [Recompose] Show a Spinner While a Component is Loading using Recompose

    Learn how to use the 'branch' and 'renderComponent' higher-order components to show a spinner while ...

  8. ios开发网络学习五:MiMEType ,多线程下载文件思路,文件的压缩和解压缩

    一:MiMEType:一般可以再百度上搜索到相应文件的MiMEType,或是利用c语言的api去获取文件的MiMEType : //对该文件发送一个异步请求,拿到文件的MIMEType - (void ...

  9. php实现 删除字符串中出现次数最少的字符

    php实现  删除字符串中出现次数最少的字符 一.总结 一句话总结:数组排序是改变数组的,而其它函数一般不改变原数据,比如str_replace(); 1.单案例测试通过而多案例测试不通过怎么办? 检 ...

  10. 忙里偷闲( ˇˍˇ )闲里偷学【C语言篇】——(9)链表

    我们至少可以通过两种结构来存储数据 数组 1.需要一整块连续的存储空间,内存中可能没有 2.插入元素,删除元素效率极低. 3.查找数据快 链表 1.查找效率低 2.不需要一块连续的内存空间 3.插入删 ...