← Назад к вопросам

Как решить задачу отрисовки шахматного поля?

1.3 Junior🔥 11 комментариев
#UI и вёрстка#Производительность и оптимизация

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Подходы к отрисовке шахматного поля в Android

Отрисовка шахматного поля — классическая задача, которая демонстрирует понимание работы с графикой, макетами и алгоритмами. Вот основные подходы, которые я бы рассмотрел в зависимости от требований к производительности, гибкости и сложности логики.

1. Использование TableLayout или GridLayout (самый простой способ)

Для статического поля без сложной логики можно использовать стандартные макеты. Каждая клетка — это View (например, ImageView или View с фоном).

<TableLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:stretchColumns="*">
    
    <TableRow>
        <View android:background="#FFFFFF" android:layout_weight="1" android:aspectRatio="1"/>
        <View android:background="#000000" android:layout_weight="1" android:aspectRatio="1"/>
        <!-- ... остальные клетки -->
    </TableRow>
    
    <!-- ... остальные строки -->
</TableLayout>

Плюсы:

  • Быстрая реализация для прототипов
  • Не требует кастомной отрисовки

Минусы:

  • Неэффективно при большом количестве клеток (64 View)
  • Сложно управлять из кода (изменение цвета, добавление фигур)
  • Плохая производительность из-за вложенности

2. Кастомная отрисовка через Canvas в View или SurfaceView

Оптимальный подход для игровых приложений. Создаем собственный View и переопределяем onDraw().

class ChessBoardView(context: Context, attrs: AttributeSet) : View(context, attrs) {
    
    private val boardSize = 8
    private val lightColor = Color.parseColor("#F0D9B5")
    private val darkColor = Color.parseColor("#B58863")
    
    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        
        val cellSize = width.toFloat() / boardSize
        
        for (row in 0 until boardSize) {
            for (col in 0 until boardSize) {
                val paint = Paint().apply {
                    color = if ((row + col) % 2 == 0) lightColor else darkColor
                    isAntiAlias = true
                }
                
                val left = col * cellSize
                val top = row * cellSize
                val right = left + cellSize
                val bottom = top + cellSize
                
                canvas.drawRect(left, top, right, bottom, paint)
            }
        }
    }
    
    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        // Делаем view квадратной
        val width = MeasureSpec.getSize(widthMeasureSpec)
        val height = MeasureSpec.getSize(heightMeasureSpec)
        val size = min(width, height)
        setMeasuredDimension(size, size)
    }
}

Плюсы:

  • Высокая производительность
  • Полный контроль над отрисовкой
  • Легко добавлять анимации, подсветку клеток

Минусы:

  • Требует знания работы с Canvas
  • Нужно самостоятельно обрабатывать касания

3. Использование RecyclerView с GridLayoutManager

Хороший компромисс для сложных интерфейсов, где клетки могут содержать разный контент.

class ChessBoardAdapter(private val board: Array<Array<ChessCell>>) : 
    RecyclerView.Adapter<ChessBoardAdapter.CellViewHolder>() {
    
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CellViewHolder {
        val view = LayoutInflater.from(parent.context)
            .inflate(R.layout.item_chess_cell, parent, false)
        return CellViewHolder(view)
    }
    
    override fun onBindViewHolder(holder: CellViewHolder, position: Int) {
        val row = position / 8
        val col = position % 8
        val cell = board[row][col]
        
        holder.bind(cell)
    }
    
    override fun getItemCount() = 64
    
    class CellViewHolder(view: View) : RecyclerView.ViewHolder(view) {
        fun bind(cell: ChessCell) {
            // Установка цвета, фигуры и т.д.
        }
    }
}

// В Activity/Fragment
val recyclerView = findViewById<RecyclerView>(R.id.chess_board)
recyclerView.layoutManager = GridLayoutManager(this, 8)
recyclerView.adapter = ChessBoardAdapter(chessBoard)

Плюсы:

  • Автоматическая оптимизация (переиспользование ViewHolder)
  • Легко добавлять сложную логику в каждую клетку
  • Поддержка жестов и анимаций из коробки

Минусы:

  • Избыточность для простого поля
  • Сложнее управлять точными размерами клеток

4. Использование Compose (современный подход)

Для проектов на Jetpack Compose решение становится элегантным и декларативным.

@Composable
fun ChessBoard() {
    BoxWithConstraints(modifier = Modifier.fillMaxSize()) {
        val cellSize = maxWidth / 8
        
        Canvas(modifier = Modifier.size(maxWidth)) {
            for (row in 0 until 8) {
                for (col in 0 until 8) {
                    val color = if ((row + col) % 2 == 0) LightSquare else DarkSquare
                    
                    drawRect(
                        color = color,
                        topLeft = Offset(col * cellSize.toPx(), row * cellSize.toPx()),
                        size = Size(cellSize.toPx(), cellSize.toPx())
                    )
                }
            }
        }
    }
}

Плюсы:

  • Декларативный и читаемый код
  • Высокая производительность
  • Легко интегрировать с состоянием приложения

Минусы:

  • Требует миграции на Compose
  • Меньше ресурсов и примеров

Ключевые рекомендации

  1. Для простых статических полей используйте GridLayout или TableLayout
  2. Для игровых приложений выбирайте кастомную отрисовку через Canvas
  3. Для сложных интерактивных интерфейсов с динамическим контентом подойдет RecyclerView
  4. Для новых проектов рассматривайте Jetpack Compose как наиболее современное решение

Дополнительные улучшения:

  • Добавьте кеширование Bitmap для повторяющихся элементов
  • Реализуйте обработку касаний для определения выбранной клетки
  • Используйте ObjectAnimator для плавных анимаций перемещения фигур
  • Оптимизируйте через hardware acceleration и избегайте операций в onDraw()

Выбор подхода зависит от конкретных требований: нужна ли поддержка старых версий Android, планируются ли сложные анимации, будет ли поле частью более сложного интерфейса. В профессиональной разработке я чаще всего использую кастомную отрисовку через Canvas, так как она дает максимальный контроль и производительность для игровых задач.

Как решить задачу отрисовки шахматного поля? | PrepBro