旋转转盘
2016-06-22 14:13
218 查看
版权声明:from Reone [https://github.com/Reone] https://blog.csdn.net/qq_18644529/article/details/89951868
面试:你懂什么是分布式系统吗?Redis分布式锁都不会?>>>
在github上一个旋转转盘项目的基础上进行修改,原项目地址 https://github.com/tangkai/android/tree/88eb94cf0004756ccd8f0136106678c89938dff7/MyRotation
1.原来项目支持的是方形的图,view长宽是根据对角线来的,现在我修改成根据view长宽来控制图片大小,这样就可以在xml上控制这个VIew大小了.
2.可以将圆分割成不同的块,给每块添加事件.以
圆心为坐标原点,从x轴负半轴开始顺时针增加,onPartClick中 position 从0开始
RotateVIew.java
package com.sun.shine.myrotation.view; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.PixelFormat; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.os.Handler; import android.os.Message; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; public class RotateView extends View { /** * 原心坐标x */ float o_x; /** * 原心坐标y */ float o_y; /** * 处理惯性的handler */ Handler handler; /** * handler处理消息的间隔 */ int delayedTime = 20; /** * 消息信号,滚动的标识 */ static final int play = 0; /** * 消息信号,停止滚动的标识 */ static final int stop = 1; /** * 上次记录的时间,计算一定时间所走过的弧度、计算速度. */ double currentTime = 0; /** * 图片的宽度 */ int width; /** * 图片的高度 */ int height; /** * view的真实宽度与高度:因为是旋转,所以这个view是正方形,它的值是图片的对角线长度 */ double maxwidth; /** * 旋转的图片 */ Bitmap rotatBitmap; public RotateView(Context context, AttributeSet attrs) { super(context, attrs); init(); } /** * 初始化handler与速度计算器 */ private void init() { vRecord = new VRecord(); handler = new Handler() { @Override public void handleMessage(Message msg) { double detaTime = System.currentTimeMillis() - currentTime; switch (msg.what) { case play: { if (isClockWise) { speed = speed - a * detaTime; if (speed <= 0) { return; } else { handler.sendEmptyMessageDelayed(play, delayedTime); } } else { speed = speed + a * detaTime; if (speed >= 0) { return; } else { handler.sendEmptyMessageDelayed(play, delayedTime); } } addDegree((float)(speed * detaTime + (a * detaTime * detaTime) / 2)); // if (a < a_max) { // a = (float)(a + a_add*detaTime); // System.out.println("a:"+a); // } currentTime = System.currentTimeMillis(); invalidate(); break; } case stop: { speed = 0; handler.removeMessages(play); } } super.handleMessage(msg); } }; // 默认是有一张图片的 initSize(); } public void setRotatBitmap(Bitmap bitmap) { rotatBitmap = bitmap; initSize(); postInvalidate(); } public void setRotatDrawableResource(int id) { BitmapDrawable drawable = (BitmapDrawable)getContext().getResources().getDrawable(id); setRotatDrawable(drawable); } public void setRotatDrawable(BitmapDrawable drawable) { // rotatBitmap = drawable.getBitmap(); rotatBitmap = drawableToBitmap(drawable); initSize(); postInvalidate(); } private Bitmap drawableToBitmap(Drawable drawable) { Bitmap bitmap = Bitmap .createBitmap( drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565); Canvas canvas = new Canvas(bitmap); // canvas.setBitmap(bitmap); drawable.setBounds(0, 0, getLayoutParams().width,getLayoutParams().height); drawable.draw(canvas); return bitmap; } private void initSize() { if (rotatBitmap == null) { // throw new NoBitMapError("Error,No bitmap in RotatView!"); return; } width = getLayoutParams().width; height = getLayoutParams().height; // maxwidth = Math.sqrt(width * width + height * height); maxwidth = Math.min(width,height); o_x = o_y = (float)(maxwidth / 2);//确定圆心坐标 } /** * 通过此方法来控制旋转度数,如果超过360,让它求余,防止,该值过大造成越界 * * @param added */ private void addDegree(float added) { deta_degree += added; if (deta_degree > 360 || deta_degree < -360) { deta_degree = deta_degree % 360; } } @Override protected void onDraw(Canvas canvas) { Matrix matrix = new Matrix(); // 设置转轴位置 matrix.setTranslate((float)width / 2, (float)height / 2); // 开始转 matrix.preRotate(deta_degree); // matrix.preRotate(deta_degree,(float)width / 2, (float)height / 2); // 转轴还原 matrix.preTranslate(-(float)width / 2, -(float)height / 2); // 将位置送到view的中心 matrix.postTranslate((float)(maxwidth - width) / 2, (float)(maxwidth - height) / 2); canvas.drawBitmap(rotatBitmap, matrix,paint); super.onDraw(canvas); } Paint paint=new Paint(); /* @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // TODO Auto-generated method stub super.onMeasure(widthMeasureSpec, heightMeasureSpec); // 它的宽高不是图片的宽高,而是以宽高为直角的矩形的对角线的长度 // setMeasuredDimension((int)maxwidth, (int)maxwidth); } */ /** * 手指触屏的初始x的坐标 */ float down_x; /** * 手指触屏的初始y的坐标 */ float down_y; /** * 移动时的x的坐标 */ float target_x; /** * 移动时的y的坐标 */ float target_y; /** * 放手时的x的坐标 */ float up_x; /** * 放手时的y的坐标 */ float up_y; /** * 当前的弧度(以该 view 的中心为圆点) */ float current_degree; /** * 放手时的弧度(以该 view 的中心为圆点) */ float up_degree; /** * 当前圆盘所转的弧度(以该 view 的中心为圆点) */ float deta_degree; /** * 最后一次手势滑过的时间 */ double lastMoveTime = 0; /** * 最小加速度(当手指放手是) */ public static final float a_min = 0.001f; /** * 加速度增量 */ public static final float a_add = 0.001f; /** * 加速度 */ float a = a_min; /** * 最大加速度(当手指按住时) */ public static final float a_max = a_min * 5; /** * 旋转速度(度/毫秒) */ double speed = 0; /** * 速度计算器 */ VRecord vRecord; /** * 是否为顺时针旋转 */ boolean isClockWise; private float touchRecordDegree; @Override public boolean onTouchEvent(MotionEvent event) { // TODO Auto-generated method stub if (rotatBitmap == null) { throw new NoBitMapError("Error,No bitmap in RotatView!"); } switch (event.getAction()) { case MotionEvent.ACTION_DOWN: { down_x = event.getX(); down_y = event.getY(); current_degree = detaDegree(o_x, o_y, down_x, down_y); vRecord.reset(); // handler.sendEmptyMessage(stop); a = a_max; touchRecordDegree = deta_degree; break; } case MotionEvent.ACTION_MOVE: { down_x = target_x = event.getX(); down_y = target_y = event.getY(); float degree = detaDegree(o_x, o_y, target_x, target_y); // 滑过的弧度增量 float dete = degree - current_degree; // 如果小于-90度说明 它跨周了,需要特殊处理350->17, if (dete < -270) { dete = dete + 360; // 如果大于90度说明 它跨周了,需要特殊处理-350->-17, } else if (dete > 270) { dete = dete - 360; } lastMoveTime = System.currentTimeMillis(); vRecord.add(dete, lastMoveTime); addDegree(dete); current_degree = degree; postInvalidate(); break; } case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: { a = a_min; double lastupTime = System.currentTimeMillis(); double detaTime = lastupTime - lastMoveTime; up_x = event.getX(); up_y = event.getY(); up_degree = detaDegree(o_x, o_y, up_x, up_y); // 放手时的速度 speed = speed + vRecord.getSpeed(); if (speed > 0) { speed = Math.min(VRecord.max_speed, speed); } else { speed = Math.max(-VRecord.max_speed, speed); } // System.out.println("speed:" + speed); if (speed > 0) { isClockWise = true; // v = 1; } else { isClockWise = false; // v = -1; } currentTime = System.currentTimeMillis(); handler.sendEmptyMessage(0); if(deta_degree == touchRecordDegree){ onDegreeClick(event.getX(),event.getY()); } break; } } return true; } private void onDegreeClick(float x, float y) { float dx = x - width/2; float dy = height/2 - y; float diff = (deta_degree+360)%360; float click = (float) Math.toDegrees(Math.atan(Math.abs(dy/dx))); // Toast.makeText(getContext(),"x = "+dx+" y= "+dy+" off = "+diff+" click = " + click,Toast.LENGTH_SHORT).show(); //用来判断点击位置的角度 float result = 0; if(dx>0 && dy > 0){//第一象限 result = (540 - click - diff)%360;//(90+(90-click)+diff+360)%360 }else if(dx < 0 && dy > 0){//第二象限 result = (360 + click - diff)%360; }else if(dx < 0 && dy < 0){//第三象限 result = (720 - click - diff)%360;//((90-click)+270-diff+360)%360 }else if(dx > 0 && dy < 0){//第四象限 result = (540 + click - diff)%360; }else if(dx == 0){ onDegreeClick(x+0.01f,y); }else if(dy == 0){ onDegreeClick(x,y+0.01f); } //分配圆之后,result从x轴负半轴开始顺时针增加 if(partClickListener!=null){ float partDegree = 360/partCount; int postion = (int) (result / partDegree); partClickListener.onPartClick(postion); } } /** * 计算以(src_x,src_y)为坐标圆点,建立直角体系,求出(target_x,target_y)坐标与x轴的夹角 * 主要是利用反正切函数的知识求出夹角 * * @param src_x * @param src_y * @param target_x * @param target_y * @return */ float detaDegree(float src_x, float src_y, float target_x, float target_y) { float detaX = target_x - src_x; float detaY = target_y - src_y; double d; if (detaX != 0) { float tan = Math.abs(detaY / detaX); if (detaX > 0) { if (detaY >= 0) { d = Math.atan(tan); } else { d = 2 * Math.PI - Math.atan(tan); } } else { if (detaY >= 0) { d = Math.PI - Math.atan(tan); } else { d = Math.PI + Math.atan(tan); } } } else { if (detaY > 0) { d = Math.PI / 2; } else { d = -Math.PI / 2; } } return (float)((d * 180) / Math.PI); } /** * 一个异常,用来判断是否有rotatbitmap * * @author sun.shine */ static class NoBitMapError extends RuntimeException { /** * */ private static final long serialVersionUID = 1L; public NoBitMapError(String detailMessage) { super(detailMessage); } } /** * 速度计算器 原来是将最近的 弧度增量和时间点记录下来,然后<br> * 通过增量除以总时间求出平均值做为它的即时手势滑过的速度 * * @author sun.shine */ static class VRecord { /** * 数组中的有效数字 */ int addCount; /** * 最大能装的数据空间 */ public static final int length = 15; /** * 二维数组,1.保存弧度增量.2.保存产生这个增量的时间点 */ double[][] record = new double[length][2]; /** * 为二维数组装载数据<br> * 注:通过此方法,有个特点,能把最后的length组数据记录下来,length以外的会丢失 * * @param detadegree * @param time */ public void add(double detadegree, double time) { for (int i = length - 1; i > 0; i--) { record[i][0] = record[i - 1][0]; record[i][1] = record[i - 1][1]; } record[0][0] = detadegree; record[0][1] = time; addCount++; } /** * 最大速度 */ public static final double max_speed = 8; /** * 通过数组里所装载的数据分析出即时速度<br> * 原理是:计算数组里的时间长度和增量的总数,然后求出每毫秒所走过的弧度<br> * 当然不能超过{@link VRecord#max_speed} * * @return */ public double getSpeed() { if (addCount == 0) { return 0; } int maxIndex = Math.min(addCount, length) - 1; if ((record[0][1] - record[maxIndex][1]) == 0) { return 0; } double detaTime = record[0][1] - record[maxIndex][1]; double sumdegree = 0; for (int i = 0; i < length - 1; i++) { sumdegree += record[i][0]; // System.out.println(record[i][0]); } // System.out.println("----------"); // System.out.println(sumdegree); // System.out.println(detaTime); double result = sumdegree / detaTime; if (result > 0) { return Math.min(result, max_speed); } else { return Math.max(result, -max_speed); } // System.out.println("v=" + result); } /** * 重置 */ public void reset() { addCount = 0; for (int i = length - 1; i > 0; i--) { record[i][0] = 0; record[i][1] = 0; } } } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); if(rotatBitmap!=null){ rotatBitmap.recycle(); rotatBitmap=null; } } private OnPartClickListener partClickListener; private int partCount; /** * 圆心为坐标原点,从x轴负半轴开始顺时针增加,onPartClick中 position 从0开始 * @param partClickListener * @param partCount 一共需要把圆分成几个部分 */ public void setOnPartClickListener(OnPartClickListener partClickListener,int partCount){ this.partClickListener = partClickListener; this.partCount = partCount; } public interface OnPartClickListener{ void onPartClick(int postion); } }
调用的时候需要设置view上旋转的图片
MainActivity.class
package com.sun.shine.myrotation; import com.sun.shine.myrotation.view.RotateView; import android.os.Bundle; import android.app.Activity; import android.widget.Toast; public class MainActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); RotateView rotatView=(RotateView)findViewById(R.id.myRotatView); rotatView.setRotatDrawableResource(R.drawable.test_item); rotatView.setOnPartClickListener(new RotateView.OnPartClickListener() { @Override public void onPartClick(int postion) { Toast.makeText(MainActivity.this," postion = "+postion,Toast.LENGTH_SHORT).show(); } },8); } }
activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@android:color/black"> <com.sun.shine.myrotation.view.RotateView android:background="@android:color/darker_gray" android:id="@+id/myRotatView" android:layout_width="300dp" android:layout_height="300dp" android:layout_centerInParent="true" /> </RelativeLayout>
相关文章推荐
- Android CircleMenu:旋转转盘选择Menu
- Android CircleMenu:旋转转盘选择Menu
- jQuery旋转插件jqueryrotate,制作转盘
- JS利用CSS3 transform rotate 实现旋转转盘或背景(大转盘抽奖)
- 旋转转盘选择Menu--第三方开源--CircleMenu
- iOS制作 转盘EaseInEaseOut旋转动画
- jQuery实现旋转转盘抽奖代码
- 【C++】转盘旋转算法
- canvas绘制旋转转盘
- 【UE4】【技巧】物体绕圆心转动(适用于转盘,旋转开关等)
- 用jQuery旋转插件jqueryrotate制作转盘抽奖
- 移动端转盘指针触摸旋转
- 旋转转盘__surfaceView
- 旋转转盘
- 旋转转盘选择Menu--第三方开源--CircleMenu
- 转盘旋转算法
- Android自定义View实现转盘旋转的效果
- 旋转转盘 算法
- cocos2dx实现转盘旋转外加粒子效果
- unity 做一个旋转起来的转盘(非抽奖)