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 detection
- private 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();
- }
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- // TODO Auto-generated method stub
- super.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;
- @Override
- public 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 true
- quadrantTouched[getQuadrant(event.getX() - mPointX,
- mPointY - event.getY())] = true;
- mGestureDetector.onTouchEvent(event);
- return true;
- }
- private class MyGestureListener extends SimpleOnGestureListener {
- @Override
- public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
- float velocityY) {
- // get the quadrant of the start and the end of the fling
- int q1 = getQuadrant(e1.getX() - mPointX, mPointY - e1.getY());
- int q2 = getQuadrant(e2.getX() - mPointX, mPointY - e2.getY());
- // the inversed rotations
- if ((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;
- }
- @Override
- public 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;
- }
- @Override
- public void run() {
- // TODO Auto-generated method stub
- if(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.RoundSpinView
- android: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;
- @Override
- protected 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);
- }
- @Override
- public void onSingleTapUp(int position) {
- // TODO Auto-generated method stub
- switch (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的自己定义监听器进行赋值
至此。你的项目就能够拥有看上去非常高大上的旋转换位菜单功能
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvc3A2NjQ1NTk3/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">
这里附上此demo相应的资源链接:点击打开链接
版权声明:本文博客原创文章,博客,未经同意,不得转载。
android圆形旋转菜单,而对于移动转换功能支持的更多相关文章
- [Android阅读代码]圆形旋转菜单CircleMenu
项目名称:圆形旋转菜单CircleMenu 原版项目代码下载 感谢原作者开源
- android简洁饼状图组件、圆形Menu菜单、画板画笔应用、答题应用等源码
Android精选源码 android自动监听复制内容源码 Android上简洁轻量级的饼图控件 好看的 Android 圆形 Menu 菜单效果 android画笔.画板功能效果的实现 Androi ...
- Android 自定义圆形旋转进度条,仿微博头像加载效果
微博 App 的用户头像有一个圆形旋转进度条的加载效果,看上去效果非常不错,如图所示: 据说 Instagram 也采用了这种效果.最近抽空研究了一下,最后实现的效果是这样: 基本上能模拟出个大概,代 ...
- 3D旋转菜单
今天来个3D旋转菜单,是纯css3实现的,主要用到transform,transition,backface-visibility. 主要是transform这个变换,它是今天猪脚. transfor ...
- android 屏幕旋转
转自:http://blog.csdn.net/oyzhizhong/article/details/8131799 屏是LANDSCAPE的,要让它默认显示为PORTRAIT. 1.kernel里要 ...
- Android 3D滑动菜单完全解析,实现推拉门式的立体特效
转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/10471245 在上一篇文章中,我们学习了Camera的基本用法,并借助它们编写了一 ...
- Android中轴旋转特效实现,制作别样的图片浏览器
转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/10766017 Android API Demos中有很多非常Nice的例子,这些例 ...
- Android立体旋转动画实现与封装(支持以X、Y、Z三个轴为轴心旋转)
本文主要介绍Android立体旋转动画,或者3D旋转,下图是我自己实现的一个界面 立体旋转分为以下三种: 1. 以X轴为轴心旋转 2. 以Y轴为轴心旋转 3. 以Z轴为轴心旋转--这种等价于andro ...
- Android 屏幕旋转 处理 AsyncTask 和 ProgressDialog 的最佳方案
的最佳方案 标签: Android屏幕旋转AsyncTaskProgressDialog 2014-07-19 09:25 39227人阅读 评论(46) 收藏 举报 分类: [android 进阶之 ...
随机推荐
- MySQLDump在使用之前一定要想到的事情
MySQLDump经常用于迁移数据和备份. 下面创建实验数据,两个数据库和若干表create database db1 ;use db1;create table t1(id int primary ...
- 【例题5-1 UVA 10474 】Where is the Marble?
[链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] 排序 用lower_bound找就可以了. ->lower_bound,如果里面所有的数字都比x小,那么它的返回值会越界! [ ...
- 关于重定向RedirectAttributes的用法(转)
原文地址:https://zhuanlan.zhihu.com/p/21353217?refer=pengsong-java RedirectAttributes 是Spring mvc 3.1版本之 ...
- [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 ...
- oracle数据库未打开解决的方法
Microsoft Windows [版本号 6.1.7601] 版权全部 (c) 2009 Microsoft Corporation.保留全部权利. C:\Users\Administrator& ...
- Swift3.0 功能二 (表情键盘与图文混排)
随着iOS越来越多表情键盘以及图文混排的需求,本文运用Swift3.0系统的实现其功能以及封装调用方法,写的不好,如有错误望各位提出宝贵意见,多谢 项目源码地址: 相关知识点都有标识 项目源码地址 废 ...
- [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 ...
- ios开发网络学习五:MiMEType ,多线程下载文件思路,文件的压缩和解压缩
一:MiMEType:一般可以再百度上搜索到相应文件的MiMEType,或是利用c语言的api去获取文件的MiMEType : //对该文件发送一个异步请求,拿到文件的MIMEType - (void ...
- php实现 删除字符串中出现次数最少的字符
php实现 删除字符串中出现次数最少的字符 一.总结 一句话总结:数组排序是改变数组的,而其它函数一般不改变原数据,比如str_replace(); 1.单案例测试通过而多案例测试不通过怎么办? 检 ...
- 忙里偷闲( ˇˍˇ )闲里偷学【C语言篇】——(9)链表
我们至少可以通过两种结构来存储数据 数组 1.需要一整块连续的存储空间,内存中可能没有 2.插入元素,删除元素效率极低. 3.查找数据快 链表 1.查找效率低 2.不需要一块连续的内存空间 3.插入删 ...