|
|
@@ -3,15 +3,16 @@ package com.example.memory
|
|
|
import android.animation.Animator
|
|
|
import android.animation.AnimatorListenerAdapter
|
|
|
import android.animation.AnimatorInflater
|
|
|
+import android.animation.ObjectAnimator
|
|
|
import androidx.appcompat.app.AppCompatActivity
|
|
|
import android.os.Bundle
|
|
|
-import android.os.Handler
|
|
|
-import android.os.Looper
|
|
|
import android.view.Gravity
|
|
|
import android.view.View
|
|
|
import android.widget.FrameLayout
|
|
|
import android.widget.GridLayout
|
|
|
import android.widget.ImageView
|
|
|
+import android.widget.Toast
|
|
|
+import kotlinx.coroutines.*
|
|
|
|
|
|
class MainActivity : AppCompatActivity() {
|
|
|
|
|
|
@@ -21,13 +22,14 @@ class MainActivity : AppCompatActivity() {
|
|
|
private val cardBack = R.drawable.squarecat
|
|
|
|
|
|
private val flippedCards = mutableListOf<ImageView>()
|
|
|
+ private var matchedPairs = 0
|
|
|
private var isBoardLocked = false
|
|
|
+ private val activityScope = CoroutineScope(Dispatchers.Main)
|
|
|
|
|
|
override fun onCreate(savedInstanceState: Bundle?) {
|
|
|
super.onCreate(savedInstanceState)
|
|
|
|
|
|
val rootLayout = FrameLayout(this)
|
|
|
-
|
|
|
val gridLayout = GridLayout(this).apply {
|
|
|
columnCount = 4
|
|
|
rowCount = 2
|
|
|
@@ -52,7 +54,6 @@ class MainActivity : AppCompatActivity() {
|
|
|
setImageResource(cardBack)
|
|
|
tag = images[i]
|
|
|
setOnClickListener(cardClickListener)
|
|
|
-
|
|
|
val params = GridLayout.LayoutParams().apply {
|
|
|
width = cardSize
|
|
|
height = cardSize
|
|
|
@@ -67,23 +68,26 @@ class MainActivity : AppCompatActivity() {
|
|
|
setContentView(rootLayout)
|
|
|
}
|
|
|
|
|
|
+ private val cardClickListener = View.OnClickListener { view ->
|
|
|
+ if (view is ImageView) {
|
|
|
+ flipCard(view)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
private fun flipCard(imageView: ImageView) {
|
|
|
- if (isBoardLocked || flippedCards.contains(imageView)) {
|
|
|
+ if (isBoardLocked || flippedCards.contains(imageView) || imageView.parent == null) {
|
|
|
return
|
|
|
}
|
|
|
|
|
|
val outAnimator = AnimatorInflater.loadAnimator(applicationContext, R.animator.card_flip_out)
|
|
|
outAnimator.setTarget(imageView)
|
|
|
-
|
|
|
outAnimator.addListener(object : AnimatorListenerAdapter() {
|
|
|
override fun onAnimationEnd(animation: Animator) {
|
|
|
val faceImageId = imageView.tag as Int
|
|
|
imageView.setImageResource(faceImageId)
|
|
|
-
|
|
|
val inAnimator = AnimatorInflater.loadAnimator(applicationContext, R.animator.card_flip_in)
|
|
|
inAnimator.setTarget(imageView)
|
|
|
inAnimator.start()
|
|
|
-
|
|
|
handleFlippedCard(imageView)
|
|
|
}
|
|
|
})
|
|
|
@@ -94,39 +98,65 @@ class MainActivity : AppCompatActivity() {
|
|
|
flippedCards.add(imageView)
|
|
|
if (flippedCards.size == 2) {
|
|
|
isBoardLocked = true
|
|
|
- checkForMatch()
|
|
|
+ activityScope.launch {
|
|
|
+ checkForMatch()
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- private fun checkForMatch() {
|
|
|
+ private suspend fun checkForMatch() {
|
|
|
val card1 = flippedCards[0]
|
|
|
val card2 = flippedCards[1]
|
|
|
|
|
|
if (card1.tag == card2.tag) {
|
|
|
- card1.setOnClickListener(null)
|
|
|
- card2.setOnClickListener(null)
|
|
|
+ // Карты совпали: запускаем анимацию исчезновения
|
|
|
+ matchedPairs++
|
|
|
+ // Убираем их из списка перевернутых
|
|
|
flippedCards.clear()
|
|
|
- isBoardLocked = false
|
|
|
+ // Даем небольшую паузу, чтобы игрок увидел совпадение
|
|
|
+ delay(500)
|
|
|
+ disappearAnimation(card1)
|
|
|
+ disappearAnimation(card2)
|
|
|
} else {
|
|
|
- Handler(Looper.getMainLooper()).postDelayed({
|
|
|
- flipCardBack(card1)
|
|
|
- flipCardBack(card2)
|
|
|
- }, 1000)
|
|
|
+ // Карты не совпали: переворачиваем обратно через секунду
|
|
|
+ delay(1000)
|
|
|
+ flipCardBack(card1)
|
|
|
+ flipCardBack(card2)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private fun disappearAnimation(imageView: ImageView) {
|
|
|
+ ObjectAnimator.ofFloat(imageView, "alpha", 1f, 0f).apply {
|
|
|
+ duration = 500 // Длительность анимации
|
|
|
+ addListener(object : AnimatorListenerAdapter() {
|
|
|
+ override fun onAnimationEnd(animation: Animator) {
|
|
|
+ // Делаем View невидимым, чтобы освободить место для кликов
|
|
|
+ imageView.visibility = View.INVISIBLE
|
|
|
+ // После исчезновения второй карты проверяем на выигрыш
|
|
|
+ if (imageView == flippedCards.getOrNull(1)) {
|
|
|
+ checkWinCondition()
|
|
|
+ }
|
|
|
+ }
|
|
|
+ })
|
|
|
+ start()
|
|
|
}
|
|
|
+ // После исчезновения карты разблокируем доску
|
|
|
+ isBoardLocked = false
|
|
|
+ checkWinCondition()
|
|
|
}
|
|
|
|
|
|
+
|
|
|
private fun flipCardBack(imageView: ImageView) {
|
|
|
+ if (imageView.parent == null) return // Проверка, что карта еще на поле
|
|
|
+
|
|
|
val outAnimator = AnimatorInflater.loadAnimator(applicationContext, R.animator.card_flip_out)
|
|
|
outAnimator.setTarget(imageView)
|
|
|
-
|
|
|
outAnimator.addListener(object : AnimatorListenerAdapter() {
|
|
|
override fun onAnimationEnd(animation: Animator) {
|
|
|
imageView.setImageResource(cardBack)
|
|
|
-
|
|
|
val inAnimator = AnimatorInflater.loadAnimator(applicationContext, R.animator.card_flip_in)
|
|
|
inAnimator.setTarget(imageView)
|
|
|
inAnimator.start()
|
|
|
-
|
|
|
if (flippedCards.size == 2) {
|
|
|
flippedCards.clear()
|
|
|
isBoardLocked = false
|
|
|
@@ -136,9 +166,15 @@ class MainActivity : AppCompatActivity() {
|
|
|
outAnimator.start()
|
|
|
}
|
|
|
|
|
|
- private val cardClickListener = View.OnClickListener { view ->
|
|
|
- if (view is ImageView) {
|
|
|
- flipCard(view)
|
|
|
+ private fun checkWinCondition() {
|
|
|
+ if (matchedPairs == cardFaces.size) {
|
|
|
+ Toast.makeText(this, "Поздравляем! Вы выиграли!", Toast.LENGTH_LONG).show()
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ override fun onDestroy() {
|
|
|
+ super.onDestroy()
|
|
|
+ // Отменяем все корутины, чтобы избежать утечек памяти
|
|
|
+ activityScope.cancel()
|
|
|
+ }
|
|
|
}
|