Android-自定义幸运抽奖转盘

效果图:这里写图片描述

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

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值