zhuanpanView.java
/*
* 说明:
* 该类自定义了转盘类,可通过给定转盘中各个块的概率,控制指针的指向
* 创建转盘时,需提供转盘背景图像panpic 和 指针图像panhandpic,二者为宽度相同的正方行
* 转盘的块数由概率数组p[]限定
*
* 调用:
* final zhuanpanView zhuanpan = new zhuanpanView(this) ; //创建转盘
* int p[] = {20, 0, 20, 0, 30, 30}; //p为转盘所有块的概率数组
* zhuanpan.startRotate(p); //转盘指向对应块,返回值为块位置(从0顺时针计数)
* - 若转盘处于旋转状态再次调用,则返回-1.
*/
import java.util.Random;
import android.app.Activity;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.WindowManager;
//自定义的转盘View
public class zhuanpanView extends View implements Runnable
{
//界面需要的图片
private Bitmap panpic;
private Bitmap panhandpic;
public int width, height; //转盘的宽度和高度
//旋转矩阵
private Matrix panRotate=new Matrix(); //可控制盘面的旋转
private Matrix panhandTrans=new Matrix(); //可控制指针的旋转
private int degree=0; //旋转角度
private float degreeSpeed=0; //旋转角度变化增量,控制转盘转速由快到慢
private int destdegree=360*6+60; //最终停止角度位置
private int offDegree = 0; //角度偏移量,记录转盘指针前一次的位置
public boolean flagRotate=false; //标志转盘指针当前是否处于旋转状态
private int centerX, centerY; //旋转中心位置
public static Random random = new Random(); //随机数对象
//创建转盘控件-代码创建时,可调用该函数
public zhuanpanView(Context context)
{
super(context);
zhuanpanViewInit(context);
}
//创建转盘控件-XML布局创建控件时,会调用该函数
public zhuanpanView(Context context, AttributeSet attrs)
{
super(context, attrs);
zhuanpanViewInit(context);
}
//转盘控件自身的初始化
private void zhuanpanViewInit(Context context)
{
Resources r=context.getResources();
//生成图片
panpic=BitmapFactory.decodeStream(r.openRawResource(R.drawable.table));
panhandpic=BitmapFactory.decodeStream(r.openRawResource(R.drawable.pointer));
//转盘大小自适应屏幕,已480屏幕宽度为基准
float magnify = getMagnifyParams((Activity)context);
panpic=resize(panpic, magnify);
panhandpic=resize(panhandpic, magnify);
width = panhandpic.getWidth(); //设置转盘的宽度
height = panhandpic.getHeight();
centerX = width/2; //获取指针旋转的中心位置
centerY = height/2;
//用线程来刷新界面
Thread thread=new Thread(this);
thread.start();
}
//获取与屏幕适配的放大倍数
private float getMagnifyParams(Activity activity)
{
float magnify = 1f;
//获取屏幕的宽度和高度
WindowManager wm = activity.getWindowManager();
int screenWidth = wm.getDefaultDisplay().getWidth();
int screenHeight = wm.getDefaultDisplay().getHeight();
int min = screenWidth < screenHeight ? screenWidth : screenHeight; //取宽高较小的
magnify = min/480f;
Log.e("转盘自适应:", " ");
Log.e("屏幕分辨率", screenWidth+"," + screenHeight);
Log.e("转盘放大倍数", magnify+" " );
return magnify;
}
//将Bitmap放大size倍
private static Bitmap resize(Bitmap bitmap, float size)
{
Matrix matrix = new Matrix();
matrix.postScale(size, size); //长和宽放大缩小的比例
Bitmap resizeBmp = Bitmap.createBitmap(bitmap,0,0,bitmap.getWidth(),bitmap.getHeight(),matrix,true);
return resizeBmp;
}
//重写View类的onDraw()函数
@Override
protected void onDraw(Canvas canvas)
{
canvas.drawBitmap(panpic, panRotate, null); //绘制出转盘
panhandTrans.setRotate(degree+offDegree, centerX, centerY); //转盘指针绕其中心旋转角度degree
canvas.drawBitmap(panhandpic, panhandTrans, null); //绘制指针
//控制位置的平移
//panhandTrans.setTranslate(centerX-panhandpic.getWidth()/2, centerY-panhandpic.getHeight()/5*3);
}
//重写的run函数,用来控制转盘指针的转动,改变旋转的角度
public void run()
{
try
{
while(true)
{
if(flagRotate && degree < destdegree) //控制转盘中的指针旋转到指定角度
{
this.degree += (int)degreeSpeed; //当前旋转到的角度
//这个函数强制UI线程刷新界面
this.postInvalidate();
Thread.sleep(30); //每秒40次绘制
degreeAccelerate(); //执行加减速处理
if(degree >= destdegree) flagRotate = false;
}
}
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
//控制转盘中指针的旋转加减速
private void degreeAccelerate()
{
if(degreeSpeed == 0) degreeSpeed = 1;
if(degree<230) //执行加速,大概130度范围加速到最大
{
if(degreeSpeed <= 20)
degreeSpeed *= 1.19f;
else if(degreeSpeed >= 21)
degreeSpeed = 20.57f;
}
else if(destdegree - degree < 720) //执行减速
{
if(degreeSpeed > 1)
degreeSpeed *= 0.9752f;
else
degreeSpeed = 1;
}
}
//转盘数据重置
public void reset()
{
offDegree = 0;
degree=0;
degreeSpeed=0;
}
//重置当前角度和转速,保存指针角度位置
private void reset2()
{
offDegree = (degree+offDegree) % 360; //先保存上次的角度偏移值
degree=0;
degreeSpeed=0;
}
//根据概率数组p控制转盘的旋转
public int startRotate(int p[])
{
if(flagRotate) //如果转盘正处于旋转状态
{
Log.e("转盘选中位置", "-1");
return -1; //-1表示转盘当前不可用
}
reset2(); //获取转盘指针上次的角度偏移值,重置当前角度
int select = set_destdegree(p); //根据各块的给定概率设置旋转的最终角度和选中块位置
Log.e("转盘选中位置", select+"");
this.flagRotate=true; //开始旋转
return select;
}
/**
* 按默认概率,控制转盘旋转,
* 转盘块数 = 概率数组p.length
* startRotate(p)控制转盘旋转,返回值为转盘指针指向的块号
*/
public void startRotate1()
{
int p[] = {0, 20, 20, 20, 20, 20, 20, 20};
startRotate(p);
}
//停止指针旋转,可不调用待其自动停止
public void stopRotate()
{
this.flagRotate=false;
}
//根据各部分的比例值,设置旋转最终停止位置
public int set_destdegree(int p[])
{
int len = p.length; //转盘中的总块数
random.setSeed(random.nextLong()); //随机设置其随机种子,进一步随机化
int countp = 0; //统计所有块的总概率
for(int j= 0; j < len; j++)countp += p[j];
int num = random.nextInt(countp) + 1; //生成1到countp之间的随机数
//根据各块的比例值,将num转换为对应的块位置 i
int tmp = 0; //累计前面各块的概率和
int i=0;
for(; i<len && tmp < num; i++)
{
tmp += p[i]; //累计概率
if(num <= tmp)break; //num在块p[i]的概率范围内
}
int partDegree = (int)(360/len); //计算各块所占的角度大小
int partRnddegree = random.nextInt(partDegree-3)+2; //块内角度随机偏移, -3)+2避免指向两块的交界处
//块位置转化为块的对应角度
destdegree = partDegree * i + 360*6 + partRnddegree;//指向选定块,多转6圈,块内角度随机
destdegree -= offDegree; //目标角度减少初始偏移
return i; //返回指针所指的块位置
}
}
添加转盘操作
添加:
=》1.
zhuanpanView.java、
pointer.png、table.png
到项目
=》2.
NewActivity.java中(待添加转盘的Activity)
添加全局变量:
public zhuanpanView zhuanpan; //添加转盘
public Handler handler; //处理转盘的显示与隐藏
NewActivity.onCreate(){}中添加: ——可调整 params.topMargin = 50; 的值修改转盘的垂直位置
handler = new Handler()
{
@SuppressLint("HandlerLeak")
@Override
public void handleMessage(Message msg)
{
switch (msg.what)
{
case 1: //显示转盘
if(zhuanpan==null) //没有创建转盘,则创建
{
zhuanpan = new zhuanpanView(NewActivity.this); //创建转盘
//转盘的布局参数
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(zhuanpan.width, zhuanpan.height);
params.topMargin = 50;
params.gravity = Gravity.CENTER_HORIZONTAL|Gravity.CENTER_VERTICAL/*Gravity.TOP*/;
addContentView(zhuanpan, params); //添加转盘到Activity
}
else zhuanpan.setVisibility(View.VISIBLE); //已有,则显示
break;
case 2: //隐藏转盘
if(zhuanpan!=null) //转盘存在
{
zhuanpan.setVisibility(View.INVISIBLE); //隐藏
zhuanpan.reset(); //转盘重置,回到初始状态
}
// if(zhuanpan!=null && zhuanpan.getParent()!=null) ((ViewGroup)zhuanpan.getParent()).removeView(zhuanpan);
break;
case 3: //启动转盘
zhuanpan.startRotate1();
break;
}
}
};
=》3.
抽奖控制处添加:(调用函数进行控制)
/**
* 向父线程发送消息
* what = 1, 显示转盘
* what = 2, 隐藏转盘
*/
private void zhuanpanControl(int what)
{
Handler handler = NewActivity.instance.handler; //用于子线程向父线程发送消息
Message msg = new Message();
msg.what = what;
handler.sendMessage(msg); //通知handler处理事件
};
/**
* 启动转盘
* @param p 各转盘块的概率 p = {10, 10, 10, 10, 15, 15, 15, 15}
* @return 指针所指向的转盘块位置(从0顺时针计数)
*/
private int zhuanpanStart(int[] p)
{
return NewActivity.instance.zhuanpan.startRotate(p);
}
http://download.csdn.net/detail/scimence/8016379
附录:(2015_08_17)
1、 zhuanpanView2.java
package com.linkstudio.popstar.script;
/*
* 说明:
* 该类自定义了转盘类,可通过给定转盘中各个块的概率,控制指针的指向
* 创建转盘时,需提供转盘背景图像panpic 和 指针图像panhandpic,二者为宽度相同的正方行
* 转盘的块数由概率数组p[]长度限定
*
* 默认设置:初始时,转盘指针指向0度位置,指向第一个块的中心位置(左边缘,设置pointerCenter = false;)
*
* 调用:
* final zhuanpanView zhuanpan = new zhuanpanView2() ; //创建转盘
* int p[] = {20, 0, 20, 0, 30, 30}; //p为转盘所有块的概率数组
* zhuanpan.startRotate(p); //转盘指向对应块,返回值为块位置(从0顺时针计数)
* - 若转盘处于旋转状态再次调用,则返回-1.
*/
import java.util.Random;
import android.util.Log;
import com.hlge.lib.base.Component;
// 自定义的转盘View
public class zhuanpanView2 implements Runnable
{
Component panhand;
private int degree = 0; // 旋转角度
private float degreeSpeed = 0; // 旋转角度变化增量,控制转盘转速由快到慢
private int destdegree = 0; // 最终停止角度位置
private int offDegree = 0; // 角度偏移量,记录转盘指针前一次的位置
private float drawOffDegree = 0; // 绘制偏移角度,指针指向距0度位置的度数,顺时针为正
public boolean flagRotate = false; // 标志转盘指针当前是否处于旋转状态
private float centerX, centerY; // 旋转中心位置
private int blockNums = 1; // 转盘中的总块数
protected float blockDegree = 360; // 一个转盘块占用的角度
public static Random random = new Random(); // 随机数对象
public boolean pointerCenter = true; // 转盘指针是否指向第一块的中心位置,false:指向第一块的起始边缘
// 转盘指针角度的调整
public void degreeAdjust(float drawOffDegree)
{
this.drawOffDegree = drawOffDegree - 0.3f;
panhand.setRotate(centerX, centerY, -(degree + offDegree + drawOffDegree));
}
// 设置转盘指针的中心位置
public void setCenter(float centerX, float centerY)
{
this.centerX = centerX;
this.centerY = centerY;
panhand.setRotate(centerX, centerY, -(degree + offDegree + drawOffDegree));
}
// 创建转盘控件-代码创建时,可调用该函数,X/Y为位置调整
public zhuanpanView2(Component panhand, float X, float Y)
{
this.panhand = panhand;
panhand.setXY((short) (panhand.x + X), (short) (panhand.y + Y)); // 重新调整位置
this.centerX = panhand.width / 2;
this.centerY = panhand.height / 2;
}
public boolean isRun = false;
public void start()
{
Thread thread = new Thread(this);
thread.start();
isRun = true;
}
public void interrupt()
{
isRun = false;
}
// 重写的run函数,用来控制转盘指针的转动,改变旋转的角度
public void run()
{
try
{
// while(isRun && (ScriptLib.luckyform != null || ScriptLib.sevenDayReward_Lucky != null))
while (isRun)
{
if (flagRotate && degree < destdegree) // 控制转盘中的指针旋转到指定角度
{
this.degree += (int) degreeSpeed; // 当前旋转到的角度
update(); // 转盘旋转时,角度变动执行相应逻辑
panhand.setRotate(centerX, centerY, -(degree + offDegree + drawOffDegree)); // 转盘指针绕其中心旋转角度degree
// panhand.setRotate(-(degree+offDegree+drawOffDegree));
Thread.sleep(25); // 每秒25毫秒操作一次
degreeAccelerate(); // 执行加减速处理
if (degree >= destdegree)
{
flagRotate = false;
rotateStopDoing(); // 转盘停止转动时,执行逻辑
interrupt();
}
}
}
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
/** 更新操作,供子类重写 */
public void update()
{}
/** 转盘停止转动时调用,供子类重写 */
public void rotateStopDoing()
{}
// 控制转盘中指针的旋转加减速
private void degreeAccelerate()
{
if (degreeSpeed == 0) degreeSpeed = 1;
if (degree < 230) // 执行加速,大概130度范围加速到最大
{
if (degreeSpeed <= 20)
degreeSpeed *= 1.19f;
else if (degreeSpeed >= 21) degreeSpeed = 20.57f;
}
else if (destdegree - degree < 720) // 执行减速
{
if (degreeSpeed > 1)
degreeSpeed *= 0.9752f;
else
degreeSpeed = 1;
}
}
// 转盘数据重置
public void reset()
{
offDegree = 0;
degree = 0;
degreeSpeed = 0;
}
// 重置当前角度和转速,保存指针角度位置
private void reset2()
{
offDegree = (degree + offDegree) % 360; // 先保存上次的角度偏移值
degree = 0;
degreeSpeed = 0;
}
// 根据概率数组p控制转盘的旋转
public int startRotate(int p[])
{
if (flagRotate) // 如果转盘正处于旋转状态
{
Log.e("转盘选中位置", "-1");
return -1; // -1表示转盘当前不可用
}
reset2(); // 获取转盘指针上次的角度偏移值,重置当前角度
int select = set_destdegree(p); // 根据各块的给定概率设置旋转的最终角度和选中块位置
Log.e("转盘选中位置", select + "");
this.flagRotate = true; // 开始旋转
start();
return select;
}
/** 按默认概率,控制转盘旋转, 转盘块数 = 概率数组p.length startRotate(p)控制转盘旋转,返回值为转盘指针指向的块号 */
public void startRotate1()
{
int p[] = { 0, 20, 20, 20, 20, 20, 20, 20 };
startRotate(p);
}
// 停止指针旋转,可不调用待其自动停止
public void stopRotate()
{
this.flagRotate = false;
}
/**
* 获取转盘指针实时角度值,相对于第0块起始边角度位置
*/
public int curDegree()
{
float half = (pointerCenter ? (int)(blockDegree / 2) : 0);
return (int)(degree + half + offDegree);
}
/**
* 指针角度为degree时,所指的块位置
*/
public int blockIndex(float degree)
{
return (int) (degree % 360 / blockDegree);
}
/**
* 指针角度为degree时,所指的块位置(限定块角度大小partDegree)
*/
public int blockIndex(float degree, float partDegree)
{
return (int) (degree % 360 / partDegree);
}
// 根据各部分的比例值,设置旋转最终停止位置
public int set_destdegree(int p[])
{
blockNums = p.length; // 转盘中的总块数
random.setSeed(random.nextLong()); // 随机设置其随机种子,进一步随机化
int countp = 0; // 统计所有块的总概率
for (int j = 0; j < blockNums; j++)
countp += p[j];
int num = random.nextInt(countp) + 1; // 生成1到countp之间的随机数
// 根据各块的比例值,将num转换为对应的块位置 i
int tmp = 0; // 累计前面各块的概率和
int i = 0;
for (; i < blockNums && tmp < num; i++)
{
tmp += p[i]; // 累计概率
if (num <= tmp) break; // num在块p[i]的概率范围内
}
blockDegree = 360 / blockNums; // 计算各块所占的角度大小
int partRnddegree = random.nextInt((int)blockDegree - 9) + 5; // 块内角度随机偏移, -9)+5避免指向两块的交界处
// 块位置转化为块的对应角度
destdegree = (int)(blockDegree * i) + 360 * 6 + partRnddegree; // 指向选定块,多转6圈,块内角度随机
if (pointerCenter) destdegree -= (int)(blockDegree / 2); // 相对于转盘背景少转动半个块内角
destdegree -= offDegree; // 目标角度减少初始偏移
return i; // 返回指针所指的块位置
}
}
/**
* 2015-8-11上午10:18:58
* wangzhongyuan
*/
package com.linkstudio.popstar.script;
import android.util.Log;
import com.hlge.lib.base.Component;
import com.hlge.lib.tool.SoundManager;
/**
* zhuanpanView 继承自转盘基础类zhuanpanView2,新增转盘指示灯、即时音效
* -----
* 2015-8-11 上午10:18:58
* wangzhongyuan
*/
public class zhuanpanView extends zhuanpanView2
{
private float nextDegree = 0; // 下一处更新操作,角度位置
zhuanpanLights16 tex;
private boolean thisFormDebug = true;
public zhuanpanView(Component panhand, float X, float Y, zhuanpanLights16 tex)
{
super(panhand, X, Y);
this.tex = tex;
}
/** 转盘指针指向各个块时,的更新操作 */
public void update()
{
int degree = curDegree();
if (degree > nextDegree)
{
int index = blockIndex(degree, blockDegree / 2); // (blockDegree/2)块大小的块索引
if (index % 2 == 1) SoundManager.playSound("zhuandong.mp3", false);
updateProcess(index); // 显示指针所指块索引对应内容
nextDegree += blockDegree / 2; // 间隔一定角度值,执行更新操作
}
}
String info = "";
// 更新转盘显示信息
private void updateProcess(int curBlock)
{
if (thisFormDebug) info += curBlock + ", " + nextDegree + "; ";
tex.index = curBlock;
}
/** 转盘停止转动时调用 */
public void rotateStopDoing()
{
selectMessage(); //转盘停止转动调用逻辑
SoundManager.playSound("zhuandong_js.mp3", false); //播放转盘停止转动音效
tex.index = -1; //转盘重置,不绘制灯
if (thisFormDebug)
{
Log.e("转盘指针所指块变动:", info);
info = "";
}
nextDegree %= 360;
}
/**
* 转盘停止转动时调用,选中块处理逻辑,供子类调用
*/
public void selectMessage() {}
}
3、zhuanpanLights16.java
/**
* 2015-8-11上午10:19:17
* wangzhongyuan
*/
package com.linkstudio.popstar.script;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.hlge.lib.base.HlgeTexture;
import com.hlge.lib.base.SpineTexture;
/**
* Lights16 转盘指示灯
* -----
* 2015-8-11 上午10:19:17
* wangzhongyuan
*/
public class zhuanpanLights16 extends HlgeTexture
{
SpineTexture[] lights;
public int index = -1, preIndex = -1;
/** 创建Texture */
public zhuanpanLights16()
{
super();
lights = new SpineTexture[16];
for (int i = 0; i < 16; i++)
lights[i] = new SpineTexture("7", (14 + i) + "", true);
}
/** SpineTexture的绘制 */
public void paint(SpriteBatch g)
{
paint(g, x, -y);
}
/** SpineTexture的绘制 */
public void paint(SpriteBatch g, float x, float y)
{
if (index >= lights.length) index = 0;
if (index >= 0 && lights[index] != null)
{
g.setColor(color.toIntBits()); // 设置透明度
if (parent != null)
{
x += parent.width / 2;
y += parent.height / 2;
}
// if (index != preIndex)
// {
// preIndex = index;
// if (lights[index].isPlayOver()) lights[index].rePlay(); // lights[index].spine.setAction(spine.actionId, false); //
// }
lights[index].paint(g, x, y);
}
}
@Override
public void dispose()
{
for (int i = 0; i < lights.length; i++)
if (lights[i] != null) lights[i].dispose();
lights = null;
}
}