您的位置:首页 > 其它

旋转转盘

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>

本博客原地址:https://www.geek-share.com/detail/2677524965.html

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: