当前位置: 首页 > news >正文

自定义View学习记录之 滚动抽奖单片

老虎机演示

优点

该View是一个抽奖的单片,支持滚动图片和文本 ,可自定义文本颜色大小等信息,多个单片间可自由组合启动顺序


1.具体View代码

package com.example.test.weightimport android.animation.ValueAnimator
import android.content.Context
import android.graphics.Color
import android.util.AttributeSet
import android.view.Gravity
import android.view.View
import android.view.animation.AccelerateDecelerateInterpolator
import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.TextView
import androidx.core.animation.doOnEnd
import androidx.core.graphics.toColorInt
import com.example.test.Rclass OneArmBandit @JvmOverloads constructor(context: Context,attrs: AttributeSet? = null,defStyleAttr: Int = 0
) : FrameLayout(context, attrs, defStyleAttr) {private var verticalSpacing = 20.dpToPx() // 选项间的垂直间距private var itemHeight = 0  //滚动选项的高度private var itemType = RollType.TEXT  //滚动选项类型private var maxY = 0 //滚动选项 底部边界private var singleRollDistance = 0 //单次完整滚动的距离private var minY = 0 //滚动选项 顶部边界private var rollCount = 4 //滚动期间滚动的圈数private var centerTop = 0 //滚动选项的居中时顶部坐标private val rollItem = mutableListOf<RollItem>() //滚动选项列表private val imageResourceList =   //滚动选项的图片资源mutableListOf(R.mipmap.item_0,R.mipmap.item_1,R.mipmap.item_2)//滚动选项的文本相关private val textList = mutableListOf<String>("00", "01", "02", "03", "04", "05")private var textSize = 16private var textColor = "#000000".toColorInt()private var rollValueAnimator: ValueAnimator? = null //滚动动画private var doOnEndCallBack: (resultIndex: Int) -> Unit = {}  //滚动动画结束回调init {addView()}fun addView() {// 清除现有子 View 和 rollItem 列表removeAllViews() //移除所有旧的子 ViewrollItem.clear()if (itemType == RollType.IMAGE) {for (image in imageResourceList) {val imageView = ImageView(context).apply {adjustViewBounds = truescaleType = ImageView.ScaleType.FIT_XYsetImageResource(image)layoutParams = LayoutParams(LayoutParams.MATCH_PARENT,LayoutParams.WRAP_CONTENT)}rollItem.add(RollItem(imageView))addView(imageView)}} else {for (mText in textList) {val textView = TextView(context).apply {textSize = this@OneArmBandit.textSize.toFloat()setTextColor(this@OneArmBandit.textColor)text = mTextlayoutParams = LayoutParams(LayoutParams.MATCH_PARENT,LayoutParams.WRAP_CONTENT)gravity = Gravity.CENTERincludeFontPadding = falsesetBackgroundColor(Color.GRAY)}rollItem.add(RollItem(textView))addView(textView)}}post {itemHeight = rollItem[0].view.measuredHeightrequestLayout() // 重新布局}}override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {super.onLayout(changed, left, top, right, bottom)centerTop = (height - itemHeight) / 2for (i in rollItem.indices) {rollItem[i].apply {x = (width - view.measuredWidth) / 2y = centerTop - i * (itemHeight + verticalSpacing)startY = yview.layout(x, y, x + view.measuredWidth, y + view.measuredHeight)}}singleRollDistance = (itemHeight + verticalSpacing) * rollItem.sizemaxY = centerTop + rollItem.size / 2 * (itemHeight + verticalSpacing)minY = maxY - singleRollDistance}fun startRoll() {// 停止当前正在进行的动画rollValueAnimator?.cancel()// 随机选择一个结果项val resultIndex = rollItem.indices.random()val resultItem = rollItem[resultIndex]// 计算需要滚动的总距离val totalDistance = if (resultItem.y >= centerTop) {// 如果结果项在中心线下方,需要多滚几圈rollCount * singleRollDistance + (resultItem.y - centerTop)} else {// 如果结果项在中心线上方,少滚一点距离(rollCount + 1) * singleRollDistance - (centerTop - resultItem.y)}// 创建值动画器rollValueAnimator = ValueAnimator.ofInt(0, totalDistance).apply {duration = 3000 // 3秒动画时间interpolator = AccelerateDecelerateInterpolator()addUpdateListener { animation ->val distance = animation.animatedValue as Intfor (item in rollItem) {item.y = if (item.rollNum == 0) {item.startY + distance} else {minY + distance - (item.rollNum - 1) * singleRollDistance - (item.startY - maxY)}// 处理边界条件if (item.y > maxY) {item.rollNum++}// 应用新位置item.view.y = item.y.toFloat()}invalidate()}doOnEnd {post {for (item in rollItem) {item.rollNum = 0item.startY = item.y}doOnEndCallBack.invoke(resultIndex)}}}// 开始动画rollValueAnimator?.start()}override fun onDetachedFromWindow() {super.onDetachedFromWindow()rollValueAnimator?.let {it.cancel()  // 停止动画it.removeAllUpdateListeners()  // 移除所有监听器it.removeAllListeners()  // 移除所有动画监听rollValueAnimator = null  // 置空引用}doOnEndCallBack = { _ -> }rollItem.clear()imageResourceList.clear()textList.clear()}/*** 添加结束回调* @param callBack 结束回调*/fun addDoOnEndCallBack(callBack: (resultIndex: Int) -> Unit) {doOnEndCallBack = callBack}/*** 设置滚动选项类型 默认为文本* @param rollType 滚动选项类型*/fun setItemType(rollType: RollType) {itemType = rollTypeaddView()}/*** 设置滚动文本选项* @return 滚动选项列表*/fun setText(list: List<String>, color: Int = Color.BLACK, size: Int = 16) {textList.clear()textList.addAll(list)textColor = colortextSize = sizeaddView()}/*** 获取滚动选项列表* @return 滚动选项列表*/fun setImageResourceList(list: List<Int>) {imageResourceList.clear()imageResourceList.addAll(list)if (itemType == RollType.IMAGE) addView()}/*** 滚动选项类* @param view 滚动选项视图* @param x 滚动选项的 X 坐标* @param y 滚动选项当前 Y 坐标* @param startY 滚动选项的起始 Y坐标* @param rollNum 滚动选项当前的滚动圈数*/data class RollItem(val view: View,var x: Int = 0,var y: Int = 0,var startY: Int = 0,var rollNum: Int = 0)//滚动选项类型enum class RollType {TEXT, //文本IMAGE   // 图片}private fun Int.dpToPx(): Int = (this * resources.displayMetrics.density).toInt()
}

2.使用

<?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"xmlns:tools="http://schemas.android.com/tools"android:id="@+id/main"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity"><LinearLayoutandroid:id="@+id/linearLayout"android:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal"android:layout_marginHorizontal="20dp"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintTop_toTopOf="parent"><com.example.test.weight.OneArmBanditandroid:id="@+id/oneArmBandit1"android:layout_width="0dp"android:layout_height="150dp"android:layout_weight="1"android:background="@color/white" /><com.example.test.weight.OneArmBanditandroid:id="@+id/oneArmBandit2"android:layout_width="0dp"android:layout_height="150dp"android:layout_weight="1"android:background="@color/white"android:layout_marginHorizontal="10dp"/><com.example.test.weight.OneArmBanditandroid:id="@+id/oneArmBandit3"android:layout_width="0dp"android:layout_height="150dp"android:layout_weight="1"android:background="@color/white" /></LinearLayout><TextViewandroid:id="@+id/startBt"android:layout_width="80dp"android:layout_height="wrap_content"android:text="开始"android:gravity="center"android:textColor="@color/black"android:textSize="20sp"android:layout_marginTop="20dp"android:background="@color/white"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toBottomOf="@+id/linearLayout" /></androidx.constraintlayout.widget.ConstraintLayout>
package com.example.testimport android.graphics.Color
import android.os.Bundle
import android.widget.Button
import android.widget.Toast
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import com.example.test.databinding.ActivityMainBinding
import com.example.test.weight.OneArmBanditclass MainActivity : AppCompatActivity() {val binding by lazy { ActivityMainBinding.inflate(layoutInflater) }override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)enableEdgeToEdge()setContentView(binding.root)binding.oneArmBandit1.apply {setItemType(OneArmBandit.RollType.TEXT)setText(listOf("番茄","苹果","橘子","香蕉","李子") , Color.BLACK , 20)addDoOnEndCallBack {postDelayed({addView() //结束一秒后,恢复默认状态},1000)}}binding.oneArmBandit2.apply {val list = listOf(R.mipmap.item_0 , R.mipmap.item_1,R.mipmap.item_2,R.mipmap.item_0 ,R.mipmap.item_1,R.mipmap.item_2)setItemType(OneArmBandit.RollType.IMAGE)setImageResourceList(list)addDoOnEndCallBack {binding.oneArmBandit3.startRoll()}}binding.oneArmBandit3.apply {val list = listOf("X0倍","X1倍","X2倍","X3倍","X4倍","X5倍")setItemType(OneArmBandit.RollType.TEXT)setText(list , Color.RED,20)addDoOnEndCallBack {Toast.makeText(context,list[it] , Toast.LENGTH_SHORT).show()}}binding.startBt.setOnClickListener {binding.oneArmBandit1.startRoll()binding.oneArmBandit2.startRoll()}//当前连续开始的话,会基于当前位置滚动,而不是重新从索引0开始排列后滚动,想做到重新开始滚动的话,重新调用一遍addView()就行}
}

http://www.dtcms.com/a/332457.html

相关文章:

  • 前端性能优化工具Performance面板实战指南
  • 为什么 /deep/ 现在不推荐使用?
  • Webpack详解
  • HTML 常用标签介绍
  • 经典回顾:Hive执行原理、MapReduce执行流程、Spark执行流程
  • html抽奖功能
  • Apache 如何支持SHTML(SSI)的配置方法
  • 更换cmd背景图片
  • C++ 优选算法 力扣 1004. 最大连续1的个数 II 滑动窗口 (同向双指针)优化 每日一题 详细题解
  • 【Java Web 快速入门】十、AOP
  • 活到老学到老之Jenkins Pipeline Job
  • spring-ai-alibaba 学习(二十五)——graph之内置节点
  • Linux815 shell:while
  • Spring Boot接口签名校验设计与实现
  • 设计模式(Design Patterns)
  • WEB安全--Java安全--Servlet内存马
  • DzzOffice 开发手册之系统的配置
  • 短剧小程序系统开发:打造个性化娱乐新体验
  • 【反序列化基本介绍】
  • 25.Linux 聚合链路与软件网桥
  • 【中微半导体】BAT32G139GK48FA 定时器B输入捕获测速(寄存器TBSR/TBIER/TB/TBGRA/TBGRC)
  • Struts文件泄露漏洞分析与修复方案
  • 企业级Spring事务管理:从单体应用到微服务分布式事务完整方案
  • Baumer高防护相机如何通过YoloV8深度学习模型实现驾驶员疲劳的检测识别(C#代码UI界面版)
  • MySQL 主键详解:作用与使用方法
  • 搭建前端开发环境 安装nvm nodejs pnpm 配置环境变量
  • MySQL、PolarDB、PolarDB-X、TableStore、MongoDB、TiDB、ClickHouse选型
  • 融合开源AI智能名片与链动2+1模式的微商新零售转型研究——基于S2B2C商城小程序的实践探索
  • 戴永红×数图:重构零售空间价值,让陈列创造效益!
  • HTML5新增属性