首先想要说明一下,这个Demo例子是从eoeAndroid上面Download下来的,本文里只是解析,学习一下实现原理。从昨天开始就想分析下,一直拖到今天,不到5点,睡不着了(当然不是因为这个技术问题),就趁着早晨把他写下来吧,多有不足,请多多原谅。
下面开始正题 ,先看下程序运行是图片:
然后你可以拖动圆形菜单外面项到圆形菜单中:
开始正式的代码解析:
主Activity的onCreate:
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); this.requestWindowFeature(Window.FEATURE_NO_TITLE); getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); mNewView = new NewView(getApplicationContext(), 300, 200, 150); setContentView(mNewView); new Thread(new myThread()).start(); }
class myThread implements Runnable { public void run() { while (!Thread.currentThread().isInterrupted()) { Message message = new Message(); switch (mNewView.getReturn()) { case 1: message.what = 0x101; break; case 2: message.what = 0x102; break; case 3: message.what = 0x103; break; case 4: message.what = 0x104; break; case 5: message.what = 0x105; break; case 6: message.what = 0x106; break; case 7: message.what = 0x107; break; case 8: message.what = 0x108; break; case 9: message.what = 0x109; break; } QSA.this.myHandler.sendMessage(message); try { Thread.sleep(10); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } }
Handler myHandler = new Handler() { public void handleMessage(Message msg) { // newView.getReturn = -1; switch (msg.what) { case 0x101: break; case 0x102: break; case 0x103: mIntent = new Intent(getApplicationContext(), ShowPic.class); startActivity(mIntent); break; case 0x104: break; case 0x105: break; case 0x106: break; case 0x107: mIntent = new Intent(getApplicationContext(), RunLed.class); startActivity(mIntent); break; case 0x108: break; case 0x109: break; } super.handleMessage(msg); } };
public class NewView extends View { //返回值,用于调用者获取newView状态 protected static int mGetReturn = -1; private Paint mPaint = new Paint(); // stone列表 private BigStone[] mStones; int mode = NONE; static final int NONE = 0; //拖动 static final int DRAG = 1; //用于双指视图缩放 static final int ZOOM = 2; //存放数据的数组 private BigStone[] mMenus; private BigStone[] mAddMenus = new BigStone[MENUS]; // 数目 private static int STONE_COUNT = 5; private static int MENUS = 4; // 圆心坐标 private float mPointX = 0, mPointY = 0; private int flagwai = 0; private int flag = 0; // 半径 private int mRadius = 0; // 每两个点间隔的角度 private int mDegreeDelta; private float maxX, maxY, minX, minY;
看它的构造方法:
public NewView(Context context, int px, int py, int radius) { super(context); //为圆心和半径赋值 mPointX = px; mPointY = py; mRadius = radius; setBackgroundResource(R.drawable.menubkground); //设置菜单项(为两个菜单项赋值) setupStones(); //计算坐标 computeCoordinates(); }
private void setupStones() { mStones = new BigStone[STONE_COUNT]; mMenus = new BigStone[MENUS]; BigStone stone; BigStone menus; //初始角度 int angle = 0; //每项之间相隔角度 mDegreeDelta = 360 / STONE_COUNT; //初始圆形菜单外的项 if (flagwai == 0) { for (int i = 0; i < MENUS; i++) { menus = new BigStone(); menus.bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.menu6 + i); menus.text = String.valueOf(1 + i); mMenus[i] = menus; } } //初始化圆形菜单项 for (int index = 0; index < STONE_COUNT; index++) { stone = new BigStone(); stone.angle = angle; stone.bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.menu1 + index); stone.text = String.valueOf(1 + index); //每一项的角度 angle += mDegreeDelta; mStones[index] = stone; } }
class BigStone { // 图片 Bitmap bitmap; // 角度 int angle; // x坐标 float x; // y坐标 float y; String text; // 是否可见 boolean isVisible = true; }
computeCoordinates,计算每一项的坐标:
private void computeCoordinates() { BigStone stone; BigStone menus; for (int index = 0; index < STONE_COUNT; index++) { stone = mStones[index]; stone.x = mPointX + (float) (mRadius * Math.cos(stone.angle * Math.PI / 180)); stone.y = mPointY + (float) (mRadius * Math.sin(stone.angle * Math.PI / 180)); } if (flag == 0) { for (int i = 0; i < MENUS; i++) { menus = mMenus[i]; switch (i) { case 0: menus.x = 300 * 1.8f; menus.y = 50; break; case 1: menus.x = 300 * 1.8f + 100; menus.y = 50; break; case 2: menus.x = 300 * 1.8f + 200; menus.y = 50; break; case 3: menus.x = 300 * 1.8f; menus.y = 150; break; //MENUS设置为4,下面这个应该是多余的 case 4: menus.x = 300 * 1.8f + 10 + 100; menus.y = 250; break; } } } }
@Override public void onDraw(Canvas canvas) { Paint paint = new Paint(); paint.setAntiAlias(true); paint.setColor(Color.WHITE); paint.setStyle(Paint.Style.FILL); paint.setAlpha(0x30); if (change == 0) { //绘制中心小圆 canvas.drawCircle(mPointX, mPointY, mRadius - 80, paint); } paint.setStyle(Paint.Style.FILL); paint.setAntiAlias(true); paint.setColor(Color.BLUE); paint.setAlpha(0x30); if (change == 0) { canvas.drawCircle(mPointX, mPointY, mRadius + 41, paint); // 大圆 } if (change == 1) { //满足一个条件不绘制圆盘菜单,绘制矩形菜单。 canvas.drawRect(800, 240, 0, 140, paint); } //绘制每一个外部菜单项 for (int i = 0; i < MENUS; i++) { if (!mMenus[i].isVisible) continue; drawMenus(canvas, mMenus[i].bitmap, mMenus[i].x, mMenus[i].y); } //绘制每一个园内菜单 for (int index = 0; index < STONE_COUNT; index++) { if (!mStones[index].isVisible) continue; drawInCenter(canvas, mStones[index].bitmap, mStones[index].x, mStones[index].y, mStones[index].text); } }
绘制菜单项时,调用了drawMenus和drawInCenter方法:
void drawMenus(Canvas canvas, Bitmap b, float x, float y) { canvas.drawBitmap(b, x - b.getWidth() / 2, y - b.getHeight() / 2, null); // 图标 } void drawInCenter(Canvas canvas, Bitmap bitmap, float left, float top, String text) { canvas.drawText(text, left, top, mPaint); canvas.drawBitmap(bitmap, left - bitmap.getWidth() / 2, top - bitmap.getHeight() / 2, null); }
@Override public boolean dispatchTouchEvent(MotionEvent e) { dumpEvent(e); switch (e.getAction() & MotionEvent.ACTION_MASK) { case MotionEvent.ACTION_DOWN: //判断选中的是哪个点 for (int i = 0; i < STONE_COUNT; i++) { if (e.getX() >= mStones[i].x - 20 && e.getX() <= mStones[i].x + 40 && e.getY() >= mStones[i].y && e.getY() <= mStones[i].y + 40) { if (i < 5) { mGetReturn = Integer.valueOf(mStones[i].text); } else { mGetReturn = Integer.valueOf(mStones[i].text) + 5; } Toast.makeText(getContext(), String.valueOf(mGetReturn), Toast.LENGTH_SHORT).show(); } } //把mode设为拖动 mode = DRAG; return true; case MotionEvent.ACTION_POINTER_DOWN: //获取两点间的距离 oldDist = spacing(e); if (oldDist > 100f) { mode = ZOOM; } return true; case MotionEvent.ACTION_MOVE: //获取最大最小坐标 getMaxMin(e); if (mode == DRAG) { } else if (mode == ZOOM) { //获取两点间的距离 // float newDist = spacing(e); // if (newDist > 100f) { // /* // * if (change == 0) { change = 1; // * // * resetStonesAngle(e.getX(), e.getY()); // * computeCoordinates(); invalidate(); } // */ // } } int a = 0; for (int i = 0; i < MENUS; i++) { if (e.getX() > mMenus[i].x - 40 && e.getX() < mMenus[i].x + 40 && e.getY() > mMenus[i].y - 40 && e.getY() < mMenus[i].y + 40) { mMenus[i].x = e.getX(); mMenus[i].y = e.getY(); flag = 1; computeCoordinates(); //重新绘制 postInvalidate(); //从外面添加到圆形菜单中 if (e.getX() < maxX && e.getX() > minX && e.getY() < maxY && e.getY() > minY) { if (mMenus[i].isVisible) { for (int j = 0; j < MENUS; j++) { if (mAddMenus[j] == null && a == 0) { mAddMenus[j] = mMenus[i]; a = 1; } } STONE_COUNT++; mDegreeDelta = 360 / STONE_COUNT; mStones = new BigStone[STONE_COUNT]; flagwai = 1; int angle = 0; BigStone stone; for (int index = 0; index < STONE_COUNT; index++) { stone = new BigStone(); if (index < 5) { stone.bitmap = BitmapFactory .decodeResource(getResources(), R.drawable.menu1 + index); stone.text = String.valueOf(1 + index); } else { stone.bitmap = mAddMenus[index - 5].bitmap; stone.text = mAddMenus[index - 5].text; } stone.angle = angle; angle += mDegreeDelta; mStones[index] = stone; } //把添加到圆内菜单项中的外部Item设为不可见,是不是可以考虑移除? mMenus[i].isVisible = false; } } break; } } if (e.getX() < maxX && e.getX() > minX && e.getY() < maxY && e.getY() > minY) { if (e.getX() < maxX - 80 && e.getX() > minX + 81 && e.getY() < maxY - 80 && e.getY() > minY + 81) { mPointX = e.getX(); mPointY = e.getY(); } //重新设置每个item的角度 resetStonesAngle(e.getX(), e.getY()); //重新设置每一个item的坐标 computeCoordinates(); //重绘 invalidate(); } break; case MotionEvent.ACTION_UP: break; case MotionEvent.ACTION_POINTER_UP: mode = NONE; break; } return super.dispatchTouchEvent(e); }
先看以下事件:
举例子来说:当我们放一个食指到屏幕上时,触发ACTION_DOWN事件;再放一个中指到屏幕上,触发ACTION_POINTER_DOWN事件;此时再把食指或中指放开,都会触发ACTION_POINTER_UP事件;再放开最后一个手指,触发ACTION_UP事件;而同时在整个过程中,ACTION_MOVE事件会一直不停地被触发。
逐个事件分析一下:
case MotionEvent.ACTION_DOWN: //判断选中的是哪个点 for (int i = 0; i < STONE_COUNT; i++) { if (e.getX() >= mStones[i].x - 20 && e.getX() <= mStones[i].x + 40 && e.getY() >= mStones[i].y && e.getY() <= mStones[i].y + 40) { if (i < 5) { mGetReturn = Integer.valueOf(mStones[i].text); } else { mGetReturn = Integer.valueOf(mStones[i].text) + 5; } Toast.makeText(getContext(), String.valueOf(mGetReturn), Toast.LENGTH_SHORT).show(); } } //把mode设为拖动 mode = DRAG; return true;
case MotionEvent.ACTION_POINTER_DOWN: //获取两点间的距离 oldDist = spacing(e); if (oldDist > 100f) { mode = ZOOM; } return true;
private float spacing(MotionEvent event) { float x = event.getX(0) - event.getX(1); float y = event.getY(0) - event.getY(1); return FloatMath.sqrt(x * x + y * y); }
move:
case MotionEvent.ACTION_MOVE: //获取最大最小坐标 getMaxMin(e); if (mode == DRAG) { } else if (mode == ZOOM) { //获取两点间的距离 // float newDist = spacing(e); // if (newDist > 100f) { // /* // * if (change == 0) { change = 1; // * // * resetStonesAngle(e.getX(), e.getY()); // * computeCoordinates(); invalidate(); } // */ // } } int a = 0; for (int i = 0; i < MENUS; i++) { if (e.getX() > mMenus[i].x - 40 && e.getX() < mMenus[i].x + 40 && e.getY() > mMenus[i].y - 40 && e.getY() < mMenus[i].y + 40) { mMenus[i].x = e.getX(); mMenus[i].y = e.getY(); flag = 1; computeCoordinates(); //重新绘制 postInvalidate(); //从外面添加到圆形菜单中 if (e.getX() < maxX && e.getX() > minX && e.getY() < maxY && e.getY() > minY) { if (mMenus[i].isVisible) { for (int j = 0; j < MENUS; j++) { if (mAddMenus[j] == null && a == 0) { mAddMenus[j] = mMenus[i]; a = 1; } } STONE_COUNT++; mDegreeDelta = 360 / STONE_COUNT; mStones = new BigStone[STONE_COUNT]; flagwai = 1; int angle = 0; BigStone stone; for (int index = 0; index < STONE_COUNT; index++) { stone = new BigStone(); if (index < 5) { stone.bitmap = BitmapFactory .decodeResource(getResources(), R.drawable.menu1 + index); stone.text = String.valueOf(1 + index); } else { stone.bitmap = mAddMenus[index - 5].bitmap; stone.text = mAddMenus[index - 5].text; } stone.angle = angle; angle += mDegreeDelta; mStones[index] = stone; } //把添加到圆内菜单项中的外部Item设为不可见,是不是可以考虑移除? mMenus[i].isVisible = false; } } break; } } if (e.getX() < maxX && e.getX() > minX && e.getY() < maxY && e.getY() > minY) { if (e.getX() < maxX - 80 && e.getX() > minX + 81 && e.getY() < maxY - 80 && e.getY() > minY + 81) { mPointX = e.getX(); mPointY = e.getY(); } //重新设置每个item的角度 resetStonesAngle(e.getX(), e.getY()); //重新设置每一个item的坐标 computeCoordinates(); //重绘 invalidate(); } break;
里面有getMaxMin方法:
private void getMaxMin(MotionEvent e) { float tempx; float tempy; for (int i = 0; i < STONE_COUNT; i++) { for (int j = 0; j < STONE_COUNT; j++) { if (mStones[i].x < mStones[j].x) { tempx = mStones[i].x; mStones[i].x = mStones[j].x; mStones[j].x = tempx; } if (mStones[i].y < mStones[j].y) { tempy = mStones[i].y; mStones[i].y = mStones[j].y; mStones[j].y = tempy; } } } maxX = mStones[STONE_COUNT - 1].x; minX = mStones[0].x; maxY = mStones[STONE_COUNT - 1].y; minY = mStones[0].y; }
里面的计算角度方法:
private void resetStonesAngle(float x, float y) { int angle = computeCurrentAngle(x, y); for (int index = 0; index < STONE_COUNT; index++) { mStones[index].angle = angle; angle += mDegreeDelta; } } 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; } return degree; }
最后源代码下载地址:http://download.csdn.net/detail/aomandeshangxiao/4857216