Skip to content

Commit

Permalink
2024 lab2 update
Browse files Browse the repository at this point in the history
  • Loading branch information
kpomazi committed Feb 22, 2024
1 parent 627df10 commit c987a2b
Showing 1 changed file with 60 additions and 67 deletions.
127 changes: 60 additions & 67 deletions docs/laborok/tictactoe/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -201,69 +201,10 @@ A `MainActivity` a fenti ábra alapján három menüpontot tartalmaz középre i

Nézzük át a laborvezetővel a felület felépítését!

## Highscore gomb eseménykezelő

Az *Eredmények* menüpontra kattintva egy `Toast` üzenetet kell megjeleníteni. Ehhez meg kell keresni az *Eredmények* menüpont gombját és be kell állítani neki az alábbi eseménykezelőt a `MainActivity` `onCreate()` függvényén belül:

```kotlin
val btnHighScore = findViewById<Button>(R.id.btnHighScores)
btnHighScore.setOnClickListener {
Toast.makeText(
this@MainActivity,
getString(R.string.toast_highscore),
Toast.LENGTH_LONG
).show()
}
```

!!!info "onClickListener"
A [`setOnClickListener`](https://developer.android.com/reference/android/view/View.html#setOnClickListener(android.view.View.OnClickListener)) függvény valójában egy [`View.OnClickListener`](https://developer.android.com/reference/android/view/View.OnClickListener) interfészt megvalósító objektumot vár paraméterként, amelynek egyetlen megvalósítandó függvénye van. Ezt létrehozhatnánk a Java-s [anonim osztályok stílusában](https://kotlinlang.org/docs/reference/object-declarations.html#object-expressions) is, de helyette kihasználjuk, hogy a függvények elsőrendű tagjai a Kotlin nyelvnek, így rendelkezünk igazi függvény típusokkal. Jelen esetben a paraméterben egy olyan [lambda kifejezést](https://kotlinlang.org/docs/reference/lambdas.html#lambda-expressions-and-anonymous-functions) adunk át, amely fejléce megegyezik az elvárt interfész egyetlen függvényének fejlécével, a [SAM conversion](https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions) nyelvi funkció pedig a háttérben a lambda alapján létrehozza a megfelelő `View.OnClickListener` példányt.


!!!example "BEADANDÓ (1 pont)"
Készíts egy **képernyőképet**, amelyen látszik a **highscores Toast üzenet** (emulátoron, készüléket tükrözve vagy képernyőfelvétellel), a **az ahhoz tartozó kódrészlet**, valamint a **neptun kódod a kódban valahol kommentként**. A képet a megoldásban a repository-ba f1.png néven töltsd föl.


## AboutActivity felület

Ahogy korábban említettük, az *Infó* menü elindítja az `AboutActivity`-t. Elsőként készítsük el az `AboutActivity` felületét, melyet a `res/layout/activity_about.xml` ír le. Mint korábban, itt is lehet `ConstraintLayout`-ot készíteni a segítséggel, vagy alább megtalálható az XML:

![](assets/constraint_layout_2.gif)

```xml
<?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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".AboutActivity"
tools:viewBindingIgnore="true">

<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:text="@string/txt_about"
android:textSize="32sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>
```

## Navigáció megvalósítása Activityk közt

A következő lépésként valósítsuk meg a navigációt (váltást) az `Activity`-k között. Az *Új játék* menüpont hatására a `GameActivity`-re, az *Infó* menüpont hatására pedig az `AboutActivity`-re kell átváltanunk. `Activity`-k közti váltást egy `Intent` segítségével tudunk implementálni - beszéljük meg a laborvezetővel az `Intent`-ek alapjait. Ezt a témát előadáson később mélyebben fogjuk még érinteni.

Valósítsuk meg ezen két gomb eseménykezelőjét szintén a `MainActivity` `onCreate()` függvényében!
## ViewBinding

!!!tip "findViewById"
Ezt csinálhatnánk az előzőhöz hasonlóan, azaz példányosítunk egy gombot, a `findViewById` metódussal referenciát szerzünk a felületen lévő vezérlőre, és a példányon beállítjuk az eseménykezelőt. Azonban a `findViewById` hívásnak számos problémája [van](https://developer.android.com/topic/libraries/view-binding#findviewbyid). Ezekről bővebben az előadáson lesz szó (pl.: *Null safety*, *type safety*). Ezért e helyett "nézetkötést", azaz `ViewBinding`-ot fogunk használni.
A [`ViewBinding`](https://developer.android.com/topic/libraries/view-binding) a kódírást könnyíti meg számunkra. Amennyiben ezt használjuk, az automatikusan generálódó *binding* osztályokon keresztül közvetlen referencián keresztül tudunk elérni minden *ID*-val rendelkező erőforrást az `XML` fájljainkban.
A felületi elemeink elérésére "nézetkötést", azaz `ViewBinding`-ot fogunk használni.
A [`ViewBinding`](https://developer.android.com/topic/libraries/view-binding) a kódírást könnyíti meg számunkra. Amennyiben ezt használjuk, az automatikusan generálódó *binding* osztályokon keresztül közvetlen referencián keresztül tudunk elérni minden *ID*-val rendelkező erőforrást az `XML` fájljainkban.

Először is be kell kapcsolnunk a modulunkra a `ViewBinding`-ot. Az `app` modulhoz tartozó `build.gradle.kts` fájlban az `android` tagen belülre illesszük be az engedélyezést:

Expand Down Expand Up @@ -296,7 +237,7 @@ Majd nyomjunk a felső kék sávon jobb oldalon megjelenő `Sync Now` gombra. Ez
Minden generált osztály tartalmaz egy `getRoot()` metódust, amely direkt referenciaként szolgál a layout gyökerére. A példában a `getRoot()` metódus a `LinearLayout`-tal tér vissza.


Ezzel után már a teljes modulunkban automatikusan elérhetővé vált a `ViewBinging`. Használatához az `Activity`-nkben csak példányosítanunk kell a `binding` objektumot, amin keresztül majd elérhetjük az erőforrásainkat.
Ezzel után már a teljes modulunkban automatikusan elérhetővé vált a `ViewBinding`. Használatához az `Activity`-nkben csak példányosítanunk kell a `binding` objektumot, amin keresztül majd elérhetjük az erőforrásainkat.
A `binding` példány működéséhez három dolgot kell tennünk:

1. A generált `binding` osztály *statikus* `inflate` függvényével példányosítjuk a `binding` osztályunkat az `Activity`-hez,
Expand Down Expand Up @@ -324,7 +265,9 @@ class MainActivity : AppCompatActivity() {
!!!tip "lateinit"
A [`lateinit`](https://kotlinlang.org/docs/reference/properties.html#late-initialized-properties-and-variables) kulcsszóval megjelölt property-ket a fordító megengedi inicializálatlanul hagyni az osztály konstruktorának lefutása utánig, anélkül, hogy nullable-ként kéne azokat megjelölnünk (ami később kényelmetlenné tenné a használatukat, mert mindig ellenőriznünk kéne, hogy `null`-e az értékük). Ez praktikus olyan esetekben, amikor egy osztály inicializálása nem a konstruktorában történik (például ahogy az `Activity`-k esetében az `onCreate`-ben), mert később az esetleges `null` eset lekezelése nélkül használhatjuk majd a property-t. A `lateinit` használatával átvállaljuk a felelősséget a fordítótól, hogy a property-t az első használata előtt inicializálni fogjuk - ellenkező esetben kivételt kapunk.

Ezek után már be is állíthatjuk a gombjaink eseménykezelőit. (Cseréljük le a `btnHighScores`-t is.):
## Highscore gomb eseménykezelő

Az *Eredmények* menüpontra kattintva egy `Toast` üzenetet kell megjeleníteni. Ehhez meg kell keresni az *Eredmények* menüpont gombját és be kell állítani neki az alábbi eseménykezelőt a `MainActivity` `onCreate()` függvényén belül:

```kotlin
binding.btnHighScores.setOnClickListener {
Expand All @@ -334,7 +277,54 @@ binding.btnHighScores.setOnClickListener {
Toast.LENGTH_LONG
).show()
}
```

!!!info "onClickListener"
A [`setOnClickListener`](https://developer.android.com/reference/android/view/View.html#setOnClickListener(android.view.View.OnClickListener)) függvény valójában egy [`View.OnClickListener`](https://developer.android.com/reference/android/view/View.OnClickListener) interfészt megvalósító objektumot vár paraméterként, amelynek egyetlen megvalósítandó függvénye van. Ezt létrehozhatnánk a Java-s [anonim osztályok stílusában](https://kotlinlang.org/docs/reference/object-declarations.html#object-expressions) is, de helyette kihasználjuk, hogy a függvények elsőrendű tagjai a Kotlin nyelvnek, így rendelkezünk igazi függvény típusokkal. Jelen esetben a paraméterben egy olyan [lambda kifejezést](https://kotlinlang.org/docs/reference/lambdas.html#lambda-expressions-and-anonymous-functions) adunk át, amely fejléce megegyezik az elvárt interfész egyetlen függvényének fejlécével, a [SAM conversion](https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions) nyelvi funkció pedig a háttérben a lambda alapján létrehozza a megfelelő `View.OnClickListener` példányt.


!!!example "BEADANDÓ (1 pont)"
Készíts egy **képernyőképet**, amelyen látszik a **highscores Toast üzenet** (emulátoron, készüléket tükrözve vagy képernyőfelvétellel), a **az ahhoz tartozó kódrészlet**, valamint a **neptun kódod a kódban valahol kommentként**. A képet a megoldásban a repository-ba f1.png néven töltsd föl.


## AboutActivity felület

Ahogy korábban említettük, az *Infó* menü elindítja az `AboutActivity`-t. Elsőként készítsük el az `AboutActivity` felületét, melyet a `res/layout/activity_about.xml` ír le. Mint korábban, itt is lehet `ConstraintLayout`-ot készíteni a segítséggel, vagy alább megtalálható az XML:

![](assets/constraint_layout_2.gif)

```xml
<?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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".AboutActivity"
tools:viewBindingIgnore="true">

<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:text="@string/txt_about"
android:textSize="32sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>
```

## Navigáció megvalósítása Activityk közt

A következő lépésként valósítsuk meg a navigációt (váltást) az `Activity`-k között. Az *Új játék* menüpont hatására a `GameActivity`-re, az *Infó* menüpont hatására pedig az `AboutActivity`-re kell átváltanunk. `Activity`-k közti váltást egy `Intent` segítségével tudunk implementálni - beszéljük meg a laborvezetővel az `Intent`-ek alapjait. Ezt a témát előadáson később mélyebben fogjuk még érinteni.

Valósítsuk meg ezen két gomb eseménykezelőjét szintén a `MainActivity` `onCreate()` függvényében!

```kotlin
binding.btnStart.setOnClickListener {
startActivity(Intent(this@MainActivity, GameActivity::class.java))
}
Expand Down Expand Up @@ -678,9 +668,12 @@ override fun onTouchEvent(event: MotionEvent?): Boolean {
}
```

## Alkalmazás ikon lecserélése
## Alkalmazás ikon lecserélése - Önálló feladat

Az alkalmazás ikonját jelenleg a `res/mipmap[-ldpi/mdpi/hdpi/xhdpi/...]` mappákban található `ic_launcher.png` jelképezi. Keressünk egy új ikont és cseréljük le. Nem muszáj az ikont minden felbontásban elkészíteni, egyszerűen elhelyezhetónk egy méretet a `mipmap` mappában is (melyet létre kell hozni), ekkor természetesen különböző felbontású eszközökön torzulhat az ikon képe. (Ha marad idő, a beépített *Asset Studio*-val elkészíthetjük az összes szükséges változatot.)

Az alkalmazás ikonját jelenleg a `res/mipmap[-ldpi/mdpi/hdpi/xhdpi/...]` mappákban található `ic_launcher.png` jelképezi. A laborvezető segítségével keressünk egy új ikont és cseréljük le. Nem muszáj az ikont minden felbontásban elkészíteni, egyszerűen elhelyezhetónk egy méretet a `mipmap` mappában is (melyet létre kell hozni), ekkor természetesen különböző felbontású eszközökön torzulhat az ikon képe. (Ha marad idő, a beépített *Asset Studio*-val elkészíthetjük az összes szükséges változatot.)
!!!tip "Ikon generálása"
Ikon generálására használhatjuk például a következő oldalt: https://icon.kitchen/

Próbáljuk ki az alkalmazást!

Expand All @@ -697,7 +690,7 @@ binding.btnStart.setOnClickListener {
Készíts egy **képernyőképet**, amelyen látszik a **játéktér játék közben** (emulátoron, készüléket tükrözve vagy képernyőfelvétellel), a **TicTacToeView kódjának egy részlete**, valamint a **neptun kódod a kódban valahol kommentként**. A képet a megoldásban a repository-ba f4.png néven töltsd föl.


## Játéklogika ellenőrzése - önálló feladat
## Játéklogika ellenőrzése - Önálló feladat

Valósítson meg egy függvényt, mely minden lépés után leellenőrzi, hogy győzött-e valamelyik játékos, vagy nincs-e döntetlen. Amennyiben vége a játéknak, egy `Toast` üzenettel jelezze ezt a felhasználónak és lépjen vissza a főmenübe. A laborvezető segítségével vizsgálja meg, hogy a `View` osztályból hogyan érhető el az őt tartalmazó "host" `Activity`, aminek így például egy `gameOver()` függvénye meghívható, ami megvalósítja a fent leírt játék befejezést.

Expand Down

0 comments on commit c987a2b

Please sign in to comment.