Bu codelab, Android Kotlin Hakkında Temel Bilgiler kursunun bir parçasıdır. Bu kurstan en iyi şekilde yararlanmak için codelab'leri sırayla tamamlamanızı öneririz. Kursla ilgili tüm codelab'ler Android Kotlin Hakkında Temel Bilgiler codelab'leri açılış sayfasında listelenir.
Başlık ekranı | Oyun ekranı | Puan ekranı |
Giriş
Bu codelab'de, Android Mimari Bileşenlerinden biri olan ViewModel
hakkında bilgi edineceksiniz:
- Kullanıcı arayüzüyle ilgili verileri yaşam döngüsüne duyarlı bir şekilde depolamak ve yönetmek için
ViewModel
sınıfını kullanırsınız.ViewModel
sınıfı, ekran döndürme ve klavye kullanılabilirliğinde değişiklik gibi cihaz yapılandırması değişikliklerinde verilerin korunmasını sağlar. - Yapılandırma değişikliklerinden etkilenmeyen
ViewModel
nesnesini oluşturup döndürmek içinViewModelFactory
sınıfını kullanırsınız.
Bilmeniz gerekenler
- Kotlin'de temel Android uygulamaları oluşturma
- Uygulamanızda gezinmeyi uygulamak için gezinme grafiğini kullanma
- Uygulamanızın hedefleri arasında gezinmek ve gezinme hedefleri arasında veri aktarmak için kod ekleme
- Etkinlik ve parça yaşam döngülerinin işleyiş şekli.
- Bir uygulamaya günlük kaydı bilgilerini ekleme ve Android Studio'da Logcat'i kullanarak günlükleri okuma
Neler öğreneceksiniz?
- Önerilen Android uygulama mimarisini kullanma
- Uygulamanızda
Lifecycle
,ViewModel
veViewModelFactory
sınıflarını kullanma - Cihaz yapılandırması değişiklikleri sırasında kullanıcı arayüzü verilerini koruma
- Fabrika yöntemi tasarım kalıbının ne olduğu ve nasıl kullanılacağı.
- Arayüzü kullanarak
ViewModel
nesnesi oluşturmaViewModelProvider.Factory
Yapacaklarınız
- Uygulamanın verilerini kaydetmek için uygulamaya
ViewModel
ekleyin. Böylece veriler, yapılandırma değişikliklerinden etkilenmez. - Oluşturucu parametreleriyle bir
ViewModel
nesnesi oluşturmak içinViewModelFactory
ve fabrika yöntemi tasarım kalıbını kullanın.
5. dersteki codelab'lerde, başlangıç koduyla başlayarak GuessTheWord uygulamasını geliştirirsiniz. GuessTheWord, iki oyuncunun mümkün olan en yüksek puanı elde etmek için işbirliği yaptığı, sessiz sinema tarzı bir oyundur.
Birinci oyuncu, uygulamadaki kelimelere bakar ve her birini sırayla canlandırır. Kelimeyi ikinci oyuncuya göstermemeye dikkat eder. İkinci oyuncu kelimeyi tahmin etmeye çalışır.
Oyunu oynamak için ilk oyuncu cihazda uygulamayı açar ve aşağıdaki ekran görüntüsünde gösterildiği gibi bir kelime (ör. "gitar") görür.
İlk oyuncu, kelimeyi söylememeye dikkat ederek kelimeyi canlandırır.
- İkinci oyuncu kelimeyi doğru tahmin ettiğinde birinci oyuncu Bildim düğmesine basar. Bu işlem, sayıyı bir artırır ve sonraki kelimeyi gösterir.
- İkinci oyuncu kelimeyi tahmin edemezse birinci oyuncu Atla düğmesine basar. Bu durumda sayı bir azalır ve bir sonraki kelimeye geçilir.
- Oyunu sonlandırmak için End Game (Oyunu Sonlandır) düğmesine basın. (Bu işlev, serideki ilk codelab'in başlangıç kodunda yer almaz.)
Bu görevde, başlangıç uygulamasını indirip çalıştıracak ve kodu inceleyeceksiniz.
1. adım: Başlayın
- GuessTheWord başlangıç kodunu indirin ve projeyi Android Studio'da açın.
- Uygulamayı Android destekli bir cihazda veya emülatörde çalıştırın.
- Düğmelere dokunun. Atla düğmesinin sonraki kelimeyi gösterdiğini ve puanı bir azalttığını, Anladım düğmesinin ise sonraki kelimeyi gösterdiğini ve puanı bir artırdığını unutmayın. Oyunu Bitir düğmesi uygulanmadığından bu düğmeye dokunduğunuzda herhangi bir işlem yapılmaz.
2. adım: Kod incelemesi yapın
- Uygulamanın nasıl çalıştığını anlamak için Android Studio'da kodu inceleyin.
- Özellikle önemli olan aşağıdaki dosyalara göz atmayı unutmayın.
MainActivity.kt
Bu dosya yalnızca varsayılan, şablonla oluşturulmuş kodu içeriyor.
res/layout/main_activity.xml
Bu dosya, uygulamanın ana düzenini içerir. Kullanıcı uygulamada gezinirken NavHostFragment
diğer parçaları barındırır.
Kullanıcı arayüzü parçaları
Başlangıç kodunda, com.example.android.guesstheword.screens
paketi altında üç farklı pakette üç parça bulunur:
- Başlık ekranı için
title/TitleFragment
- Oyun ekranı için
game/GameFragment
- Skor ekranı için
score/ScoreFragment
screens/title/TitleFragment.kt
Başlık parçası, uygulama başlatıldığında görüntülenen ilk ekrandır. Oyun ekranına gitmek için Oyna düğmesine bir tıklama işleyicisi ayarlanır.
screens/game/GameFragment.kt
Bu, oyunun büyük bir bölümünün geçtiği ana parçadır:
- Değişkenler, mevcut kelime ve mevcut puan için tanımlanır.
resetList()
yöntemi içinde tanımlananwordList
, oyunda kullanılacak kelimelerin örnek listesidir.onSkip()
yöntemi, Atla düğmesinin tıklama işleyicisidir. Puanı 1 azaltır venextWord()
yöntemini kullanarak sonraki kelimeyi gösterir.onCorrect()
yöntemi, Anladım düğmesinin tıklama işleyicisidir. Bu yöntem,onSkip()
yöntemine benzer şekilde uygulanır. Tek fark, bu yöntemin puanı azaltmak yerine 1 puan artırmasıdır.
screens/score/ScoreFragment.kt
ScoreFragment
, oyundaki son ekrandır ve oyuncunun nihai puanını gösterir. Bu codelab'de, bu ekranı görüntülemek ve nihai puanı göstermek için uygulama ekleyeceksiniz.
res/navigation/main_navigation.xml
Gezinme grafiği, parçaların gezinme yoluyla nasıl bağlandığını gösterir:
- Kullanıcı, başlık parçasından oyun parçasına gidebilir.
- Kullanıcı, oyun fragmentinden skor fragmentine gidebilir.
- Kullanıcı, skor fragmentinden oyun fragmentine geri dönebilir.
Bu görevde, GuessTheWord başlangıç uygulamasındaki sorunları bulursunuz.
- Başlangıç kodunu çalıştırın ve oyunu birkaç kelime boyunca oynayın. Her kelimeden sonra Atla veya Anladım'a dokunun.
- Oyun ekranında artık bir kelime ve mevcut skor gösteriliyor. Cihazı veya emülatörü döndürerek ekran yönünü değiştirin. Mevcut puanın kaybolduğunu unutmayın.
- Oyunu birkaç kelime daha kullanarak açıklayın. Oyun ekranı bir skorla gösterildiğinde uygulamayı kapatıp yeniden açın. Uygulama durumu kaydedilmediği için oyunun baştan başladığını fark edeceksiniz.
- Oyunu birkaç kelimeyle oynayın, ardından Oyunu Bitir düğmesine dokunun. Hiçbir şey olmadığını fark edeceksiniz.
Uygulamadaki sorunlar:
- Başlangıç uygulaması, yapılandırma değişiklikleri sırasında (ör. cihaz yönü değiştiğinde veya uygulama kapatılıp yeniden başlatıldığında) uygulama durumunu kaydetmez ve geri yüklemez.
Bu sorunuonSaveInstanceState()
geri çağırmasını kullanarak çözebilirsiniz. AncakonSaveInstanceState()
yöntemini kullanmak için durumu bir pakette kaydetmek ve bu durumu almak üzere mantığı uygulamak için ek kod yazmanız gerekir. Ayrıca, depolanabilecek veri miktarı da minimum düzeydedir. - Kullanıcı Oyunu Bitir düğmesine dokunduğunda oyun ekranı, skor ekranına gitmiyor.
Bu sorunları, bu codelab'de öğrendiğiniz uygulama mimarisi bileşenlerini kullanarak çözebilirsiniz.
Uygulama mimarisi
Uygulama mimarisi, uygulamalarınızın sınıflarını ve aralarındaki ilişkileri, kodun düzenli olması, belirli senaryolarda iyi performans göstermesi ve kolayca kullanılabilmesi için tasarlama yöntemidir. Bu dört codelab'den oluşan seride, GuessTheWord uygulamasında yaptığınız iyileştirmeler Android uygulama mimarisi yönergelerine uygun olur ve Android Architecture Components'ı kullanırsınız. Android uygulama mimarisi, MVVM (model-view-viewmodel) mimari desenine benzer.
GuessTheWord uygulaması, ilgi alanlarının ayrılması tasarım ilkesine uyar ve sınıflara ayrılır. Her sınıf ayrı bir ilgi alanını ele alır. Dersin bu ilk codelab'inde, çalıştığınız sınıflar bir kullanıcı arayüzü denetleyicisi, bir ViewModel
ve bir ViewModelFactory
'dir.
Kullanıcı arayüzü denetleyicisi
Kullanıcı arayüzü denetleyicisi, Activity
veya Fragment
gibi kullanıcı arayüzü tabanlı bir sınıftır. Bir kullanıcı arayüzü denetleyicisi yalnızca görünümleri görüntüleme ve kullanıcı girişini yakalama gibi kullanıcı arayüzü ve işletim sistemi etkileşimlerini işleyen mantığı içermelidir. Gösterilecek metni belirleyen mantık gibi karar verme mantığını kullanıcı arayüzü denetleyicisine yerleştirmeyin.
GuessTheWord başlangıç kodunda, kullanıcı arayüzü denetleyicileri üç parçadır: GameFragment
, ScoreFragment,
ve TitleFragment
. "İlgi alanlarının ayrılması" tasarım ilkesine göre GameFragment
yalnızca oyun öğelerini ekrana çizmekten ve kullanıcının düğmelere ne zaman dokunduğunu bilmekten sorumludur. Kullanıcı bir düğmeye dokunduğunda bu bilgiler GameViewModel
'ya iletilir.
ViewModel
Bir ViewModel
, ViewModel
ile ilişkili bir parçada veya etkinlikte gösterilecek verileri tutar. Bir ViewModel
, verilerin kullanıcı arayüzü denetleyicisi tarafından görüntülenmeye hazırlanması için veriler üzerinde basit hesaplamalar ve dönüşümler yapabilir. Bu mimaride ViewModel
karar verme işlemini gerçekleştirir.GameViewModel
, ekranda gösterilecek veriler olduğu için puan değeri, kelime listesi ve mevcut kelime gibi verileri tutar. GameViewModel
, verilerin mevcut durumuna karar vermek için basit hesaplamalar yapmaya yönelik iş mantığını da içerir.
ViewModelFactory
ViewModelFactory
, oluşturucu parametreleriyle veya parametreler olmadan ViewModel
nesnelerini oluşturur.
Sonraki codelab'lerde, kullanıcı arayüzü denetleyicileri ve ViewModel
ile ilgili diğer Android Architecture Components hakkında bilgi edineceksiniz.
ViewModel
sınıfı, kullanıcı arayüzüyle ilgili verileri depolamak ve yönetmek için tasarlanmıştır. Bu uygulamada her ViewModel
bir parçayla ilişkilendirilir.
Bu görevde, uygulamanıza ilk ViewModel
öğenizi (GameFragment
için GameViewModel
) eklersiniz. Ayrıca ViewModel
simgesinin yaşam döngüsüne duyarlı olmasının ne anlama geldiğini de öğrenirsiniz.
1. adım: GameViewModel sınıfını ekleyin
build.gradle(module:app)
dosyasını açın.dependencies
bloğunun içineViewModel
için Gradle bağımlılığını ekleyin.
Kitaplığın en son sürümünü kullanırsanız çözüm uygulaması beklendiği gibi derlenir. Çalışmıyorsa sorunu çözmeyi deneyin veya aşağıda gösterilen sürüme geri dönün.
//ViewModel
implementation 'androidx.lifecycle:lifecycle-extensions:2.0.0'
screens/game/
paket klasöründeGameViewModel
adlı yeni bir Kotlin sınıfı oluşturun.GameViewModel
sınıfınınViewModel
soyut sınıfını genişletmesini sağlayın.ViewModel
'ın yaşam döngüsüne nasıl duyarlı olduğunu daha iyi anlamanıza yardımcı olmak içinlog
ifadesi içeren birinit
bloğu ekleyin.
class GameViewModel : ViewModel() {
init {
Log.i("GameViewModel", "GameViewModel created!")
}
}
2. adım: onCleared() işlevini geçersiz kılın ve günlük kaydı ekleyin
İlişkili parça ayrıldığında veya etkinlik tamamlandığında ViewModel
yok edilir. ViewModel
yok edilmeden hemen önce kaynakları temizlemek için onCleared()
geri çağırma işlevi çağrılır.
GameViewModel
sınıfındaonCleared()
yöntemini geçersiz kılın.onCleared()
içine bir günlük ifadesi ekleyerekGameViewModel
yaşam döngüsünü izleyin.
override fun onCleared() {
super.onCleared()
Log.i("GameViewModel", "GameViewModel destroyed!")
}
3. adım: GameViewModel'ı oyun parçasıyla ilişkilendirin
Bir ViewModel
, kullanıcı arayüzü denetleyicisiyle ilişkilendirilmelidir. İkisini ilişkilendirmek için kullanıcı arayüzü denetleyicisinde ViewModel
öğesine bir referans oluşturursunuz.
Bu adımda, ilgili kullanıcı arayüzü denetleyicisi olan GameFragment
içinde GameViewModel
öğesinin referansını oluşturursunuz.
GameFragment
sınıfında, üst düzeyde sınıf değişkeni olarakGameViewModel
türünde bir alan ekleyin.
private lateinit var viewModel: GameViewModel
4. adım: ViewModel'i başlatın
Ekran döndürme gibi yapılandırma değişiklikleri sırasında parçalar gibi kullanıcı arayüzü denetleyicileri yeniden oluşturulur. Ancak ViewModel
örnekleri kalır. ViewModel
sınıfını kullanarak ViewModel
örneğini oluşturursanız parça her yeniden oluşturulduğunda yeni bir nesne oluşturulur. Bunun yerine, ViewModelProvider
kullanarak ViewModel
örneğini oluşturun.
ViewModelProvider
nasıl çalışır?
ViewModelProvider
, mevcut birViewModel
varsa onu döndürür, yoksa yeni birViewModel
oluşturur.ViewModelProvider
, verilen kapsamla (bir etkinlik veya bir parça) ilişkili birViewModel
örneği oluşturur.- Oluşturulan
ViewModel
, kapsam geçerli olduğu sürece saklanır. Örneğin, kapsam bir parçaysaViewModel
, parça ayrılana kadar korunur.
ViewModel
öğesini başlatın. ViewModelProviders.of()
yöntemini kullanarak ViewModelProvider
oluşturun:
GameFragment
sınıfındaviewModel
değişkenini başlatın. Bu koduonCreateView()
içine, bağlama değişkeninin tanımından sonra yerleştirin.ViewModelProviders.of()
yöntemini kullanın ve ilişkiliGameFragment
bağlamını veGameViewModel
sınıfını iletin.ViewModel
nesnesinin başlatılmasının üstüne,ViewModelProviders.of()
yöntem çağrısını günlüğe kaydetmek için bir günlük ifadesi ekleyin.
Log.i("GameFragment", "Called ViewModelProviders.of")
viewModel = ViewModelProviders.of(this).get(GameViewModel::class.java)
- Uygulamayı çalıştırın. Android Studio'da Logcat bölmesini açın ve
Game
ile filtreleyin. Cihazınızda veya emülatörünüzde Oynat düğmesine dokunun. Oyun ekranı açılır.
Logcat'te gösterildiği gibi,GameFragment
öğesininonCreateView()
yöntemi,GameViewModel
öğesini oluşturmak içinViewModelProviders.of()
yöntemini çağırır.GameFragment
veGameViewModel
öğesine eklediğiniz günlük kaydı ifadeleri Logcat'te gösterilir.
- Cihazınızda veya emülatörünüzde otomatik döndürme ayarını etkinleştirin ve ekran yönünü birkaç kez değiştirin.
GameFragment
her seferinde yok edilip yeniden oluşturulduğundanViewModelProviders.of()
her seferinde çağrılır. AncakGameViewModel
yalnızca bir kez oluşturulur ve her çağrı için yeniden oluşturulmaz veya yok edilmez.
I/GameFragment: Called ViewModelProviders.of I/GameViewModel: GameViewModel created! I/GameFragment: Called ViewModelProviders.of I/GameFragment: Called ViewModelProviders.of I/GameFragment: Called ViewModelProviders.of
- Oyundan çıkın veya oyun parçasından çıkın.
GameFragment
yok edildi. İlişkiliGameViewModel
de yok edilir ve geri çağırmaonCleared()
çağrılır.
I/GameFragment: Called ViewModelProviders.of I/GameViewModel: GameViewModel created! I/GameFragment: Called ViewModelProviders.of I/GameFragment: Called ViewModelProviders.of I/GameFragment: Called ViewModelProviders.of I/GameViewModel: GameViewModel destroyed!
ViewModel
, yapılandırma değişikliklerinden etkilenmez. Bu nedenle, yapılandırma değişikliklerinden etkilenmemesi gereken veriler için iyi bir yerdir:
- Ekranda gösterilecek verileri ve bu verileri işleyecek kodu
ViewModel
içine yerleştirin. - Etkinlikler, parçalar ve görünümler yapılandırma değişikliklerinden etkilenmediği için
ViewModel
hiçbir zaman parçalara, etkinliklere veya görünümlere referans içermemelidir.
Karşılaştırma için, GameFragment
kullanıcı arayüzü verilerinin ViewModel
eklenmeden önce ve eklendikten sonra başlangıç uygulamasında nasıl işlendiği aşağıda açıklanmıştır:ViewModel
ViewModel
eklemeden önce:
Uygulamanın ekran döndürme gibi bir yapılandırma değişikliği geçirdiği sırada oyun parçası yok edilir ve yeniden oluşturulur. Veriler kaybolur.ViewModel
ekleyip oyun fragmentinin kullanıcı arayüzü verileriniViewModel
içine taşıdıktan sonra:
Fragmentin göstermesi gereken tüm veriler artıkViewModel
. Uygulama yapılandırma değişikliği geçirdiğindeViewModel
korunur ve veriler saklanır.
Bu görevde, uygulamanın kullanıcı arayüzü verilerini, verileri işleme yöntemleriyle birlikte GameViewModel
sınıfına taşırsınız. Bunu, yapılandırma değişiklikleri sırasında verilerin saklanması için yaparsınız.
1. adım: Veri alanlarını ve veri işlemeyi ViewModel'e taşıyın
Aşağıdaki veri alanlarını ve yöntemlerini GameFragment
konumundan GameViewModel
konumuna taşıyın:
word
,score
vewordList
veri alanlarını taşıyın.word
vescore
öğelerininprivate
olmadığından emin olun.
Görünümlere referans içerdiğinden bağlama değişkeniGameFragmentBinding
taşınmamalıdır. Bu değişken, düzeni genişletmek, tıklama dinleyicilerini ayarlamak ve verileri ekranda göstermek için kullanılır. Bunlar, parçanın sorumluluklarıdır.resetList()
venextWord()
yöntemlerini taşıyın. Bu yöntemler, ekranda hangi kelimenin gösterileceğine karar verir.onCreateView()
yönteminin içinden, yöntem çağrılarınıresetList()
venextWord()
ileGameViewModel
'ininit
bloğuna taşıyın.
Bu yöntemlerinit
bloğunda olmalıdır. Bunun nedeni,ViewModel
oluşturulduğunda kelime listesini sıfırlamanız gerektiğidir. Parça her oluşturulduğunda sıfırlamanız gerekmez. Günlük ifadesiniGameFragment
öğesinininit
bloğunda silebilirsiniz.
GameFragment
içindeki onSkip()
ve onCorrect()
tıklama işleyicileri, verileri işleme ve kullanıcı arayüzünü güncelleme kodunu içerir. Kullanıcı arayüzünü güncelleme kodu parçada kalmalı ancak verileri işleme kodu ViewModel
taşınmalıdır.
Şimdilik, her iki yere de aynı yöntemleri yerleştirin:
onSkip()
veonCorrect()
yöntemleriniGameFragment
'denGameViewModel
'ye kopyalayın.GameViewModel
içinde,onSkip()
veonCorrect()
yöntemlerininprivate
olmadığından emin olun. Çünkü bu yöntemlere parçadan referans vereceksiniz.
Yeniden düzenlemeden sonra GameViewModel
sınıfının kodu:
class GameViewModel : ViewModel() {
// The current word
var word = ""
// The current score
var score = 0
// The list of words - the front of the list is the next word to guess
private lateinit var wordList: MutableList<String>
/**
* Resets the list of words and randomizes the order
*/
private fun resetList() {
wordList = mutableListOf(
"queen",
"hospital",
"basketball",
"cat",
"change",
"snail",
"soup",
"calendar",
"sad",
"desk",
"guitar",
"home",
"railway",
"zebra",
"jelly",
"car",
"crow",
"trade",
"bag",
"roll",
"bubble"
)
wordList.shuffle()
}
init {
resetList()
nextWord()
Log.i("GameViewModel", "GameViewModel created!")
}
/**
* Moves to the next word in the list
*/
private fun nextWord() {
if (!wordList.isEmpty()) {
//Select and remove a word from the list
word = wordList.removeAt(0)
}
updateWordText()
updateScoreText()
}
/** Methods for buttons presses **/
fun onSkip() {
if (!wordList.isEmpty()) {
score--
}
nextWord()
}
fun onCorrect() {
if (!wordList.isEmpty()) {
score++
}
nextWord()
}
override fun onCleared() {
super.onCleared()
Log.i("GameViewModel", "GameViewModel destroyed!")
}
}
Aşağıda, yeniden düzenlemeden sonraki GameFragment
sınıfının kodu verilmiştir:
/**
* Fragment where the game is played
*/
class GameFragment : Fragment() {
private lateinit var binding: GameFragmentBinding
private lateinit var viewModel: GameViewModel
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
// Inflate view and obtain an instance of the binding class
binding = DataBindingUtil.inflate(
inflater,
R.layout.game_fragment,
container,
false
)
Log.i("GameFragment", "Called ViewModelProviders.of")
viewModel = ViewModelProviders.of(this).get(GameViewModel::class.java)
binding.correctButton.setOnClickListener { onCorrect() }
binding.skipButton.setOnClickListener { onSkip() }
updateScoreText()
updateWordText()
return binding.root
}
/** Methods for button click handlers **/
private fun onSkip() {
if (!wordList.isEmpty()) {
score--
}
nextWord()
}
private fun onCorrect() {
if (!wordList.isEmpty()) {
score++
}
nextWord()
}
/** Methods for updating the UI **/
private fun updateWordText() {
binding.wordText.text = word
}
private fun updateScoreText() {
binding.scoreText.text = score.toString()
}
}
2. adım: GameFragment'teki tıklama işleyicileri ve veri alanlarıyla ilgili referansları güncelleyin
GameFragment
bölümündeonSkip()
veonCorrect()
yöntemlerini güncelleyin. Puanı güncellemek için kodu kaldırın ve bunun yerineviewModel
üzerinde ilgilionSkip()
veonCorrect()
yöntemlerini çağırın.nextWord()
yönteminiViewModel
'ye taşıdığınız için oyun parçası artık bu yönteme erişemiyor.
GameFragment
'dekionSkip()
veonCorrect()
yöntemlerindenextWord()
çağrısınıupdateScoreText()
veupdateWordText()
ile değiştirin. Bu yöntemler, verileri ekranda gösterir.
private fun onSkip() {
viewModel.onSkip()
updateWordText()
updateScoreText()
}
private fun onCorrect() {
viewModel.onCorrect()
updateScoreText()
updateWordText()
}
GameFragment
içinde,score
veword
değişkenleriniGameViewModel
değişkenlerini kullanacak şekilde güncelleyin. Çünkü bu değişkenler artıkGameViewModel
içinde yer alıyor.
private fun updateWordText() {
binding.wordText.text = viewModel.word
}
private fun updateScoreText() {
binding.scoreText.text = viewModel.score.toString()
}
GameViewModel
içinde,nextWord()
yöntemindeupdateWordText()
veupdateScoreText()
yöntemlerine yapılan çağrıları kaldırın. Bu yöntemler artıkGameFragment
üzerinden çağrılıyor.- Uygulamayı oluşturun ve hata olmadığından emin olun. Hata varsa projeyi temizleyip yeniden oluşturun.
- Uygulamayı çalıştırın ve oyunu bazı kelimelerle oynayın. Oyun ekranındayken cihazı döndürün. Yön değişikliğinden sonra mevcut puanın ve mevcut kelimenin korunduğuna dikkat edin.
Tebrikler! Artık uygulamanızın tüm verileri ViewModel
içinde depolandığı için yapılandırma değişiklikleri sırasında korunur.
Bu görevde, End Game (Oyunu Bitir) düğmesi için tıklama işleyiciyi uygulayacaksınız.
GameFragment
içindeonEndGame()
adlı bir yöntem ekleyin. Kullanıcı Oyunu Bitir düğmesine dokunduğundaonEndGame()
yöntemi çağrılır.
private fun onEndGame() {
}
GameFragment
içinde,onCreateView()
yönteminde Anladım ve Atla düğmeleri için tıklama dinleyicilerini ayarlayan kodu bulun. Bu iki satırın hemen altında, End Game (Oyunu Bitir) düğmesi için bir tıklama işleyicisi ayarlayın. Bağlama değişkenini (binding
) kullanın. Tıklama işleyicisinin içindeonEndGame()
yöntemini çağırın.
binding.endGameButton.setOnClickListener { onEndGame() }
GameFragment
içinde, uygulamada puan ekranına gitmek içingameFinished()
adlı bir yöntem ekleyin. Safe Args'ı kullanarak puanı bağımsız değişken olarak iletin.
/**
* Called when the game is finished
*/
private fun gameFinished() {
Toast.makeText(activity, "Game has just finished", Toast.LENGTH_SHORT).show()
val action = GameFragmentDirections.actionGameToScore()
action.score = viewModel.score
NavHostFragment.findNavController(this).navigate(action)
}
onEndGame()
yöntemindegameFinished()
yöntemini çağırın.
private fun onEndGame() {
gameFinished()
}
- Uygulamayı çalıştırın, oyunu oynayın ve bazı kelimeler arasında geçiş yapın. Oyunu Bitir düğmesine dokunun. Uygulamanın puan ekranına gittiğini ancak nihai puanın gösterilmediğini fark edin. Bunu bir sonraki görevde düzelteceksiniz.
Kullanıcı oyunu bitirdiğinde ScoreFragment
puanı göstermez. ScoreFragment
tarafından gösterilecek puanı tutacak bir ViewModel
istiyorsunuz. Fabrika yöntemi kalıbını kullanarak ViewModel
başlatma sırasında puan değerini iletirsiniz.
Fabrika yöntemi deseni, nesne oluşturmak için fabrika yöntemlerini kullanan bir yapısal tasarım desenidir. Fabrika yöntemi, aynı sınıfın bir örneğini döndüren bir yöntemdir.
Bu görevde, puan parçası için parametreli bir oluşturucu ve ViewModel
öğesini oluşturmak için bir fabrika yöntemi içeren bir ViewModel
oluşturacaksınız.
score
paketi altındaScoreViewModel
adlı yeni bir Kotlin sınıfı oluşturun. Bu sınıf, puan fragment'ı içinViewModel
olacak.ScoreViewModel
sınıfınıViewModel.
sınıfından genişletin. Son puan için bir oluşturucu parametresi ekleyin. Günlük ifadesi içeren birinit
bloğu ekleyin.ScoreViewModel
sınıfına, son puanı kaydetmek içinscore
adlı bir değişken ekleyin.
class ScoreViewModel(finalScore: Int) : ViewModel() {
// The final score
var score = finalScore
init {
Log.i("ScoreViewModel", "Final score is $finalScore")
}
}
score
paketi altındaScoreViewModelFactory
adlı başka bir Kotlin sınıfı oluşturun. Bu sınıf,ScoreViewModel
nesnesinin oluşturulmasından sorumludur.ScoreViewModelFactory
dersiniViewModelProvider.Factory
tarihinden itibaren uzatın. Nihai skor için bir oluşturucu parametresi ekleyin.
class ScoreViewModelFactory(private val finalScore: Int) : ViewModelProvider.Factory {
}
ScoreViewModelFactory
içinde Android Studio, uygulanmamış bir soyut üye hakkında hata gösteriyor. Hatayı düzeltmek içincreate()
yöntemini geçersiz kılın.create()
yönteminde, yeni oluşturulanScoreViewModel
nesnesini döndürün.
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(ScoreViewModel::class.java)) {
return ScoreViewModel(finalScore) as T
}
throw IllegalArgumentException("Unknown ViewModel class")
}
ScoreFragment
içindeScoreViewModel
veScoreViewModelFactory
için sınıf değişkenleri oluşturun.
private lateinit var viewModel: ScoreViewModel
private lateinit var viewModelFactory: ScoreViewModelFactory
ScoreFragment
içinde,onCreateView()
içinde,binding
değişkenini başlatma işleminden sonraviewModelFactory
değişkenini başlatın.ScoreViewModelFactory
kullanın. Nihai puanı, bağımsız değişken paketindenScoreViewModelFactory()
için oluşturucu parametresi olarak iletin.
viewModelFactory = ScoreViewModelFactory(ScoreFragmentArgs.fromBundle(arguments!!).score)
onCreateView(
içinde,viewModelFactory
başlatıldıktan sonraviewModel
nesnesini başlatın.ViewModelProviders.of()
yöntemini çağırın, ilişkili puan parçası bağlamını veviewModelFactory
öğesini iletin. Bu işlem,viewModelFactory
sınıfında tanımlanan fabrika yöntemi kullanılarakScoreViewModel
nesnesini oluşturur.
.
viewModel = ViewModelProviders.of(this, viewModelFactory)
.get(ScoreViewModel::class.java)
onCreateView()
yönteminde,viewModel
başlatıldıktan sonrascoreText
görünümünün metniniScoreViewModel
içinde tanımlanan nihai puana ayarlayın.
binding.scoreText.text = viewModel.score.toString()
- Uygulamanızı çalıştırın ve oyunu oynayın. Kelimelerin bir kısmını veya tamamını inceleyip Oyunu Bitir'e dokunun. Puan snippet'inde artık nihai puanın gösterildiğini fark edeceksiniz.
- İsteğe bağlı:
ScoreViewModel
üzerinde filtreleme yaparak Logcat'tekiScoreViewModel
günlüklerini kontrol edin. Puan değeri gösterilmelidir.
2019-02-07 10:50:18.328 com.example.android.guesstheword I/ScoreViewModel: Final score is 15
Bu görevde, ViewModel
kullanmak için ScoreFragment
uyguladınız. Ayrıca, ViewModelFactory
arayüzünü kullanarak ViewModel
için parametreli bir oluşturucu oluşturmayı da öğrendiniz.
Tebrikler! Uygulamanızın mimarisini, Android Architecture Components'tan birini kullanacak şekilde değiştirdiniz, ViewModel
. Uygulamanın yaşam döngüsü sorununu çözdünüz ve artık oyunun verileri yapılandırma değişikliklerinden etkilenmiyor. Ayrıca, ViewModelFactory
arayüzünü kullanarak ViewModel
oluşturmak için parametreli bir oluşturucu oluşturmayı da öğrendiniz.
Android Studio projesi: GuessTheWord
- Android uygulama mimarisi yönergelerinde, farklı sorumluluklara sahip sınıfların ayrılması önerilir.
- Kullanıcı arayüzü denetleyicisi,
Activity
veyaFragment
gibi kullanıcı arayüzü tabanlı bir sınıftır. Kullanıcı arayüzü denetleyicileri yalnızca kullanıcı arayüzü ve işletim sistemi etkileşimlerini işleyen mantığı içermeli, kullanıcı arayüzünde gösterilecek verileri içermemelidir. Bu verileriViewModel
içine yerleştirin. ViewModel
sınıfı, kullanıcı arayüzüyle ilgili verileri depolar ve yönetir.ViewModel
sınıfı, verilerin ekran döndürme gibi yapılandırma değişikliklerinden etkilenmemesini sağlar.ViewModel
, önerilen Android Mimari Bileşenleri'nden biridir.ViewModelProvider.Factory
,ViewModel
nesnesi oluşturmak için kullanabileceğiniz bir arayüzdür.
Aşağıdaki tabloda, kullanıcı arayüzü denetleyicileri ile bunlar için veri tutan ViewModel
örnekleri karşılaştırılmaktadır:
Kullanıcı arayüzü denetleyicisi | ViewModel |
Bu codelab'de oluşturduğunuz |
|
Kullanıcı arayüzünde gösterilecek veri içermiyor. | Kullanıcı arayüzü denetleyicisinin kullanıcı arayüzünde gösterdiği verileri içerir. |
Verileri görüntüleme kodu ve tıklama dinleyicileri gibi kullanıcı etkinliği kodu içerir. | Veri işleme için kod içerir. |
Her yapılandırma değişikliği sırasında yok edilir ve yeniden oluşturulur. | Yalnızca ilişkili kullanıcı arayüzü denetleyicisi kalıcı olarak kaldırıldığında (ör. bir etkinlik için etkinlik tamamlandığında veya bir parça için parça ayrıldığında) yok edilir. |
Görünümler içerir. | Yapılandırma değişikliklerinden sonra etkinlikler, parçalar veya görünümler korunmaz ancak |
İlişkili | İlişkili kullanıcı arayüzü denetleyicisine herhangi bir referans içermez. |
Udacity kursu:
Android geliştirici belgeleri:
- ViewModel'e Genel Bakış
- Yaşam Döngüsüne Duyarlı Bileşenlerle Yaşam Döngülerini İşleme
- Uygulama mimarisi kılavuzu
ViewModelProvider
ViewModelProvider.Factory
Diğer:
- MVVM (model-view-viewmodel) mimari kalıbı.
- İlgi alanlarının ayrılması (SoC) tasarım ilkesi
- Fabrika yöntemi kalıbı
Bu bölümde, bir eğitmenin yönettiği kurs kapsamında bu codelab'i tamamlayan öğrenciler için olası ödevler listelenmektedir. Eğitmen, aşağıdakileri yapmalıdır:
- Gerekirse ödev atayın.
- Öğrencilere ev ödevi ödevlerini nasıl göndereceklerini bildirin.
- Ödevlere not verin.
Eğitmenler bu önerileri istedikleri kadar kullanabilir ve uygun olduğunu düşündükleri diğer ödevleri verebilirler.
Bu codelab'i kendi başınıza tamamlıyorsanız bilginizi test etmek için bu ödevleri kullanabilirsiniz.
Bu soruları yanıtlayın
1. Soru
Cihaz yapılandırması değişikliği sırasında veri kaybını önlemek için uygulama verilerini hangi sınıfa kaydetmelisiniz?
ViewModel
LiveData
Fragment
Activity
2. Soru
Bir ViewModel
hiçbir zaman parçalara, etkinliklere veya görünümlere referans içermemelidir. Doğru mu yanlış mı?
- Doğru
- Yanlış
3. Soru
ViewModel
ne zaman yok edilir?
- İlişkili kullanıcı arayüzü denetleyicisi, cihaz yönü değişikliği sırasında yok edilip yeniden oluşturulduğunda.
- Yön değişikliğinde
- İlişkili kullanıcı arayüzü denetleyicisi tamamlandığında (etkinlikse) veya ayrıldığında (parçaysa).
- Kullanıcı Geri düğmesine bastığında.
4. Soru
ViewModelFactory
arayüzü ne için kullanılır?
ViewModel
nesnesi oluşturma.- Yön değişiklikleri sırasında verileri koruma.
- Ekranda gösterilen veriler yenilenir.
- Uygulama verileri değiştirildiğinde bildirim alma
Sonraki derse başlayın:
Bu kurstaki diğer codelab'lerin bağlantılarını Android Kotlin Hakkında Temel Bilgiler codelab'leri açılış sayfasında bulabilirsiniz.