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圆形旋转菜单,而对于移动转换功能支持的更多相关文章

  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. item.imageInsets =

    直接上代码: item.imageInsets = UIEdgeInsetsMake(6, 0, -6, 0);//设置图片居中 阅读全文 本文已收录于下面专栏: wanglixin1999 +关注 ...

  2. stm32 DMA+timer+DAC

    是有延迟的:

  3. css 翻牌 翻转 3d翻转 特效

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  4. CentOS 7开放端口和关闭防火墙

    开放端口 永久的开放需要的端口 sudo firewall-cmd --zone=public --add-port=3000/tcp --permanent sudo firewall-cmd -- ...

  5. Snmp linux

    http://blog.csdn.net/youngqj/article/details/7311849 http://blog.csdn.net/howema/article/details/418 ...

  6. Linux 网络编程系列教程

    一.基础理论篇 01.网络协议入门 02.LAN.WAN.WLAN.VLAN 和 VPN 的区别 03.IP 地址介绍 04.广播地址介绍 05.无连接和面向连接协议的区别 06.因特网的IP协议是不 ...

  7. AndroidStudio实现JNI的示例详解

    1. NDK简介 Android NDK 是在SDK前面又加上了“原生”二字,即Native Development Kit,因此又被Google称为“NDK” 1.1 NDK产生的背景 Androi ...

  8. 适合前端开发的 Chrome 扩展有哪些?(十款)

    适合前端开发的 Chrome 扩展有哪些?(十款) 一.总结 好的插件或者框架对程序员的意义重大. 二.适合前端开发的 Chrome 扩展有哪些?(十款) 掘金是一个高质量的技术社区,从 ECMASc ...

  9. 360随身WIFI作USB无线网卡的做法

    作者:朱金灿 来源:http://blog.csdn.net/clever101 1. 到控制面板上把360wifi卸载. 2. 到雷凌的官网下载网卡驱动,注意选择USB(RT2870***),操作系 ...

  10. Erlang 学习笔记

    http://wenku.baidu.com/link?url=AUQR8Hn-e-fEB_lqjXsd8XfapWj1qAK7J05JoBXFib_LlSk5qSOTia8HIxNV1XkeZi-k ...