Android自定义实现九宫格抽奖功能

2021-02-03 09:55:47 浏览数 (1)

最近的功能需求中需要实现用户使用签到获取的积分,可以在九宫格中进行抽奖消耗积分,这里使用的是自定义进行实现抽奖的功能,可以通过设置计算策略,来控制用户 中哪些奖以及中大奖 的概率,话不多说,直接上代码。

1.先看效果图

在这里插入图片描述

2.自定义View实现九宫格抽奖功能

代码语言:txt复制
public class LuckyView extends View {
    private Paint mPaint;
    private float mStrokeWidth = 5;
    private int mRepeatCount = 5; // 转的圈数
    private int mRectSize; // 矩形的宽和高(矩形为正方形)
    private boolean mShouldStartFlag;
    private boolean mShouldStartNextTurn = true; // 标记是否应该开启下一轮抽奖
    private int mStartLuckPosition = 0; // 开始抽奖的位置
    private int mCurrentPosition = -1; // 当前转圈所在的位置

    private OnLuckAnimationEndListener mLuckAnimationEndListener;

    /**
     * 可以通过对 mLuckNum 设置计算策略,来控制用户 中哪些奖 以及 中大奖 的概率
     */
    private int mLuckNum = 3; // 默认最终中奖位置

    private List<RectF> mRectFs; // 存储矩形的集合
    private int[] mItemColor = {Color.parseColor("#ffefd6"), Color.parseColor("#ffefd6")}; // 矩形的颜色
    private String[] mPrizeDescription = {"满20减1元券", "满10减1元券", "满30减2元券", "满5减1元券", "免单", "满300减40元券", "满100减10元券", "满500减50元券", "开始"};
    private int[] mLuckyPrizes = {R.drawable.aaa, R.drawable.bbb, R.drawable.ccc, R.drawable
            .ddd, R.drawable.aaa, R.drawable.bbb, R.drawable.ccc, R.drawable.ddd, R
            .drawable.center_lucky};
    private List<String> lettersBeans;
    private float left;
    private float top;
    private Bitmap[] bitmaps;
    private String[] luckyName;
    private String[] id;
    private int selectPos;


    public LuckyView(Context context) {
        this(context, null);
    }

    public LuckyView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public LuckyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); // 抗锯齿
        mPaint.setStyle(Paint.Style.FILL);
        // mPaint.setStyle(Paint.Style.STROKE); // 设置样式为描边
        mPaint.setStrokeWidth(mStrokeWidth); // 设置描边的宽度

        mRectFs = new ArrayList<>();
    }

    public void setLuckAnimationEndListener(OnLuckAnimationEndListener luckAnimationEndListener) {
        mLuckAnimationEndListener = luckAnimationEndListener;
    }

    public int getLuckNum() {
        return mLuckNum;
    }

    public void setLuckNum(int luckNum) {
        mLuckNum = luckNum;
    }

    public int[] getLuckyPrizes() {
        return mLuckyPrizes;
    }

    public void setLuckyPrizes(int[] luckyPrizes) {
        mLuckyPrizes = luckyPrizes;
    }

    //设置图片,文字数据
    public void setData(List<String> lettersBeans) {
        this.lettersBeans = lettersBeans;
        invalidate();
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        // 矩形的宽高
        mRectSize = (Math.min(w, h)) / 3;
        // 当控件大小改变的时候清空数据
        mRectFs.clear();
        initNineRect();
    }

    /**
     * 初始化 9 个矩形(正方形)的位置信息
     */
    private void initNineRect() {
        final float width = getWidth();

        // 加载前三个矩形
        for (int i = 0; i < 3; i  ) {
            float left = i * mRectSize   5;
            float right = (i   1) * mRectSize;
            float top = 5;
            float bottom = mRectSize;
            RectF rectF = new RectF(left, top, right, bottom);
            mRectFs.add(rectF);
        }

        // 加载第 4 个矩形
        mRectFs.add(new RectF(width - mRectSize   5, mRectSize   5, width, 2 * mRectSize));

        // 加载第 5~7 个矩形
        for (int j = 3; j > 0; j--) {
            float left = width - (4 - j) * mRectSize   5;
            float right = width - (3 - j) * mRectSize;
            float top = 2 * mRectSize   5;
            float bottom = 3 * mRectSize;
            RectF rectF = new RectF(left, top, right, bottom);
            mRectFs.add(rectF);
        }

        // 加载第 8 个矩形
        mRectFs.add(new RectF(5, mRectSize   5, mRectSize, 2 * mRectSize));

        // 加载中心第 9 个矩形
        mRectFs.add(new RectF(mRectSize   5, mRectSize   5, 2 * mRectSize, 2 * mRectSize));
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        //执行真正的绘制矩形操作
        drawNineRect(canvas);
        // 填充奖品图片
        drawNineBitmaps(canvas);
        // 填充奖品文字
        drawNineText(canvas);
    }

    /**
     * 在每个矩形中填充奖品图片
     * left:The position of the left side of the bitmap being drawn
     * top:The position of the top side of the bitmap being drawn
     */
    private void drawNineBitmaps(final Canvas canvas) {

        for (int i = 0; i < mRectFs.size(); i  ) {
            RectF rectF = mRectFs.get(i);
            // 将图片设置在每个矩形中央
            left = rectF.left   mRectSize / 4;
            top = rectF.top   mRectSize / 4;
            canvas.drawBitmap(Bitmap.createScaledBitmap(BitmapFactory.decodeResource(getResources(), mLuckyPrizes[i]), mRectSize / 2, mRectSize / 2, false), left, top, null);
        }
    }


    /**
     * 在每个矩形中央填充文字,代替抽奖图片
     * x:he x-coordinate of the origin of the text being drawn
     * y:The y-coordinate of the baseline of the text being drawn
     */
    private void drawNineText(Canvas canvas) {
        for (int i = 0; i < mRectFs.size(); i  ) {
            RectF rectF = mRectFs.get(i);
            float x = rectF.left   mRectSize / 4; // 将文字设置在每个矩形中央
            float y = rectF.top   mRectSize - 20;
            mPaint.setColor(Color.parseColor("#5e5448"));
            mPaint.setStyle(Paint.Style.FILL);
            mPaint.setTextSize(15); // unit px
            if (i == mRectFs.size() - 1) {
                canvas.drawText("", x, y, mPaint);
            } else {
                canvas.drawText(mPrizeDescription[i], x, y, mPaint);
            }
        }
    }

    /**
     * 执行真正的绘制矩形操作
     */
    private void drawNineRect(Canvas canvas) {
        for (int x = 0; x < mRectFs.size(); x  ) {
            RectF rectF = mRectFs.get(x);
            if (x == 8) {
                mPaint.setColor(Color.WHITE);
            } else {
                if (mCurrentPosition == x) {
                    mPaint.setColor(Color.parseColor("#edcea9"));
                } else {
                    mPaint.setColor(mItemColor[x % 2]); // 标记当前转盘经过的位置
                }
            }
            canvas.drawRect(rectF, mPaint);
        }
    }

    @SuppressLint("ClickableViewAccessibility")
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            mShouldStartFlag = mRectFs.get(8).contains(event.getX(), event.getY());
            return true;
        }
        if (event.getAction() == MotionEvent.ACTION_UP) {
            if (mShouldStartFlag) {
                if (mRectFs.get(8).contains(event.getX(), event.getY())) {
                    // mLuckAnimationEndListener.onClickLuck();
                    startAnim(); // 判断只有手指落下和抬起都在中间的矩形内时才开始执行动画抽奖
                }
                mShouldStartFlag = false;
            }
        }
        return super.onTouchEvent(event);
    }

    private void startAnim() {
        if (!mShouldStartNextTurn) {
            return;
        }
        Random random = new Random();
        setLuckNum(random.nextInt(8)); // 生成 [0,8) 的随机整数

        ValueAnimator animator = ValueAnimator.ofInt(mStartLuckPosition, mRepeatCount * 8   mLuckNum)
                .setDuration(5000);

        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                final int position = (int) animation.getAnimatedValue();
                setCurrentPosition(position % 8);
                mShouldStartNextTurn = false;
            }
        });

        animator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                mShouldStartNextTurn = true;
                mStartLuckPosition = mLuckNum;
                //最终选中的位置
                if (mLuckAnimationEndListener != null) {
                    mLuckAnimationEndListener.onLuckAnimationEnd(mCurrentPosition,
                            mPrizeDescription[mCurrentPosition]);
                }
            }
        });

        animator.start();
    }

    private void setCurrentPosition(int position) {
        mCurrentPosition = position;
        invalidate(); // 强制刷新,在 UI 线程回调 onDraw()
    }

    public void setBitmap(Bitmap[] bitmaps1, String[] name, String[] strings) {
        bitmaps = bitmaps1;
        luckyName = name;
        id = strings;
        invalidate();
    }

    /**
     * 选中id
     *
     * @param datas
     */
    public void setSelectId(int datas) {
        String selectId = datas   "";

        if (id != null && id.length != 0) {
            for (int i = 0; i < id.length; i  ) {
                if (id[i].equals(selectId)) {
                    selectPos = i;
                }
            }
        }

        startAnim();
    }

    /**
     * 用于抽奖结果回调
     */
    public interface OnLuckAnimationEndListener {
        void onLuckAnimationEnd(int pos, String msg);
    }
}

3.主界面布局文件

代码语言:txt复制
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="#fff"
    tools:context=".MainActivity">

    <com.showly.luckyactivity.view.LuckyView
        android:id="@ id/lucky_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginTop="50dp"/>

</RelativeLayout>

4.主程序

这里比较简单,就是输出抽奖结果

代码语言:txt复制
public class MainActivity extends AppCompatActivity {

    private LuckyView luckyView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initView();
        initData();
        initListener();
    }

    private void initView() {
        luckyView = findViewById(R.id.lucky_view);
    }

    private void initData() {

    }

    private void initListener() {
        luckyView.setLuckAnimationEndListener(new LuckyView.OnLuckAnimationEndListener() {
            @Override
            public void onLuckAnimationEnd(int pos, String msg) {
                //打印抽奖结果
                Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_SHORT).show();
            }
        });
    }
}

5,总结

实现九宫格抽奖重点在自定义View的处理,代码中有文字解析,这里就不重复说明了,还有这里抽奖展示的图片及文字是固定的,如果需要动态设置图片及文字数据的话,可以自己更改自定义控件中的逻辑。

需要Demo源码的童鞋可以在底部的公众号回复:"九宫格抽奖"即可获取。


小编整理了一份Android电子书籍,需要的童鞋关注公众号回复:"e_books" 即可获取哦!

在这里插入图片描述在这里插入图片描述

欢迎关注公众号(longxuanzhigu),获得更多福利、精彩内容哦!

在这里插入图片描述在这里插入图片描述

0 人点赞