效果图:
好久没有写博客了,在帮朋友修改项目的时候偶然发现了抽奖转盘,觉得挺好玩儿,就自己写了一个,为此我还特意贡献出了自己的女神们,哈哈。。。
项目参考了鸿洋的博客,在此特别感谢鸿洋大神的帮助,参考博客:http://blog.csdn.net/lmj623565791/article/details/41722441
这个自定义控件与鸿洋所写的不同点在于:
鸿洋牌抽奖转盘实现方式:
1.鸿洋采用继承并重写SurfaceView来实现,通过在子线程中不断绘制来实现转盘旋转。
2.抽奖图片采用正视角,也就是旋转过程中所看到的图片都是竖立的。
3.转盘集成在一个类中,引用时需要在xml中添加点击抽奖图片和指针。
我自己的实现方式:
1.采用传统的继承View的方式来绘制转盘,在子线程中发送旋转指令,然后在UI线程中使用View旋转动画来进行旋转。—-View旋转动画底层也是通过重新绘制实现
2.抽奖图片采用相对模块正视角。—这个纯属个人视觉爱好
3.将点击抽奖图片和绘制的转盘以及抽奖回调封装到一起,方便直接调用。
我将这个控件分布于三个类中,以下提供代码:
绘制转盘的LuckyRoller类:
package com.lxh.custom.view.luckRoller;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;
import java.util.List;
/**
* Created by lxh on 2017/3/22.
* QQ-632671653
*/
public class LuckyRoller extends View {
/**
* padding值
*/
private int mPaddingLeft;
/**
* 控件中心坐标
*/
private int centerX, centerY;
/**
* 控件宽度
*/
private int with;
/**
* 外圆环宽度
*/
private int circleWith;
/**
* 圆环半径
*/
private int circleRadius;
/**
* 转盘半径
*/
private int rollerRadius;
/**
* 绘制盘块的范围
*/
private RectF mRange;
/**
* 盘块的个数
*/
private int mItemCount;
/**
* 与文字对应图片的bitmap数组
*/
private Bitmap[] mImgsBitmap;
/**
* 转盘内容实体集合
*/
private List<LuckyBean> luckyBeanList;
/**
* 文字的大小
*/
private float mTextSize = TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_SP, 20, getResources().getDisplayMetrics());
public LuckyRoller(Context context) {
super(context);
}
public LuckyRoller(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public LuckyRoller(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// super.onMeasure(widthMeasureSpec, heightMeasureSpec);
with = MeasureSpec.getSize(widthMeasureSpec);//控件宽度
setMeasuredDimension(with, with);//重设控件宽高
centerX = getMeasuredWidth() / 2; //控件X坐标
centerY = getMeasuredHeight() / 2;//控件Y坐标
circleWith = with / 12; //外圆环的宽度
mPaddingLeft = getPaddingLeft(); //控件padding值
circleRadius = centerX - mPaddingLeft; //圆环的半径
rollerRadius = circleRadius - circleWith / 2; //扇形块的半径
luckyBeanList = LuckRollerView.dataList; //填充的数据
if (luckyBeanList!=null&&luckyBeanList.size()>=1){
mItemCount = luckyBeanList.size();
if (0 != mItemCount) {
// 圆弧的绘制范围
mRange = new RectF(centerX - circleRadius + circleWith / 2, centerY - circleRadius + circleWith / 2,
centerX + circleRadius - circleWith / 2, centerY + circleRadius - circleWith / 2);
// 初始化图片
mImgsBitmap = new Bitmap[mItemCount];
for (int i = 0; i < mItemCount; i++) {
mImgsBitmap[i] = BitmapFactory.decodeResource(getResources(), luckyBeanList.get(i).getImg());
}
}
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (0 != mItemCount) {
Paint paint = new Paint();
drawCircle(canvas, paint);
Paint rollerPaint = new Paint();
rollerPaint.setAntiAlias(true);//抗锯齿
rollerPaint.setDither(true);//防抖动
Paint mTextPaint = new Paint();
mTextPaint.setColor(0xFFffffff);
mTextPaint.setTextSize(mTextSize);
float tmpAngle = 0;
float sweepAngle = (float) (360 / 6);
for (int i = 0; i < 6; i++) {
drawRoller(canvas, rollerPaint, i, tmpAngle, sweepAngle);
drawText(tmpAngle, sweepAngle, luckyBeanList.get(i).getName(), canvas, mTextPaint);
drawIcon(tmpAngle, mImgsBitmap[i], canvas);
tmpAngle += sweepAngle;
}
}
}
/**
* 绘制文本
*
* @param startAngle
* @param sweepAngle
* @param string
*/
private void drawText(float startAngle, float sweepAngle, String string, Canvas mCanvas, Paint mTextPaint) {
Path path = new Path();
path.addArc(mRange, startAngle, sweepAngle);
float textWidth = mTextPaint.measureText(string);
// 利用水平偏移让文字居中
float hOffset = (float) (rollerRadius * Math.PI / mItemCount - textWidth / 2);// 水平偏移
float vOffset = rollerRadius / 6;// 垂直偏移
mCanvas.drawTextOnPath(string, path, hOffset, vOffset, mTextPaint);
}
/**
* 根据给定的宽和高进行缩放
*
* @param origin 原图
* @param newWidth 新图的宽
* @param newHeight 新图的高
* @return new Bitmap
*/
private Bitmap scaleBitmap(Bitmap origin, int newWidth, int newHeight) {
if (origin == null) {
return null;
}
int height = origin.getHeight();
int width = origin.getWidth();
float scaleWidth = ((float) newWidth) / width;
float scaleHeight = ((float) newHeight) / height;
Matrix matrix = new Matrix();
matrix.postScale(scaleWidth, scaleHeight);
Bitmap newBM = Bitmap.createBitmap(origin, 0, 0, width, height, matrix, false);
return newBM;
}
/**
* 绘制icon图片
*
* @param startAngle
* @param bitmap
* @param mCanvas
*/
private void drawIcon(float startAngle, Bitmap bitmap, Canvas mCanvas) {
// 设置图片的宽度为直径的1/8
int imgWidth = rollerRadius / 4;
// 确定绘制图片的位置
int x = centerX;
int y = (int) (centerY - rollerRadius / 1.8);
//将位图进行缩放
Bitmap mBitmap = scaleBitmap(bitmap, imgWidth, imgWidth);
mCanvas.save();//保存当前画布状态
mCanvas.rotate(startAngle + 120, centerX, centerY);//画布绕中心点旋转
mCanvas.drawBitmap(mBitmap, x - imgWidth / 2, y - imgWidth / 2, null);//绘制位图
mCanvas.restore();//恢复画图状态到保存前
}
/**
* 绘制圆环
*
* @param mCanvas
* @param mPaint
*/
private void drawCircle(Canvas mCanvas, Paint mPaint) {
mPaint.setAntiAlias(true);//抗锯齿
mPaint.setColor(Color.BLUE);
mPaint.setStrokeWidth(circleWith);
mPaint.setStyle(Paint.Style.STROKE);
mCanvas.drawCircle(centerX, centerY, circleRadius - circleWith / 2, mPaint);
}
/**
* 绘制转盘中的扇形
*
* @param mCanvas
* @param mPaint
* @param i
* @param tmpAngle
* @param sweepAngle
*/
private void drawRoller(Canvas mCanvas, Paint mPaint, int i, float tmpAngle, float sweepAngle) {
// 绘制模块
mPaint.setColor(luckyBeanList.get(i).getColor());
mCanvas.drawArc(mRange, tmpAngle, sweepAngle, true, mPaint);
}
}
转盘内容实体类LuckyBean:
package com.lxh.custom.view.luckRoller;
import android.graphics.Bitmap;
/**
* Created by lxh on 2017/3/29.
* QQ-632671653
*/
public class LuckyBean {
private int img;
private String name;
private int Color;
private Bitmap bitmap;
public int getImg() {
return img;
}
public void setImg(int img) {
this.img = img;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getColor() {
return Color;
}
public void setColor(int color) {
Color = color;
}
public Bitmap getBitmap() {
return bitmap;
}
public void setBitmap(Bitmap bitmap) {
this.bitmap = bitmap;
}
}
继承点击抽奖图片和转盘,以及旋转实现的LuckRollerView:
package com.lxh.custom.view.luckRoller;
import android.content.Context;
import android.os.CountDownTimer;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import com.lxh.custom.R;
import java.util.List;
/**
* Created by lxh on 2017/3/24.
* QQ-632671653
*/
public class LuckRollerView extends RelativeLayout {
/**
* 转盘控件
*/
private LuckyRoller luckyRoller;
/**
* 开始图片
*/
private ImageView imageView;
/**
* 旋转角度
*/
private int rotatiion = 0;
/**
* 是否循环标志
*/
private boolean isRunning = false;
/**
* 结果回调监听
*/
private OnLuckyRollerListenner onLuckyRollerListenner;
/**
* 是否已经开始
*/
private boolean isStart = false;
/**
* 是否第一次开始
*/
private boolean isFirst = true;
/**
* 速度控制
*/
private int speed = 0,sleep = 4;
/**
* 转盘数据
*/
public static List<LuckyBean> dataList;
public LuckRollerView(Context context) {
super(context);
}
public LuckRollerView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public LuckRollerView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
/**
* 利用handler切换到UI线程进行转盘旋转
*/
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case 1:
rotatiion += speed;
luckyRoller.setRotation(rotatiion);
break;
}
}
};
/**
* 任务线程
*/
private Thread thread = new Thread(new Runnable() {
@Override
public void run() {
while (isRunning) {
if (isStart) {
handler.sendEmptyMessage(1);
try {
thread.sleep(sleep);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
});
/**
* 初始化控件
* @param context
*/
private void initView(Context context) {
luckyRoller = new LuckyRoller(context);
LayoutParams layoutParams = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
layoutParams.addRule(RelativeLayout.CENTER_IN_PARENT);
luckyRoller.setLayoutParams(layoutParams);
imageView = new ImageView(context);
LayoutParams imgLp = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
imgLp.addRule(RelativeLayout.CENTER_IN_PARENT);
imageView.setLayoutParams(imgLp);
imageView.setImageResource(R.mipmap.start);
imageView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (isStart) {
pause();
} else {
startOrResume();
}
}
});
addView(luckyRoller);
addView(imageView);
}
/**
* 根据旋转角度确定选中区域
* @param rotatiion
*/
public void calInExactArea(int rotatiion) {
// 让指针从水平向右开始计算
float rotate = rotatiion+90;
int mItemCount = dataList.size();
rotate %= 360.0;//相对于原位置的旋转角度
for (int i = 0; i < mItemCount; i++) {
// 每个的中奖范围
float from = 360 - (i + 1) * (360 / mItemCount);
float to = from + 360 - (i) * (360 / mItemCount);
if ((rotate > from) && (rotate <= to)) {
if (onLuckyRollerListenner!=null){
onLuckyRollerListenner.onFinish(dataList.get(i).getName());
}
return;
}
}
}
/**
* 开始或者恢复旋转
*/
private void startOrResume() {
if (isFirst) {
isRunning = true;
isStart = true;
isFirst = false;
thread.start();
} else {
resume();
}
countDownTimer.start();
stopViewTimer.start();
}
private void resume() {
isStart = true;
}
/**
* 暂停
*/
private void pause() {
isStart = false;
speed = 0;
imageView.setImageResource(R.mipmap.start);
calInExactArea(rotatiion);
}
/**
* 为控件设置数据
* @param context
* @param data
*/
public void setData(Context context,List<LuckyBean> data){
this.dataList = data;
initView(context);
}
/**
* 通过计时器来设定速度和最短旋转时间
*/
private CountDownTimer countDownTimer = new CountDownTimer(sleep*1000,1000) {
@Override
public void onTick(long millisUntilFinished) {
speed+=1;
if (sleep>1)
sleep-=1;
imageView.setClickable(false);
imageView.setImageResource(R.mipmap.stop2);
}
@Override
public void onFinish() {
imageView.setClickable(true);
imageView.setImageResource(R.mipmap.stop);
}
};
/**
* 通过计时器来设定旋转最长时限
*/
private CountDownTimer stopViewTimer = new CountDownTimer(10000,1000) {
@Override
public void onTick(long millisUntilFinished) {
}
@Override
public void onFinish() {
if (isStart){
imageView.performClick();
}
}
};
/**
* 中断线程,回收内存
*/
public void destory(){
thread.interrupt();
}
/**
* 设置监听
* @param onLuckyRollerListenner
*/
public void setOnLuckyRollerListenner(OnLuckyRollerListenner onLuckyRollerListenner) {
this.onLuckyRollerListenner = onLuckyRollerListenner;
}
public interface OnLuckyRollerListenner{
void onFinish(String data);
}
}
使用方式:
1.在xml中调用:
<com.lxh.custom.view.luckRoller.LuckRollerView
android:id="@+id/luckRollerView"
android:layout_width="500dp"
android:layout_height="500dp"/>
2.在相应java类中进行初始化:
/**
* 与文字对应的图片
*/
private int[] mImgs = new int[]{R.mipmap.fanbingbing, R.mipmap.libb,
R.mipmap.liuss, R.mipmap.lyf, R.mipmap.dlrb,R.mipmap.linzl};
/**
* 抽奖的文字
*/
private String[] mStrs = new String[]{"范冰冰", "李冰冰", "刘诗诗", "刘亦菲",
"迪丽热巴", "林志玲"};
/**
* 每个盘块的颜色
*/
private int[] mColors = new int[]{0xFFFFC300, 0xFFF17E01, 0xFFFFC300,
0xFFF17E01, 0xFFFFC300, 0xFFF17E01};
private void initData(){
for (int i = 0;i<mStrs.length;i++){
LuckyBean luckyBean = new LuckyBean();
luckyBean.setName(mStrs[i]);
luckyBean.setColor(mColors[i]);
luckyBean.setImg(mImgs[i]);
luckyBeanList.add(luckyBean);
}
}
private void initView() {
initData();
luckRollerView = (LuckRollerView) findViewById(R.id.luckRollerView);
luckRollerView.setData(mContext, luckyBeanList);
luckRollerView.setOnLuckyRollerListenner(new LuckRollerView.OnLuckyRollerListenner() {
@Override
public void onFinish(String data) {
Toast.makeText(mContext, "恭喜你抽中了-" + data + "-", Toast.LENGTH_LONG).show();
}
});
}
以上代码都有详细注释,因此就不在赘述。已将项目完整代码上传至github。有需要可自行下载。
github地址:https://github.com/lxh632671653/custom
这个控件已经写好很久了,因为私人原因未能及时发布博客,望各位大佬海涵。谢谢!技术交流请加QQ:632671653