实现的效果图:六个小图片可以跟随手指滑动绕中心点旋转

代码:

package com.example.test_canvas;import java.util.ArrayList;
import java.util.List;import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffXfermode;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceHolder.Callback;
import android.view.SurfaceView;public class MyView extends SurfaceView {private final String TAG = "MyView";private final int WHAT_MOVE = 0X013;private final int WHAT_CLICK = 0X014;private final int TIME_CLICK_INTERVAL = 200;// 按下抬起的间隔时间//
    private Handler mThreadHandler = null;MyDrawableThread mThread = null;private MyViewClickListener mListener = null;private int Radius = 0;// 半径Bitmap[] mItems = null;private int viewWidth = 0;private int viewHeight = 0;private int centerX = 0;private int centerY = 0;private List<Point> mPoints = new ArrayList<Point>();private Handler mMainHandler = null;public MyView(Context context, AttributeSet attrs) { super(context, attrs);mMainHandler = new Handler(context.getMainLooper());SurfaceHolder holder = this.getHolder();holder.addCallback(mCallback);mThread = new MyDrawableThread(holder);mThread.start();}/*** 设置图片** @param items*/public void setItem(Bitmap[] items, MyViewClickListener listener) {Log.d(TAG, "--->setItem");if (items != null) {mItems = items;}mListener = listener;}float x = 0;float y = 0;private float mBuildDegree = 0;private long time_start = 0;@Overridepublic boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) {case MotionEvent.ACTION_DOWN:Log.d(TAG, "ACTION_DOWN");x = event.getX();y = event.getY();time_start = System.currentTimeMillis();break;case MotionEvent.ACTION_UP: {Log.d(TAG, "ACTION_UP");float dstx = event.getX();float dsty = event.getY();if ((System.currentTimeMillis() - time_start) <= TIME_CLICK_INTERVAL) {Log.d(TAG, "--->TIME_CLICK_INTERVAL");mThreadHandler.sendMessage(mThreadHandler.obtainMessage(WHAT_CLICK, (int) dstx, (int) dsty));}time_start = 0;x = 0;y = 0;break;}case MotionEvent.ACTION_MOVE:Log.d(TAG, "ACTION_MOVE");float dstx = event.getX();float dsty = event.getY();Log.d(TAG, "src x: " + x + "y: " + y);Log.d(TAG, "dst x: " + dstx + "y: " + dsty);float degree = 0;degree = (float) getActionDegrees(centerX, centerY, dstx, dsty, x,y);Log.d(TAG, "degree: " + degree);x = dstx;y = dsty;mBuildDegree -= degree;mThreadHandler.sendMessage(mThreadHandler.obtainMessage(WHAT_MOVE,mBuildDegree));break;default:break;}return true;}private class MyDrawableThread extends Thread {private SurfaceHolder mHolder;public MyDrawableThread(SurfaceHolder holder) {mHolder = holder;}@Overridepublic void run() {Log.d(TAG, "--->MyThread run");Looper.prepare();mThreadHandler = new Handler() {public void handleMessage(Message msg) {switch (msg.what) {case WHAT_MOVE:float degree = (Float) msg.obj;drawImgByRoate(degree);break;case WHAT_CLICK:if (mListener != null && mItems != null&& mItems.length > 0 && mItems[0] != null) {int dstx = msg.arg1;int dsty = msg.arg2;Bitmap firstBitmap = mItems[0];int interval = (firstBitmap.getHeight() > firstBitmap.getWidth()) ? firstBitmap.getWidth() / 2: firstBitmap.getHeight() / 2;Log.d(TAG, "--->TIME_CLICK_INTERVAL tempdistance: "+ interval);if (mPoints != null) {for (int i = 0; i < mPoints.size(); ++i) {Point point = mPoints.get(i);double tempx = Math.abs(point.x - dstx);double tempy = Math.abs(point.y - dsty);double distance = Math.sqrt(tempx * tempx+ tempy * tempy);Log.d(TAG, "--->TIME_CLICK_INTERVAL for: "+ i + "  " + distance);if (distance <= interval) {final int index = i;mMainHandler.post(new Runnable() {@Overridepublic void run() {Log.d(TAG,"--->TIME_CLICK_INTERVAL onItemClick: "+ index);mListener.onItemClick(index);}});break;}}}}break;default:super.handleMessage(msg);}}};Looper.loop();}private void drawImgByRoate(float degree) {Log.d(TAG, "mdegree>> " + degree);Canvas canvas = null;Paint paint = new Paint();int count = mItems.length;try {synchronized (mHolder) {canvas = mHolder.lockCanvas();clear(canvas);mPoints.clear();for (int i = 0; i < count; i++) {Bitmap bitmap = mItems[i];// 计算x,yfloat reaote = degree + (360 / count) * i;Log.d(TAG, i + "  >> " + reaote);float x = (float) (Radius* Math.cos(Math.toRadians(reaote)) + centerX);float y = (float) (Radius* Math.sin(Math.toRadians(reaote)) + centerY);Log.d(TAG, "x>> " + x);Log.d(TAG, "y>> " + y);// drawint tempX = (int) (x - bitmap.getWidth() / 2);int tempY = (int) (y - bitmap.getHeight() / 2);mPoints.add(new Point((int) x, (int) y)); canvas.drawBitmap(bitmap, tempX, tempY, paint);}}} catch (Exception e) {e.printStackTrace();} finally {if (canvas != null) {Log.d(TAG, "--->unlockCanvasAndPost");mHolder.unlockCanvasAndPost(canvas);// 结束锁定画图,并提交改变。
                }}}private void clear(Canvas canvas) {Paint paint = new Paint();paint.setXfermode(new PorterDuffXfermode(Mode.CLEAR));canvas.drawPaint(paint);paint.setXfermode(new PorterDuffXfermode(Mode.SRC));}};SurfaceHolder.Callback mCallback = new Callback() {@Overridepublic void surfaceDestroyed(SurfaceHolder holder) {// TODO Auto-generated method stubLog.d(TAG, "--->surfaceDestroyed");}@Overridepublic void surfaceCreated(SurfaceHolder holder) {// TODO Auto-generated method stubLog.d(TAG, "--->surfaceCreated");viewWidth = getWidth();viewHeight = getHeight();Radius = (viewWidth / 2) - 100;Log.d(TAG, "viewWidth: " + viewWidth);Log.d(TAG, "viewHeight: " + viewHeight);centerX = viewWidth / 2;centerY = viewHeight / 2;mThreadHandler.sendMessage(mThreadHandler.obtainMessage(WHAT_MOVE,0f));}@Overridepublic void surfaceChanged(SurfaceHolder holder, int format, int width,int height) {Log.d(TAG, "--->surfaceChanged");}};public void release() {if (mItems != null) {for (Bitmap bitmap : mItems) {bitmap = null;}}mItems = null;mListener = null;}/*** 获取两点到第三点的夹角。** @param centerX* @param centerY* @param nowX* @param nowY* @param oldX* @param oldY* @return*/private double getActionDegrees(float centerX, float centerY, float nowX,float nowY, float oldX, float oldY) {double a = Math.sqrt((nowX - oldX) * (nowX - oldX) + (nowY - oldY)* (nowY - oldY));double b = Math.sqrt((centerX - oldX) * (centerX - oldX)+ (centerY - oldY) * (centerY - oldY));double c = Math.sqrt((nowX - centerX) * (nowX - centerX)+ (nowY - centerY) * (nowY - centerY));// 余弦定理double cosA = (b * b + c * c - a * a) / (2 * b * c);// 返回余弦值为指定数字的角度,Math函数为我们提供的方法double arcA = Math.acos(cosA);double degree = arcA * 180 / Math.PI;// 接下来我们要讨论正负值的关系了,也就是求出是顺时针还是逆时针。// 第1、2象限if (nowY < centerY && oldY < centerY) {if (nowX < centerX && oldX > centerX) {// 由2象限向1象限滑动return degree;}// 由1象限向2象限滑动else if (nowX >= centerX && oldX <= centerX) {return -degree;}}// 第3、4象限if (nowY > centerY && oldY > centerY) {// 由3象限向4象限滑动if (nowX < centerX && oldX > centerX) {return -degree;}// 由4象限向3象限滑动else if (nowX > centerX && oldX < centerX) {return degree;}}// 第2、3象限if (nowX < centerX && oldX < centerX) {// 由2象限向3象限滑动if (nowY < centerY && oldY > centerY) {return -degree;}// 由3象限向2象限滑动else if (nowY > centerY && oldY < centerY) {return degree;}}// 第1、4象限if (nowX > centerX && oldX > centerX) {// 由4向1滑动if (nowY > centerY && oldY < centerY) {return -degree;}// 由1向4滑动else if (nowY < centerY && oldY > centerY) {return degree;}}// 在特定的象限内float tanB = (nowY - centerY) / (nowX - centerX); float tanC = (oldY - centerY) / (oldX - centerX);if ((nowX > centerX && nowY > centerY && oldX > centerX&& oldY > centerY && tanB > tanC)// 第一象限|| (nowX > centerX && nowY < centerY && oldX > centerX&& oldY < centerY && tanB > tanC)// 第四象限|| (nowX < centerX && nowY < centerY && oldX < centerX&& oldY < centerY && tanB > tanC)// 第三象限|| (nowX < centerX && nowY > centerY && oldX < centerX&& oldY > centerY && tanB > tanC))// 第二象限return -degree;return degree;}public interface MyViewClickListener {public void onItemClick(int itemIndex);}
}

转载于:https://www.cnblogs.com/afluy/p/4324430.html

【Android】自定义环形菜单View相关推荐

  1. QML自定义环形菜单/环形选择框

    Qt5 中本身提供了扇形菜单 PieMenu,属于 QtQuick.Extras 模块,这个模块是拓展自 QtQuick.Control1 的,QtQuick.Control1 在 Qt5 高版本被废 ...

  2. android自定义侧滑菜单代码,原生Android 侧滑菜单实践(部分)

    此为第一个制作侧滑菜单的实践 . 此部分仅仅为部分实践: 仅缺menu的字符串布局,以及需要修改的MainActivity.java文件,也是需要主要修改的地方. 从使用MD设计-进行侧滑菜单的制作( ...

  3. Android自定义menu菜单布局

    一:先写一个自定义的菜单布局 menu_gallery.xml: <?xml version="1.0" encoding="utf-8"?> &l ...

  4. android自定义侧滑菜单slidmenu

    实现上主要就是一个自定义的MySlidView,在这个MySlidView里边去加载两个你要显示的View(mMenuView, mSlidView),即一个是滑动之后,左侧的mSlidView,另一 ...

  5. android自定义LinearLayout和View

    自定义线性布局经常用到: 第一种是在扩展的LinearLayout构造函数中使用Inflater加载一个布局,并从中提取出相关的UI组件进行封装,形成一个独立的控件.在使用该控件时,由于它所有的子元素 ...

  6. Android 自定义环形圆形显示统计数据z

    <?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http: ...

  7. Android 自定义评论回复view

    先上效果图: 一.创建xml 1.android_ceshi_activity <?xml version="1.0" encoding="utf-8"? ...

  8. android自定义验证码倒计时View

    关于自定义View的构造方法里面的参数的含义可以参考: http://www.cnblogs.com/angeldevil/p/3479431.html 代码: 倒计时类: public class ...

  9. [Android]自定义系统菜单的背景

    不多说,上图,见代码. package lab.sodino.menutest; import android.content.Context; import android.app.Activity ...

最新文章

  1. openGL学习笔记(1)——常用方法原型解释
  2. HIT训练营----1 题解
  3. python遍历目录_Python遍历目录的4种方法
  4. for循环和数组练习
  5. 在职场中,什么是职场大忌?
  6. JDBC中给Mysql加时区问题!
  7. python语言是非开源语言_python是非开源语言吗
  8. windows和linux 下将tomcat注册为服务
  9. CCS6.0安装教程
  10. 计算机底层逻辑无法仿造大脑,重塑世界的底层逻辑|读《终极算法》
  11. 项目进度管理:制定进度计划
  12. Redis 过期策略和内存淘汰机制
  13. 卫星影像离线数据包(免费下载)
  14. 单片机c51语言中 两个位变量类型是什么,51单片机中的数据类型解析
  15. 树莓派cm4安装ax200驱动-wifi6
  16. Nagios XI网络监控软件安装
  17. Java面向对象三大特征---继承
  18. 终极网络电视王 v3.25 是什么
  19. Java动态追踪技术
  20. maven BUILD FAILURE的解决办法

热门文章

  1. 单例模式(singleton)解析例子
  2. 笔记本键盘维修[原创]
  3. 【nodejs原理源码赏析(6)】深度剖析cluster模块源码与node.js多进程(下)
  4. java.lang.String cannot be cast to org.apache.flink.table.data.StringData
  5. cannot resolve symbol ‘log‘问题解决
  6. sas ondemand for academics使用
  7. html标签书写注意事项,HTML一些标签注意事项
  8. 机器学习(九)——EM算法
  9. 【数学基础】参数估计之贝叶斯估计
  10. Git入门教程(1)