android转盘源码分析

转盘git下载地址:https://github.com/anupcowkur/Android-Wheel-Menu


package com.anupcowkur.wheelmenu;


import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;
/**
 * 继承ImageView在onSizeChanged()方法中基于视图大小设计显示图片的大小,基于Matrix实现车轮的转动,通过重写onTouchEvent实现手动转动车轮,
 * 在检测到手势检测当前触摸点的位置(x,y),然后将(x,y)转换为旋转的角度,在转换角度时根据(x,y)坐标的正负调用getQuadrant()判断可以分为四个位置,
 * 通过写一个角度转换的算法getAngle()计算出(x,y)当前的角度,将调用旋转方法Matrix.postRotate(degrees, wheelWidth/2, wheelHeight/2)实现当前旋转degrees(角度),
 * 以(wheelWidth/2, wheelHeight/2)为中心点
 */
public class WheelMenuRotation extends ImageView{


private Bitmap imageOriginal, imageScaled; //原始图片,真正显示的缩放图片
private Matrix matrix; //Matrix用于执行滚动,缩放,移动操作
private int wheelHeight,wheelWidth; //视图的高度和宽度
private int top; //当前车轮向上的位置
private double totalRotation; //统计总的转动角度
//通过用户向下或者向上的操作出发车轮滚动
private int divCount; //当前车轮划分多少个区域
private int divAngle; //每个区域占多大角度
private int selectedPosition; //当前被选中的位置
private boolean snapToCenterFlag = true; //是否是选中位置的中心指向上面
private Context context;
private WheelChangeListener wheelChangeListener;

public WheelMenuRotation(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}

/**
* 初始化

* @param context
*/
private void init(Context context) {
this.context = context;
this.setScaleType(ScaleType.MATRIX);
selectedPosition = 0;

//只需要初始化Matrix一次
if(matrix == null){
matrix = new Matrix();
}else{
matrix.reset();
}

this.setOnTouchListener(new WheelTouchListener());
}

/**
* 添加用户选择位置变化监听器

* @param wheelChangeListener
*/
public void setWheelChangeListener(WheelChangeListener wheelChangeListener){
this.wheelChangeListener = wheelChangeListener;
}

//返回用户选中的位置
public int getSelectedPosition(){
return selectedPosition;
}

/**
* 设置分段数及每段角度

* @param divCount
*/
public void setDivCount(int divCount){
this.divCount = divCount;

divAngle = 360/divCount;
totalRotation = -1 * (divAngle / 2);
}

/**
* 设置选中对应中心位置标记

* @param snapToCenterFlag
*/
public void setSnapToCenterFlag(boolean snapToCenterFlag){
this.snapToCenterFlag = snapToCenterFlag;
}

/**
* 设置默认选中的顶端位置,默认是0
* 应该设置在{#setDivCount(int) setDivCount}方法之后,这个值应该比0大,同事比divCount小
* 否则提供的值将被忽略  
* @param newTopDiv
*/
public void setAlternateTopDiv(int newTopDiv){
if(newTopDiv < 0 || newTopDiv >= divCount){
return;
}else{
top = newTopDiv;
}

selectedPosition = top;
}

/**
* 设置车轮的图片
* @param drawableId
*/
public void setWheelImage(int drawableId){
imageOriginal = BitmapFactory.decodeResource(context.getResources(), drawableId);
}

/**
* 获取视图的尺寸,一旦获取到视图尺寸以后则缩放图片适应视图的显示效果,
* 初始化Matrix同时指定视图居中显示
*/
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
//该方法将会调用多次,但只初始化一次
if(wheelWidth == 0 || wheelHeight == 0){
wheelHeight = h;
wheelWidth = w;
//重定义图片的尺寸
Matrix resize = new Matrix();
resize.postScale((float)Math.min(wheelWidth, wheelHeight) / (float) imageOriginal.getWidth(), 
(float) Math.min(wheelWidth, wheelHeight) / (float) imageOriginal.getHeight());
imageScaled = Bitmap.createBitmap(imageOriginal, 0, 0, imageOriginal.getWidth(), imageOriginal.getHeight(), resize, false);
//视图居中显示
float translateX = wheelWidth / 2 - imageScaled.getWidth()/2;
float translateY = wheelHeight / 2 - imageScaled.getHeight()/2;
matrix.postTranslate(translateX, translateY);
WheelMenuRotation.this.setImageBitmap(imageScaled);
WheelMenuRotation.this.setImageMatrix(matrix);
}
}

//获取触摸事件对应的角度
private double getAngle(double x, double y){
x = x - (wheelWidth / 2d);
y = wheelHeight - y - (wheelHeight / 2d);

switch (getQuadrant(x, y)) {
case 1:
return Math.asin(y / Math.hypot(x, y))*180 / Math.PI;
case 2:
return 180 - Math.asin(y / Math.hypot(x, y))*180 / Math.PI;
case 3:
return 180 + (-1 * Math.asin(y / Math.hypot(x, y))) * 180 / Math.PI;
case 4:
return 360 + Math.asin(y / Math.hypot(x, y)) * 180 / Math.PI;
default:
return 0;
}
}

//获取点击的位置在哪,主要分为四个区域,+
private static int getQuadrant(double x, double y){
if(x >= 0){
return y >= 0 ? 1 : 4;
}else{
return y >= 0 ? 2 : 3;
}
}

//转动车轮转动多少角度
private void rotateWhell(float degrees){
matrix.postRotate(degrees, wheelWidth/2, wheelHeight/2);
//设置以后车轮才能转动
WheelMenuRotation.this.setImageMatrix(matrix);

//增加转动 的总角度
totalRotation = totalRotation + degrees;
}

//监听用户位置变化接口
public interface WheelChangeListener {
//当用户选择新位置时调用该方法
public void onSelectionChange(int selectedPosition);
}

//监听在车轮上的触摸事件
private class WheelTouchListener implements View.OnTouchListener{
private double startAngle;


@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
//获取当前移动事件开始的角度
startAngle = getAngle(event.getX(), event.getY());
break;
case MotionEvent.ACTION_MOVE:
double currentAngle = getAngle(event.getX(), event.getY());
//转动滚轮
rotateWhell((float)(startAngle - currentAngle));
//当前的位置变为开始的位置
startAngle = currentAngle;
break;
case MotionEvent.ACTION_UP:
//获取总转动角度在360内
totalRotation = totalRotation % 360;

//呈现总的角度在正直
if(totalRotation < 0){
totalRotation = 360 + totalRotation;
}

//计算没有没有跨越的div数量
int no_of_divs_crossed = (int) ((totalRotation)/divAngle);

//计算当前的top
top = (divCount + top - no_of_divs_crossed) % divCount;

//为了下一个滚动,初始化总的转动角度没有在当前角度的里面
totalRotation = totalRotation % divAngle;

//调整中心显示
if(snapToCenterFlag){
//计算转动的角度到顶端的中心
double leftover = divAngle / 2 - totalRotation;
rotateWhell((float)(leftover));

//重新初始化总的角度
totalRotation = divAngle/2;
}

//设置当前被选中的选项
if(top == 0){
selectedPosition = divCount - 1;
}else{
selectedPosition = top - 1;
}

if(wheelChangeListener != null)
{
wheelChangeListener.onSelectionChange(selectedPosition);
}

break; 
default:
break;
}
return true;
}
}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值