You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 19:46:59