SET卡牌游戏开发:页面显示12张卡牌及崩溃问题求助
修复SET卡牌应用崩溃及功能问题
一、启动即崩溃的直接原因
1. 类型不匹配错误(MainActivity.kt)
SetCardView的color属性是自定义的Colors枚举类型,原代码传入了Android系统的Color.RED,类型完全不兼容;number属性是Number枚举,却传入了整数1,同样类型不匹配。这两个错误会触发ClassCastException,导致应用启动直接崩溃。
修复后的MainActivity代码:
import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.widget.GridLayout class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val cardContainer = findViewById<GridLayout>(R.id.card_container) val cardManager = SetCardView(this, cardContainer) cardManager.setRandomCards() } }
二、setRandomCards方法的致命问题
1. 卡牌特征不完整导致无限循环
原代码仅存储Number和Shape的组合,而SET卡牌包含4种特征(number/shape/shading/color),总共有81种组合,但Number+Shape仅9种组合。循环尝试生成12个不重复的Pair时,永远无法满足条件,会进入无限循环,最终引发ANR或崩溃。
修复方案:创建完整卡牌数据类,从81种组合中随机选取12个不重复的卡牌:
// 在SetCardView类内添加完整卡牌数据类 data class Card( val number: Number, val shape: Shape, val shading: Shading, val color: Colors ) // 修正setRandomCards方法 fun setRandomCards() { val random = Random() // 生成所有81种卡牌组合 val allCards = mutableListOf<Card>() Number.values().forEach { number -> Shape.values().forEach { shape -> Shading.values().forEach { shading -> Colors.values().forEach { color -> allCards.add(Card(number, shape, shading, color)) } } } } // 随机打乱并取前12个 allCards.shuffle() val selectedCards = allCards.take(12) // 清空容器避免重复添加 parentLayout.removeAllViews() // 将卡牌添加到容器 selectedCards.forEach { card -> val cardView = SetCardView(context, parentLayout) cardView.number = card.number cardView.shape = card.shape cardView.color = card.color cardView.shading = card.shading cardView.layoutParams = ViewGroup.LayoutParams( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT ) parentLayout.addView(cardView) } }
2. 错误复用当前实例属性
原代码中cardView.shading = shading复用了当前实例的shading属性,导致所有新卡牌的shading固定不变,不符合随机需求。修复后使用卡牌数据类中的随机shading值。
三、绘制逻辑问题修复
1. 重构onDraw方法,明确单卡牌职责
原onDraw方法仅计算属性未绘制内容,且SetCardView试图在内部绘制多张卡牌,职责混乱。修改后让SetCardView仅负责绘制单张卡牌:
override fun onDraw(canvas: Canvas) { super.onDraw(canvas) // 绘制卡牌边框与背景 val rect = RectF(0f, 0f, width.toFloat(), height.toFloat()) val path = Path() path.addRoundRect(rect, cornerRadius, cornerRadius, Path.Direction.CW) mPaint.style = Paint.Style.FILL mPaint.color = Color.WHITE canvas.drawPath(path, mPaint) mPaint.style = Paint.Style.STROKE mPaint.strokeWidth = 3.0f mPaint.color = Color.BLACK canvas.drawPath(path, mPaint) // 绘制当前卡牌的特征符号 drawCardSymbols(canvas) } // 新增绘制卡牌符号的方法 private fun drawCardSymbols(canvas: Canvas) { val symbolCount = number.ordinal + 1 val symbolHeight = height * SYMBOL_HEIGHT_SCALE_FACTOR val totalSymbolHeight = symbolCount * symbolHeight val startY = (height - totalSymbolHeight) / 2f for (i in 0 until symbolCount) { val voffset = startY + i * symbolHeight + symbolHeight / 2f - height / 2f drawShapeWithVerticalOffset(canvas, voffset) } }
2. 修复条纹填充逻辑
原代码直接修改传入的Path导致绘制错误,改用clipPath限制条纹绘制区域:
private fun drawShadingInPath(canvas: Canvas, path: Path) { mPaint.style = Paint.Style.FILL mPaint.color = when (color) { Colors.PURPLE -> Color.rgb(154, 18, 179) Colors.BLUE -> Color.rgb(18, 106, 179) Colors.GREEN -> Color.rgb(18, 179, 31) } when (shading) { Shading.EMPTY -> { mPaint.style = Paint.Style.STROKE canvas.drawPath(path, mPaint) } Shading.SOLID -> canvas.drawPath(path, mPaint) Shading.STRIP -> { canvas.save() canvas.clipPath(path) val stripDistance = width * STRIP_DISTANCE_SCALE_FACTOR var x = 0f while (x < width) { canvas.drawLine(x, 0f, x, height.toFloat(), mPaint) x += stripDistance } canvas.restore() } } }
四、布局结构调整
修改activity_main.xml,使用GridLayout作为卡牌容器:
<GridLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/card_container" android:layout_width="match_parent" android:layout_height="match_parent" android:columnCount="4" android:rowCount="3" android:alignmentMode="alignMargins" android:padding="16dp"/>
内容的提问来源于stack exchange,提问作者chia hao
相关产品推荐
相关产品推荐

