Android开发:抽奖转盘的实现

3,465 阅读4分钟

故事的开始

最近有个需求,支付成功的时候加个抽奖轮盘。类似问卷星提交后的那种东西,翻了一下gayhub,下面给出自己的实现思路.

写在题前

这东西是在github上一个项目是拓展的。但是实现时间和下载项目时间隔了有一阵。所以找不到原链没法挂上,还请见谅。若有知道原链的可以在评论区留一下,我后续加上。

效果图

照例先给最后的实现效果gif图(转gif图的时候应该是抽帧了。实际效果比图片好一点) 轮盘

实现思路

1,绘制可以切换背景的奖品View 2,将其排列成九宫格 3,通过前后两个view的变换,营造出旋转的感觉 4,状态标识控制加/减速,直至停止,并发送回调

代码

  1. PrizeItemView(奖品View)
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="@drawable/shape_white">

    <View
        android:id="@+id/overlay"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@drawable/shape_pink"
        android:visibility="invisible" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:orientation="vertical">

        <ImageView
            android:id="@+id/iv_prize"
            android:layout_width="60dp"
            android:layout_height="45dp"
            android:src="@drawable/png_prize" />

        <TextView
            android:id="@+id/tv_prize_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="9dp"
            android:text="一个奖品"
            android:textColor="#98520E"
            android:textSize="22sp" />
    </LinearLayout>
</FrameLayout>

/**
 * Created by huahen on 2021/10/25
 * ClassDescription : 抽奖转盘-奖品子view
 */
public class PrizeItemView extends FrameLayout implements PrizeItemApi {

    private Context mContext;
    private View mOverlay;
    private ImageView ivPrize;
    private TextView tvPrize;

    public PrizeItemView(@NonNull Context context) {
        this(context, null);
    }


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

    public PrizeItemView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        inflate(context, R.layout.item_prize, this);
        mOverlay = findViewById(R.id.overlay);
        ivPrize = findViewById(R.id.iv_prize);
        tvPrize = findViewById(R.id.tv_prize_name);
        mContext = context;
    }


    @Override
    public void setFocus(boolean isFocused) {
        if (mOverlay != null) {
            mOverlay.setVisibility(isFocused ? INVISIBLE : VISIBLE);
        }
    }

    @Override
    public void setUi(PrizeListBean bean) {
        ivPrize.setImageResource(bean.getPrize_path());
        tvPrize.setText(bean.getGoodName());

    }
}

/**
 * Created by huahen on 2021/10/25
 * ClassDescription :
 */
public interface PrizeItemApi {

    void setFocus(boolean isFocused);//修改当前显示状态

    void setUi(PrizeListBean bean);
}

这里写了一个自定义View,实现两个方法。
1,更新奖品数据修改UI
2,更新奖品选中背景

2. LuckDrawView(转盘View) 首先,布局是由8个PrizeItemView拼出来的,哈哈哈哈,中间加个iv,贴了张网上找的开始抽奖

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_marginBottom="4dp"
        android:layout_weight="1"
        android:orientation="horizontal">

        <com.huahen.luckdraw.luckDrawView.PrizeItemView
            android:id="@+id/item1"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_marginRight="8dp"
            android:layout_weight="1" />

        <com.huahen.luckdraw.luckDrawView.PrizeItemView
            android:id="@+id/item2"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_marginRight="8dp"
            android:layout_weight="1" />

        <com.huahen.luckdraw.luckDrawView.PrizeItemView
            android:id="@+id/item3"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_marginRight="8dp"
            android:layout_weight="1" />
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_marginBottom="8dp"
        android:layout_weight="1"
        android:orientation="horizontal">

        <com.huahen.luckdraw.luckDrawView.PrizeItemView
            android:id="@+id/item8"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_marginRight="8dp"
            android:layout_weight="1" />

        <ImageView
            android:id="@+id/iv_start"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_marginRight="8dp"
            android:layout_weight="1"
            android:background="@drawable/luck_draw_btn" />

        <com.huahen.luckdraw.luckDrawView.PrizeItemView
            android:id="@+id/item4"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_marginRight="8dp"
            android:layout_weight="1" />
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_marginBottom="8dp"
        android:layout_weight="1"
        android:orientation="horizontal">

        <com.huahen.luckdraw.luckDrawView.PrizeItemView
            android:id="@+id/item7"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_marginRight="8dp"
            android:layout_weight="1" />

        <com.huahen.luckdraw.luckDrawView.PrizeItemView
            android:id="@+id/item6"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_marginRight="8dp"
            android:layout_weight="1" />

        <com.huahen.luckdraw.luckDrawView.PrizeItemView
            android:id="@+id/item5"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_marginRight="8dp"
            android:layout_weight="1" />
    </LinearLayout>

</LinearLayout>
显示是这样的

在这里插入图片描述

来了。全文最主要的代码
public class LuckDrawView extends FrameLayout {
    private PrizeItemView itemView1, itemView2, itemView3,
            itemView8, itemView4,
            itemView7, itemView6, itemView5;
    private ImageView ivStart;
    private OnItemListener listener;

    private PrizeItemApi[] itemViewArr = new PrizeItemApi[8];

    private int currentIndex = 0;
    private int currentTotal = 0;
    private int stayIndex = 0;

    private boolean isMarqueeRunning = false;//
    private boolean isGameRunning = false; //是否处于转动
    private boolean isTryToStop = false;//是否增速/减速

    private static final int DEFAULT_SPEED = 150; //默认/最慢速度
    private static final int MIN_SPEED = 50;//最快速度
    private int currentSpeed = DEFAULT_SPEED;//当前速度

    Timer timer = new Timer();


    public LuckDrawView(@NonNull Context context) {
        this(context, null);
    }

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

    public LuckDrawView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        inflate(context, R.layout.view_luck_draw_nine, this);
        initView();
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        startMarquee();
    }

    @Override
    protected void onDetachedFromWindow() {
        stopMarquee();
        super.onDetachedFromWindow();
    }


    private void initView() {
        itemView1 = (PrizeItemView) findViewById(R.id.item1);
        itemView2 = (PrizeItemView) findViewById(R.id.item2);
        itemView3 = (PrizeItemView) findViewById(R.id.item3);
        itemView4 = (PrizeItemView) findViewById(R.id.item4);
        itemView5 = (PrizeItemView) findViewById(R.id.item5);
        itemView6 = (PrizeItemView) findViewById(R.id.item6);
        itemView7 = (PrizeItemView) findViewById(R.id.item7);
        itemView8 = (PrizeItemView) findViewById(R.id.item8);

        itemViewArr[0] = itemView1;
        itemViewArr[1] = itemView2;
        itemViewArr[2] = itemView3;
        itemViewArr[3] = itemView4;
        itemViewArr[4] = itemView5;
        itemViewArr[5] = itemView6;
        itemViewArr[6] = itemView7;
        itemViewArr[7] = itemView8;


        ivStart = findViewById(R.id.iv_start);
        ivStart.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                listener.onClick();
            }
        });
    }

    private void stopMarquee() {
        isMarqueeRunning = false;
        isGameRunning = false;
        isTryToStop = false;
    }


    private void startMarquee() {
        isMarqueeRunning = true;
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (isMarqueeRunning) {
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
//                    //灯带
//                    post(new Runnable() {
//                        @Override
//                        public void run() {
//                            if (bg_1 != null && bg_2 != null) {
//                                if (VISIBLE == bg_1.getVisibility()) {
//                                    bg_1.setVisibility(GONE);
//                                    bg_2.setVisibility(VISIBLE);
//                                } else {
//                                    bg_1.setVisibility(VISIBLE);
//                                    bg_2.setVisibility(GONE);
//                                }
//                            }
//                        }
//                    });
                }
            }
        }).start();
    }

    //获取sleep时间,即转盘运动速度,越小即越快。
    private long getInterruptTime() {
        currentTotal++;
        if (isTryToStop) {//减速直至恢复为默认速度
            currentSpeed += 10;
            if (currentSpeed > DEFAULT_SPEED) {
                currentSpeed = DEFAULT_SPEED;
            }
        } else {//增速直至最快速度
            //第一圈不做加速
            if (currentTotal / itemViewArr.length > 0) {
                currentSpeed -= 10;
            }
            if (currentSpeed < MIN_SPEED) {
                currentSpeed = MIN_SPEED;
            }
        }
        return currentSpeed;
    }

    //返回当前状态
    public boolean isGameRunning() {
        return isGameRunning;
    }

    //开始抽奖
    public void startGame(final int stayIndex) {
        if (isGameRunning) {//运动中
            return;
        }
        isGameRunning = true;
        isTryToStop = false;
        currentSpeed = DEFAULT_SPEED;
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (isGameRunning) {
                    try {
                        Thread.sleep(getInterruptTime());
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    post(new Runnable() {
                        @Override
                        public void run() {
                            //防止线程多走了一次
                            if (!isGameRunning) {
                                return;
                            }
                            int preIndex = currentIndex;
                            currentIndex++;
                            if (currentIndex >= itemViewArr.length) {
                                currentIndex = 0;
                            }

                            //根据前后两个itemView的视图变换,实现转动效果
                            itemViewArr[preIndex].setFocus(true);
                            itemViewArr[currentIndex].setFocus(false);

                            //减速&&速度达到最小&&当前view为指定view——停止
                            if (isTryToStop && currentSpeed == DEFAULT_SPEED && stayIndex == currentIndex) {
                                isGameRunning = false;
                                listener.onShop(currentIndex);
                            }
                        }
                    });
                }
            }
        }).start();

        //五秒后开始减速
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                tryToStop();
            }
        }, 5000);
    }


    //改变加速状态
    public void tryToStop() {
        isTryToStop = true;
    }


    public void setData(List<PrizeListBean> beans) {
        if (beans != null) {
            for (int i = 0; i < beans.size(); i++) {
                itemViewArr[i].setUi(beans.get(i));
            }
        }
    }


    public void setOnItemListener(OnItemListener listener) {
        this.listener = listener;
    }

    //放个接口出来做事件监听
    public interface OnItemListener {
        void onClick();//点击开始

        void onShop(int Index);//转动停止
    }
}

这么清晰的注释,应该不用我怎么解释了吧

最后是调用

        mLuckDrawView = findViewById(R.id.luck_draw);
        mLuckDrawView.setOnItemListener(new LuckDrawView.OnItemListener() {
            @Override
            public void onClick() {//开始抽奖
                int stayIndex = new Random().nextInt(8);
                mLuckDrawView.startGame(stayIndex);
            }

            @Override
            public void onShop(int Index) {//动画停止
                Toast.makeText(MainActivity.this, "这是第" + Index + "个商品", Toast.LENGTH_SHORT).show();
            }
        });
        mLuckDrawView.setData(getLuckDrawData());//设置奖品数据

就这样,一个抽奖转盘就这么实现

可能不尽完美,但也是我提供的一种思路吧。。也欢迎大家一起讨论。

笔者写到这里也不容易,如果这篇文章有帮助到你。冒昧地请求兄弟点个赞呗,如果有不同的见解。也欢迎在评论区一起探讨