效果图:
好久没有写博客了,在帮朋友修改项目的时候偶然发现了抽奖转盘,觉得挺好玩儿,就自己写了一个,为此我还特意贡献出了自己的女神们,哈哈。。。 项目参考了鸿洋的博客,在此特别感谢鸿洋大神的帮助,参考博客: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