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. lv resize

    # lvreduce -L -400G /dev/vg_atalinux001/lv_home # resize2fs   /dev/vg_atalinux001/lv_home resize2fs ...

  2. MCI

    MCI(Media Control Interface)媒体控件接口是Mircrosoft提供的一组多媒体和文件的标准接口.它的好处是可以方便地控制绝大多数多媒体设备 包括音频,视频,影碟,录像等多媒 ...

  3. [机器学习] Coursera ML笔记 - 逻辑回归(Logistic Regression)

    引言 机器学习栏目记录我在学习Machine Learning过程的一些心得笔记,涵盖线性回归.逻辑回归.Softmax回归.神经网络和SVM等等.主要学习资料来自Standford Andrew N ...

  4. [D3] Convert Dates to Numeric Values with Time Scales in D3 v4

    Mapping abstract values to visual representations is what data visualization is all about, and that’ ...

  5. Selector API用法

    java.nio.channels 类 Selector java.lang.Object java.nio.channels.Selector 直接已知子类: AbstractSelector pu ...

  6. C# 使用 RabbitMQ

    1. RabbitMQ MQ全称为Message Queue, 消息队列(MQ)是一种应用程序对应用程序的通信方法.应用程序通过写和检索出入列队的针对应用程序的数据(消息)来通信,而无需专用连接来链接 ...

  7. tomcat 启动项目时出现 ZipException: error in opening zip file

    错误情况 项目用 maven 打好 war 包后放到 tomcat 下,启动 tomcat,出现以下错误 3-Nov-2017 12:21:44.346 严重 [localhost-startStop ...

  8. 如何查看一个网页特定效果的js代码(动画效果可js和css)(页面可以看到js的源代码)

    如何查看一个网页特定效果的js代码(动画效果可js和css)(页面可以看到js的源代码) 一.总结 1.动画效果可能是 CSS 实现的,也可能是 JS 实现的. 2.直接Chrome的F12调试即可, ...

  9. 使用pprof及Go 程序的性能优化

    使用Golang 程序的性能优化及 Pprof 程序的性能优化无非就是对程序占用资源的优化.对于服务器而言,最重要的两项资源莫过于 CPU 和内存.性能优化,就是在对于不影响程序数据处理能力的情况下, ...

  10. 散列:散列函数与散列表(hash table)

    1. 散列函数 如果输入的关键字是整数,则一般合理方法是直接返回对表大小取模(Key mod TableSize)的结果,除非 Key 碰巧具有一些不太理想的特质.如,表的大小为 10,而关键字都是 ...