Android自定义View-九宫格抽奖转盘(两种实现)

好久没写文章了,一来感觉自己技术没啥进步,二来各种杂事繁忙,以至于拖了许久。正好这个版本产品需求需要做一个九宫格样式的转盘抽奖机,感觉是个挺有意思的东西,把我的解决方案和中间遇到的问题发出来,供大家参考哈~

两种方案,先看成品的效果
在这里插入图片描述

开始做这个功能的时候,跟产品确定效果,要求动画是先慢后快再变慢,我第一时间想到的就是插值器。AccelerateDecelerateInterpolator就是属于开始和结束很慢,中间速度较快的那种插值器,完美符合需求。接下来需要考虑怎么来实现九宫格。

第一种实现

我第一版的实现是使用ConstraintLayout,根据约束条件来进行九宫格的布局,这样的缺点是布局文件难看,过多的include,实现起来倒是不怎么复杂。先来看下方案的代码

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >

        <include
            android:id="@+id/include_lottery0"
            layout="@layout/layout_lottery_item"
            android:layout_width="0dp"
            android:layout_height="0dp"
            app:layout_constraintDimensionRatio="1"
            app:layout_constraintWidth_percent="0.33"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <include
            android:id="@+id/include_lottery1"
            layout="@layout/layout_lottery_item"
            android:layout_width="0dp"
            android:layout_height="0dp"
            app:layout_constraintDimensionRatio="1"
            app:layout_constraintWidth_percent="0.33"
            app:layout_constraintLeft_toRightOf="@+id/include_lottery0"
            app:layout_constraintRight_toLeftOf="@+id/include_lottery2"
            app:layout_constraintTop_toTopOf="parent" />

        <include
            android:id="@+id/include_lottery2"
            layout="@layout/layout_lottery_item"
            android:layout_width="0dp"
            android:layout_height="0dp"
            app:layout_constraintDimensionRatio="1"
            app:layout_constraintWidth_percent="0.33"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <include
            android:id="@+id/include_lottery7"
            layout="@layout/layout_lottery_item"
            android:layout_width="0dp"
            android:layout_height="0dp"
            app:layout_constraintDimensionRatio="1"
            app:layout_constraintWidth_percent="0.33"
            app:layout_constraintBottom_toTopOf="@+id/include_lottery5"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/include_lottery1" />

        <include
            android:id="@+id/include_button"
            layout="@layout/layout_lottery_item"
            android:layout_width="0dp"
            android:layout_height="0dp"
            app:layout_constraintDimensionRatio="1"
            app:layout_constraintWidth_percent="0.33"
            app:layout_constraintBottom_toTopOf="@+id/include_lottery5"
            app:layout_constraintLeft_toRightOf="@+id/include_lottery7"
            app:layout_constraintRight_toLeftOf="@+id/include_lottery3"
            app:layout_constraintTop_toBottomOf="@+id/include_lottery1" />

        <include
            android:id="@+id/include_lottery3"
            layout="@layout/layout_lottery_item"
            android:layout_width="0dp"
            android:layout_height="0dp"
            app:layout_constraintDimensionRatio="1"
            app:layout_constraintWidth_percent="0.33"
            app:layout_constraintBottom_toTopOf="@+id/include_lottery5"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/include_lottery1" />

        <include
            android:id="@+id/include_lottery6"
            layout="@layout/layout_lottery_item"
            android:layout_width="0dp"
            android:layout_height="0dp"
            app:layout_constraintDimensionRatio="1"
            app:layout_constraintWidth_percent="0.33"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent" />

        <include
            android:id="@+id/include_lottery5"
            layout="@layout/layout_lottery_item"
            android:layout_width="0dp"
            android:layout_height="0dp"
            app:layout_constraintDimensionRatio="1"
            app:layout_constraintWidth_percent="0.33"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toRightOf="@+id/include_lottery6"
            app:layout_constraintRight_toLeftOf="@+id/include_lottery4" />

        <include
            android:id="@+id/include_lottery4"
            layout="@layout/layout_lottery_item"
            android:layout_width="0dp"
            android:layout_height="0dp"
            app:layout_constraintDimensionRatio="1"
            app:layout_constraintWidth_percent="0.33"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintRight_toRightOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

罗列了9个include,并对每个布局进行了单独的约束,实现的样式如下:

在这里插入图片描述

接下来,自定义一个View,继承ConstraintLayout,在里面进行逻辑代码的编写,核心点有两个,第一个需要正确获取到肉眼所见的0~8这几个按钮的位置,这里我直接使用数组来标记这8个View,按照顺序排列好

init {
    inflate(context, R.layout.layout_lucky_draw, this)
    lotteryArray = arrayOf(
        findViewById(R.id.include_lottery0),
        findViewById(R.id.include_lottery1),
        findViewById(R.id.include_lottery2),
        findViewById(R.id.include_lottery3),
        findViewById(R.id.include_lottery4),
        findViewById(R.id.include_lottery5),
        findViewById(R.id.include_lottery6),
        findViewById(R.id.include_lottery7)
    )
    ...
}

到这一步,布局的问题就已经解决了,下面要解决动画执行和确定抽奖位置的问题。动画执行很容易可以想到使用属性动画,因为是循环转动,我们可以设置一个从0到指定位置x的范围,其中x的值对8取余就是我们事先确定的中奖的位置,代码如下:

init {
    animator.duration = 2000
    animator.interpolator = AccelerateDecelerateInterpolator()
    animator.addUpdateListener {
        val position = it.animatedValue as Int
        setCurrentPosition(position % 8)
    }
    animator.addListener(object : AnimatorListenerAdapter() {
        override fun onAnimationEnd(animation: Animator?) {
            super.onAnimationEnd(animation)
            setCurrentPosition(luckyIndex)
            lotteryStatus = 2
        }
    })
    animator.setIntValues(0, 2 * 8 + luckyIndex)
}

这里可以看到动画执行了两圈之后停止在指定的luckyIndex这个位置,其中setCurrentposition用来对指定位置进行刷新,我这里偷了个懒,没有记录之前的位置,整体刷新了,后续可以优化下

第二种实现

后面的实现是感觉上一种方法布局写的太累,于是想到了使用RecyclerView来进行布局,算是对版本1的一个扩展。RecyclerView实现很简单,设置GridLayoutManager并设置spanCount为3即可,代码就不再贴了,有兴趣可以去看代码。这种方案跟版本1大同小异,唯一需要注意的问题在于刷新的时候因为我们肉眼看到的顺序其实对应的不是RecyclerView中item的位置,所以如果刷新不是使用notifyDataSetChanged的话,需要注意这里的刷新位置,简略看下代码实现

var posMap =
mapOf<Int, Int>(0 to 0, 1 to 1, 2 to 2, 3 to 7, 4 to 8, 5 to 3, 6 to 6, 7 to 5, 8 to 4)
fun setSelectionPosition(selectPos: Int) {
    val lastPos = selectPosition
    selectPosition = selectPos
    if (lastPos != -1) {
        notifyItemChanged(reversePosition(lastPos))
    } else {
        notifyDataSetChanged()
    }
    notifyItemChanged(reversePosition(selectPos))
}

/**
* 获取真实坐标
*/
private fun reversePosition(selectPos: Int): Int {
    for ((key, value) in posMap.entries) {
        if (value == selectPos) {
            return key
        }
    }
    return -1
}

其他的实现就跟上面的大同小异了

ok,这就是一个简单的九宫格抽奖转盘的实现了,算是水了一篇,需要源码的请移步代码地址

  • 2
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是一个简单的九宫格抽奖活动的 Android 代码示例: 1. 首先,在布局文件中定义一个九宫格的 GridLayout,代码如下: ```xml <GridLayout android:id="@+id/grid_layout" android:layout_width="match_parent" android:layout_height="match_parent" android:columnCount="3" android:rowCount="3" /> ``` 2. 在代码中获取 GridLayout,并为每个子添加一个 ImageView,代码如下: ```java private GridLayout gridLayout; private void initGridLayout() { gridLayout = findViewById(R.id.grid_layout); for (int i = 0; i < 9; i++) { ImageView imageView = new ImageView(this); imageView.setImageResource(R.drawable.lottery); GridLayout.LayoutParams layoutParams = new GridLayout.LayoutParams(); layoutParams.width = 0; layoutParams.height = GridLayout.LayoutParams.WRAP_CONTENT; layoutParams.columnSpec = GridLayout.spec(i % 3, 1f); layoutParams.rowSpec = GridLayout.spec(i / 3, 1f); gridLayout.addView(imageView, layoutParams); } } ``` 这里的 R.drawable.lottery 是抽奖子的默认图片,可以根据实际需求进行更改。 3. 给每个子添加点击事件,实现抽奖功能,代码如下: ```java private void initGridLayout() { // ... for (int i = 0; i < 9; i++) { ImageView imageView = new ImageView(this); imageView.setImageResource(R.drawable.lottery); imageView.setTag(i); imageView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { int position = (int) v.getTag(); // 模拟随机抽奖结果 boolean result = new Random().nextBoolean(); if (result) { // 中奖 Toast.makeText(MainActivity.this, "恭喜中奖!", Toast.LENGTH_SHORT).show(); ((ImageView) v).setImageResource(R.drawable.prize); } else { // 未中奖 Toast.makeText(MainActivity.this, "很遗憾未中奖!", Toast.LENGTH_SHORT).show(); ((ImageView) v).setImageResource(R.drawable.empty); } // 修改该子为不可点击 v.setClickable(false); } }); // ... } } ``` 这里的 R.drawable.prize 和 R.drawable.empty 分别是中奖和未中奖的图片,可以根据实际需求进行更改。 以上就是一个简单的九宫格抽奖活动的 Android 代码示例,可以根据实际需求进行修改和完善。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值