ã泚æ ãã®èšäºã¯ã Mladen Rakonjacã®äžé£ã®èšäºã®ç¿»èš³ã§ãïŒèšäºæ¥ä»ïŒ 2017幎9æ23æ¥ïŒã Github SemperPeritusã®æåã®éšåãèªã¿å§ããã®ã§ãæ®ãã®éšåãäœããã®çç±ã§ç¿»èš³ãããŠããªãããšãçºèŠããŸããã ãããã£ãŠãç§ã¯ããªãã®æ³šæã«ç¬¬äºéšããããããŸãã èšäºã¯èšå€§ã§ããããšãå€æããŸããã
ãAndroid Studio 3.0ã§Androidã®éçºã®ãã¹ãŠãã«ããŒãããããžã§ã¯ããèŠã€ããã®ã¯éåžžã«é£ããã®ã§ãç§ã¯ãããæžãããšã«ããŸãããã
ãã®èšäºã§ã¯ã以äžãåæããŸãã
- Android Studio 3ããŒã¿1 ããŒã1
- Kotlinããã°ã©ãã³ã°èšèªããŒã1
- ãã«ããªãã·ã§ã³ããŒã1
- ConstraintLayout ããŒã1
- ããŒã¿ãã€ã³ãã£ã³ã°ã©ã€ãã©ãªããŒã1
- MVVMã¢ãŒããã¯ãã£+ãªããžããª+ AndroidãããŒãžã£ãŒã©ãããŒãã¿ãŒã³
- RxJava2ãšãããããŒã3ã¢ãŒããã¯ãã£ã§ã©ã®ããã«åœ¹ç«ã€ã
- Dagger 2.11ãäŸåæ§æ³šå ¥ãšã¯äœã§ããããã®ããŒã4ã䜿çšããçç±
- ã¬ãããã£ããïŒRx Java2ã䜿çšïŒ
- ã«ãŒã ïŒRx Java2ã䜿çšïŒ
MVVMã¢ãŒããã¯ãã£+ãªããžããª+ AndroidãããŒãžã£ãŒã©ãããŒãã¿ãŒã³
Androidã®äžçã®ã¢ãŒããã¯ãã£ã«ã€ããŠäžèš
ããªãé·ãéãAndroidéçºè ã¯ãããžã§ã¯ãã§ã¢ãŒããã¯ãã£ã䜿çšããŠããŸããã éå»3幎éã§ãAndroidéçºè ã®ã³ââãã¥ããã£ã§åœŒå¥³ã®åšãã«å€ãã®èªå€§å®£äŒãåºãŠããŸããã God Activityã®æ代ã¯éãå»ããGoogleã¯Android Architecture Blueprintsãªããžããªãå ¬éããŸãããããã«ã¯ãããŸããŸãªã¢ãŒããã¯ãã£ã¢ãããŒãã«é¢ããå€ãã®äŸãšæ瀺ãå«ãŸããŠããŸãã æåŸã«ãGoogle IO '17ã§ã圌ãã¯Android Architecture Componentsãå°å ¥ããŸãããããã¯ãããã¯ãªãŒã³ãªã³ãŒãã®äœæãšã¢ããªã±ãŒã·ã§ã³ã®æ¹åãæ¯æŽããããã«èšèšãããã©ã€ãã©ãªã®ã³ã¬ã¯ã·ã§ã³ã§ãã ã³ã³ããŒãã³ãã¯ããããã®ãã¹ãŠã䜿çšããããšãã1ã€ã ãã䜿çšããããšãã§ãããšèšããŸãã ãããããããã¯ãã¹ãŠéåžžã«æçšã§ããããšãããããŸããã ããã«æ¬æãšä»¥äžã®éšåã§ãããã䜿çšããŸãã ãŸããã³ãŒãã®åé¡ã«åãçµã¿ã次ã«ãããã®ã³ã³ããŒãã³ããšã©ã€ãã©ãªã䜿çšããŠãªãã¡ã¯ã¿ãªã³ã°ãã解決ãã¹ãåé¡ã確èªããŸãã
GUIã³ãŒããå ±æãã2ã€ã®äž»èŠãªã¢ãŒããã¯ãã£ãã¿ãŒã³ããããŸãã
- MVP
- MVVM
ã©ã¡ããè¯ãããèšãã®ã¯é£ããã§ãã äž¡æ¹ãè©ŠããŠæ±ºå®ããå¿ èŠããããŸãã ã©ã€ããµã€ã¯ã«å¯Ÿå¿ã®ã³ã³ããŒãã³ãã䜿çšããMVVMã奜ã¿ãããã«ã€ããŠæžããŸãã MVPã䜿çšããããšããªãå Žåã¯ãMediumã§ããã«é¢ããå€ãã®åªããèšäºããããŸãã
MVVMãã¿ãŒã³ãšã¯äœã§ããïŒ
MVVMã¯ãModel-View-ViewModelãšããŠæ¡åŒµãããã¢ãŒããã¯ãã£ãã¿ãŒã³ã§ãã ãã®ååã¯éçºè ãæ··ä¹±ããããšæããŸãã ç§ã圌ã®ååãæãã€ããã®ã§ããã°ã ViewModelã¯ViewãšModelãæ¥ç¶ããŠããããã ViewModelã View-ViewModel-ModelãšåŒã³ãŸãã
ãã¥ãŒã¯ã ã¢ã¯ãã£ãã㣠ã ãã©ã°ã¡ã³ãããŸãã¯ãã®ä»ã®ã«ã¹ã¿ã ãã¥ãŒïŒ Androidã«ã¹ã¿ã ãã¥ãŒ ïŒã®æœè±¡åã§ãã ãã®ãã¥ãŒãšAndroidãã¥ãŒãæ··åããªãããšãéèŠã§ãã ãã¥ãŒã¯éŠ¬é¹¿ããŠããã¹ãã§ãããããã«ããžãã¯ãæžãã¹ãã§ã¯ãããŸããã ãã¥ãŒã«ããŒã¿ãå«ããããšã¯ã§ããŸããã ViewModelã€ã³ã¹ã¿ã³ã¹ãžã®åç §ãšã Viewãå¿ èŠãšãããã¹ãŠã®ããŒã¿ãããããæ ŒçŽããå¿ èŠããããŸãã ããã«ã ãã¥ãŒã¯ãã®ããŒã¿ãç£èŠããå¿ èŠãããã ViewModelã®ããŒã¿ãå€æŽããããšã¬ã€ã¢ãŠããå€æŽãããå¿ èŠããããŸãã èŠçŽãããšã Viewã¯æ¬¡ã®åœ¹å²ãæãããŸããããŸããŸãªããŒã¿ãšç¶æ ã®ã¬ã€ã¢ãŠããã¥ãŒã
ViewModelã¯ãããŒã¿ãšããžãã¯ãå«ãã¯ã©ã¹ã®æœè±¡åã§ãããã®ããŒã¿ãåä¿¡ããã¿ã€ãã³ã°ãšè¡šç€ºããã¿ã€ãã³ã°ã§ãã ViewModelã¯çŸåšã®ç¶æ ãä¿åããŸã ã ViewModelã¯ã1ã€ä»¥äžã®Modelãžã®ãªã³ã¯ãä¿åããããããããã¹ãŠã®ããŒã¿ãåãåããŸãã ããšãã°ãããŒã¿ãã©ãããæ¥ãã®ããããŒã¿ããŒã¹ããæ¥ãã®ãããµãŒããŒããæ¥ãã®ããç¥ãã¹ãã§ã¯ãããŸããã ããã«ã ViewModelã¯Viewã«ã€ããŠäœãç¥ãå¿ èŠããããŸããã ããã«ã ViewModelã¯Androidãã¬ãŒã ã¯ãŒã¯ã«ã€ããŠäœãç¥ããªãã¯ãã§ãã
Modelã¯ã ViewModelã®ããŒã¿ãæºåããã¬ã€ã€ãŒã®æœè±¡åã§ãã ããã¯ããµãŒããŒããããŒã¿ãåä¿¡ããŠââãã£ãã·ã¥ããããããŒã«ã«ããŒã¿ããŒã¹ã«ä¿åããã¯ã©ã¹ã§ãã ãããã¯ãUserãCarãSquareãåçŽã«ããŒã¿ãä¿åããä»ã®ã¢ãã«ã¯ã©ã¹ãšåãã¯ã©ã¹ã§ã¯ãªãããšã«æ³šæããŠãã ããã ååãšããŠãããã¯ãªããžããªãã³ãã¬ãŒãã®å®è£ ã§ãããåŸã§æ€èšããŸãã ã¢ãã«ã¯ViewModelã«ã€ããŠäœãç¥ããªãã¯ãã§ãã
MVVM㯠ãæ£ããå®è£ ãããŠããã°ãã³ãŒããç Žå£ããŠãã¹ãããããããåªããæ¹æ³ã§ãã ããã«ããã SOLIDååã«åŸãããšãã§ãããããã³ãŒãã®ä¿å®ã容æã«ãªããŸãã
ã³ãŒãäŸ
次ã«ããããã©ã®ããã«æ©èœãããã瀺ãç°¡åãªäŸãäœæããŸãã
å§ããããã«ãè¡ãè¿ãç°¡åãªã¢ãã«ãäœæããŸãããïŒ
RepoModel.kt
class RepoModel { fun refreshData() : String { return "Some new data" } }
éåžžãããŒã¿ååŸã¯éåæåŒã³åºãã§ãããããåŸ æ©ããå¿ èŠããããŸãã ãããã·ãã¥ã¬ãŒãããããã«ãã¯ã©ã¹ã次ã®ããã«å€æŽããŸããã
RepoModel.kt
class RepoModel { fun refreshData(onDataReadyCallback: OnDataReadyCallback) { Handler().postDelayed({ onDataReadyCallback.onDataReady("new data") },2000) } } interface OnDataReadyCallback { fun onDataReady(data : String) }
onDataReady
ã¡ãœããã§
OnDataReadyCallback
ã€ã³ã¿ãŒãã§ã€ã¹ãäœæããŸããã ãããŠä»ã
refreshData
ã¡ãœããã¯
refreshData
ãå®è£ ïŒå®è£ ïŒããŠã
OnDataReadyCallback
ã åŸ æ©ãã·ãã¥ã¬ãŒãããã«ã¯ã
Handler
ã䜿çšããŸãã 2ç§ã«1
onDataReady
ã
OnDataReadyCallback
ã€ã³ã¿ãŒãã§ã€ã¹ãå®è£ ããã¯ã©ã¹ã§
onDataReady
ã¡ãœãããåŒã³åºãããŸãã
ViewModelãäœæããŸãããïŒ
MainViewModel.kt
class MainViewModel { var repoModel: RepoModel = RepoModel() var text: String = "" var isLoading: Boolean = false }
ã芧ã®
RepoModel
ã
text
ããã
RepoModel
ã
text
ã€ã³ã¹ã¿ã³ã¹ãããã³çŸåšã®ç¶æ ãæ ŒçŽããå€æ°
isLoading
ããããŸãã ããŒã¿ãååŸãã
refresh
ã¡ãœãããäœæããŸãããã
MainViewModel.kt
class MainViewModel { ... val onDataReadyCallback = object : OnDataReadyCallback { override fun onDataReady(data: String) { isLoading.set(false) text.set(data) } } fun refresh(){ isLoading.set(true) repoModel.refreshData(onDataReadyCallback) } }
refresh
ã¡ãœããã¯ãåŒæ°ã«
OnDataReadyCallback
å®è£ ããšã
RepoModel
ã§
refreshData
ãåŒã³åºããŸãã ããããŸãããã
object
ãšã¯äœã§ããïŒ ã€ã³ã¿ãŒãã§ã€ã¹ãå®è£ ãããããµãã¯ã©ã¹åããã«æ¡åŒµã¯ã©ã¹ãç¶æ¿ããå Žåã¯åžžã«ã ãªããžã§ã¯ã宣èšã䜿çšããŸã ã ãããŠããããå¿åã¯ã©ã¹ãšããŠäœ¿çšãããå Žåã¯ïŒ ãã®å Žåã ãªããžã§ã¯ãåŒã䜿çšããŠããŸã ïŒ
MainViewModel.kt
class MainViewModel { var repoModel: RepoModel = RepoModel() var text: String = "" var isLoading: Boolean = false fun refresh() { repoModel.refreshData( object : OnDataReadyCallback { override fun onDataReady(data: String) { text = data }) } }
refresh
ãåŒã³åºããšãã¯ããã¥ãŒãèªã¿èŸŒã¿ç¶æ ã«å€æŽããããŒã¿ãå°çã
isLoading
ã
false
ã«èšå®ãã
isLoading
ããã
false
ã
ãŸãã
text
ã
ObservableField<String>
ãããã³isLoading
on
ObservableField<Boolean>
ã ObservableField
ã¯ãObservableãªããžã§ã¯ããäœæãã代ããã«äœ¿çšã§ããããŒã¿ãã€ã³ãã£ã³ã°ã©ã€ãã©ãªã®ã¯ã©ã¹ã§ãããç£èŠãããªããžã§ã¯ããã©ããããŸãã
MainViewModel.kt
class MainViewModel { var repoModel: RepoModel = RepoModel() val text = ObservableField<String>() val isLoading = ObservableField<Boolean>() fun refresh(){ isLoading.set(true) repoModel.refreshData(object : OnDataReadyCallback { override fun onDataReady(data: String) { isLoading.set(false) text.set(data) } }) } }
varã®ä»£ããã«valã䜿çšããŠããããšã«æ³šæããŠãã ããããã£ãŒã«ãã®å€ã®ã¿ãå€æŽãããã£ãŒã«ãèªäœã¯å€æŽããªãããã§ãã ãŸããåæåããå Žåã¯ã次ã䜿çšããŸãã
initobserv.kt
val text = ObservableField("old data") val isLoading = ObservableField(false)
ããã¹ããšisLoadingãç£èŠã§ããããã«ã¬ã€ã¢ãŠããå€æŽããŸãããã éå§ããã«ã¯ã Repositoryã§ã¯ãªãMainViewModelããã€ã³ãããŸã ã
activity_main.xml
<data> <variable name="viewModel" type="me.mladenrakonjac.modernandroidapp.MainViewModel" /> </data>
次ã«ïŒ
- TextViewãå€æŽããŠãMainViewModelããã®ããã¹ããç£èŠããŸã
- isLoading trueã®å Žåã«ã®ã¿è¡šç€ºãããProgressBarãè¿œå ããŸã
- [è¿œå ]ãã¿ã³ãã¯ãªãã¯ãããšã MainViewModelããrefreshã¡ãœãããåŒã³åºããã isLoading falseã®å Žåã«ã®ã¿ã¯ãªãã¯å¯èœã«ãªããŸãã
main_activity.xml
... <TextView android:id="@+id/repository_name" android:text="@{viewModel.text}" ... /> ... <ProgressBar android:id="@+id/loading" android:visibility="@{viewModel.isLoading ? View.VISIBLE : View.GONE}" ... /> <Button android:id="@+id/refresh_button" android:onClick="@{() -> viewModel.refresh()}" android:clickable="@{viewModel.isLoading ? false : true}" /> ...
ããã§å®è¡ãããšã
View.VISIBLE and View.GONE cannot be used if View is not imported
ã
View.VISIBLE and View.GONE cannot be used if View is not imported
ã ããŠãã€ã³ããŒãããŸãããïŒ
main_activity.xml
<data> <import type="android.view.View"/> <variable name="viewModel" type="me.fleka.modernandroidapp.MainViewModel" /> </data>
OKãã¬ã€ã¢ãŠãã®å®æã§ãã ãã€ã³ãã£ã³ã°ãçµäºããŸãã åã«ãèšã£ãããã«ã
View
ã¯
ViewModel
ã€ã³ã¹ã¿ã³ã¹ãå¿ èŠã§ãã
MainActivity.kt
class MainActivity : AppCompatActivity() { lateinit var binding: ActivityMainBinding var mainViewModel = MainViewModel() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = DataBindingUtil.setContentView(this, R.layout.activity_main) binding.viewModel = mainViewModel binding.executePendingBindings() } }
æåŸã«ãå®è¡ã§ããŸã
å€ãããŒã¿ã æ°ããããŒã¿ã«çœ®ãæããããŠããããšãããããŸã ã
ããã¯åçŽãªMVVMã®äŸã§ãã
ãããã1ã€ã®åé¡ããããŸããç»é¢ãå€ããŸãããã
å€ãããŒã¿ãæ°ãã ããŒã¿ã眮ãæããŸãã ã ããã¯ã©ã®ããã«å¯èœã§ããïŒ ã¢ã¯ãã£ããã£ã®ã©ã€ããµã€ã¯ã«ãèŠãŠãã ããã
ã¢ã¯ãã£ããã£ã®ã©ã€ããµã€ã¯ã«
é»è©±ããªã³ã«ãããšãã¢ã¯ãã£ããã£ã®æ°ããã€ã³ã¹ã¿ã³ã¹ãäœæããã
onCreate()
ã¡ãœãããåŒã³åºãããŸããã ç§ãã¡ã®æŽ»åãèŠãŠãã ããïŒ
MainActivity.kt
class MainActivity : AppCompatActivity() { lateinit var binding: ActivityMainBinding var mainViewModel = MainViewModel() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = DataBindingUtil.setContentView(this, R.layout.activity_main) binding.viewModel = mainViewModel binding.executePendingBindings() } }
ã芧ã®ãšãããActivityã€ã³ã¹ã¿ã³ã¹ãäœæããããšã MainViewModelã€ã³ã¹ã¿ã³ã¹ãäœæãããŸãã ã©ãããããããåäœæãããMainActivityããšã«MainViewModelã®åãã€ã³ã¹ã¿ã³ã¹ãããã°ããã§ããïŒ
ã©ã€ããµã€ã¯ã«å¯Ÿå¿ã³ã³ããŒãã³ãã®æŠèŠ
ãªããªã å€ãã®éçºè ããã®åé¡ã«çŽé¢ããŠãããããAndroid FrameworkããŒã ã®éçºè ã¯ããã®åé¡ã解決ããããã«èšèšãããã©ã€ãã©ãªãäœæããããšã«ããŸããã ViewModelã¯ã©ã¹ã¯ãã®1ã€ã§ãã ããã¯ããã¹ãŠã®ViewModelãç¶æ¿ããã¯ã©ã¹ã§ãã
ã©ã€ããµã€ã¯ã«å¯Ÿå¿ã³ã³ããŒãã³ãã®ViewModelããMainViewModelãç¶æ¿ããŸãããã æåã«ã ã©ã€ããµã€ã¯ã«å¯Ÿå¿ã³ã³ããŒãã³ãã©ã€ãã©ãªãbuild.gradleãã¡ã€ã«ã«è¿œå ããå¿ èŠããããŸãã
build.gradle
dependencies { ... implementation "android.arch.lifecycle:runtime:1.0.0-alpha9" implementation "android.arch.lifecycle:extensions:1.0.0-alpha9" kapt "android.arch.lifecycle:compiler:1.0.0-alpha9"
MainViewModelãViewModel ã®ç¶æ¿è ã«ããŸã ã
MainViewModel.kt
package me.mladenrakonjac.modernandroidapp import android.arch.lifecycle.ViewModel class MainViewModel : ViewModel() { ... }
MainActivityã®onCreateïŒïŒã¡ãœããã¯æ¬¡ã®ããã«ãªããŸãã
MainActivity.kt
class MainActivity : AppCompatActivity() { lateinit var binding: ActivityMainBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = DataBindingUtil.setContentView(this, R.layout.activity_main) binding.viewModel = ViewModelProviders.of(this).get(MainViewModel::class.java) binding.executePendingBindings() } }
MainViewModelã®æ°ããã€ã³ã¹ã¿ã³ã¹ãäœæããªãã£ãããšã«æ³šæããŠãã ããã ViewModelProvidersã䜿çšããŠååŸããŸãã ViewModelProvidersã¯ã ViewModelProviderãååŸããã¡ãœãããæã€Utilityã¯ã©ã¹ã§ãã ããã¯ãã¹ãŠscopeã«é¢ãããã®ã§ãã ã¢ã¯ãã£ããã£ã§ViewModelProviders.ofïŒthisïŒãåŒã³åºããšã ViewModelã¯ããã®ã¢ã¯ãã£ããã£ãçããŠããéãïŒåäœæããã«ç Žæ£ããããŸã§ïŒçãç¶ããŸãã ãããã£ãŠãããããã©ã°ã¡ã³ãã§åŒã³åºããšããã©ã°ã¡ã³ããçããŠããéã ViewModelã¯çãç¶ããŸãã å³ãèŠãŠãã ããïŒ
ã¹ã³ãŒãã®ã©ã€ããµã€ã¯ã«
ViewModelProviderã¯ãæåã®åŒã³åºãã§æ°ããã€ã³ã¹ã¿ã³ã¹ãäœæããããã¢ã¯ãã£ããã£ãŸãã¯ãã©ã°ã¡ã³ããåäœæãããå Žåã«å€ãã€ã³ã¹ã¿ã³ã¹ãè¿ããŸãã
ãšæ··åããªãã§ãã ãã
MainViewModel::class.java
ã³ããªã³ã§ã¯ãåŸããªã
MainViewModel::class
ããã«ãã ãJavaã®Classãšã¯ç°ãªãKClassãè¿ãããŸãã .javaãèšè¿°ãããšãããã¥ã¡ã³ãã«ãããšïŒ
KClassã®ãã®ã€ã³ã¹ã¿ã³ã¹ã«å¯Ÿå¿ããJava ã¯ã©ã¹ã®ã€ã³ã¹ã¿ã³ã¹ãè¿ããŸã
ç»é¢ãå転ããããšã©ããªããèŠãŠã¿ãŸããã
ç»é¢ã®å転åãšåãããŒã¿ããããŸãã
ååã®èšäºã§ãã¢ããªã±ãŒã·ã§ã³ãGithubãªããžããªã®ãªã¹ããååŸããŠè¡šç€ºãããšè¿°ã¹ãŸããã ãããè¡ãã«ã¯ããªããžããªã®åœã®ãªã¹ããè¿ãgetRepositoriesé¢æ°ãè¿œå ããå¿ èŠããããŸãã
RepoModel.kt
class RepoModel { fun refreshData(onDataReadyCallback: OnDataReadyCallback) { Handler().postDelayed({ onDataReadyCallback.onDataReady("new data") },2000) } fun getRepositories(onRepositoryReadyCallback: OnRepositoryReadyCallback) { var arrayList = ArrayList<Repository>() arrayList.add(Repository("First", "Owner 1", 100 , false)) arrayList.add(Repository("Second", "Owner 2", 30 , true)) arrayList.add(Repository("Third", "Owner 3", 430 , false)) Handler().postDelayed({ onRepositoryReadyCallback.onDataReady(arrayList) },2000) } } interface OnDataReadyCallback { fun onDataReady(data : String) } interface OnRepositoryReadyCallback { fun onDataReady(data : ArrayList<Repository>) }
RepoModelããgetRepositoriesãåŒã³åºãMainViewModelã®ã¡ãœãããå¿ èŠã§ã ã
MainViewModel.kt
class MainViewModel : ViewModel() { ... var repositories = ArrayList<Repository>() fun refresh(){ ... } fun loadRepositories(){ isLoading.set(true) repoModel.getRepositories(object : OnRepositoryReadyCallback{ override fun onDataReady(data: ArrayList<Repository>) { isLoading.set(false) repositories = data } }) } }
æåŸã«ãRecyclerViewã§ãããã®ãªããžããªã衚瀺ããå¿ èŠããããŸãã ãããè¡ãã«ã¯ã以äžãè¡ãå¿ èŠããããŸãã
- ã¬ã€ã¢ãŠãrv_item_repository.xmlãäœæããŸã
- ã¬ã€ã¢ãŠãactivity_main.xmlã«RecyclerViewãè¿œå ããŸã
- RepositoryRecyclerViewAdapterãäœæãã
- recyclerviewã§ã¢ããã¿ãŒãã€ã³ã¹ããŒã«ãã
rv_item_repository.xmlãäœæããã«ã¯ãCardViewã©ã€ãã©ãªã䜿çšãããããbuild.gradleïŒã¢ããªïŒã«è¿œå ããå¿ èŠããããŸãã
implementation 'com.android.support:cardview-v7:26.0.1'
ããã¯æ¬¡ã®ãããªãã®ã§ãã
rv_item_repository.xml
<?xml version="1.0" encoding="utf-8"?> <layout 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"> <data> <import type="android.view.View" /> <variable name="repository" type="me.mladenrakonjac.modernandroidapp.uimodels.Repository" /> </data> <android.support.v7.widget.CardView android:layout_width="match_parent" android:layout_height="96dp" android:layout_margin="8dp"> <android.support.constraint.ConstraintLayout android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:id="@+id/repository_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginEnd="16dp" android:layout_marginStart="16dp" android:text="@{repository.repositoryName}" android:textSize="20sp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintHorizontal_bias="0.0" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintVertical_bias="0.083" tools:text="Modern Android App" /> <TextView android:id="@+id/repository_has_issues" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginEnd="16dp" android:layout_marginStart="16dp" android:layout_marginTop="8dp" android:text="@string/has_issues" android:textStyle="bold" android:visibility="@{repository.hasIssues ? View.VISIBLE : View.GONE}" app:layout_constraintBottom_toBottomOf="@+id/repository_name" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="1.0" app:layout_constraintStart_toEndOf="@+id/repository_name" app:layout_constraintTop_toTopOf="@+id/repository_name" app:layout_constraintVertical_bias="1.0" /> <TextView android:id="@+id/repository_owner" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginBottom="8dp" android:layout_marginEnd="16dp" android:layout_marginStart="16dp" android:text="@{repository.repositoryOwner}" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/repository_name" app:layout_constraintVertical_bias="0.0" tools:text="Mladen Rakonjac" /> <TextView android:id="@+id/number_of_starts" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="8dp" android:layout_marginEnd="16dp" android:layout_marginStart="16dp" android:layout_marginTop="8dp" android:text="@{String.valueOf(repository.numberOfStars)}" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="1" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/repository_owner" app:layout_constraintVertical_bias="0.0" tools:text="0 stars" /> </android.support.constraint.ConstraintLayout> </android.support.v7.widget.CardView> </layout>
次ã®ã¹ãããã¯ãRecyclerViewãactivity_main.xmlã«è¿œå ããããšã§ãã ãããè¡ãåã«ãRecyclerViewã©ã€ãã©ãªãè¿œå ããŠãã ããïŒ
implementation 'com.android.support:recyclerview-v7:26.0.1'
activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <layout 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"> <data> <import type="android.view.View"/> <variable name="viewModel" type="me.fleka.modernandroidapp.MainViewModel" /> </data> <android.support.constraint.ConstraintLayout android:layout_width="match_parent" android:layout_height="match_parent" tools:context="me.fleka.modernandroidapp.MainActivity"> <ProgressBar android:id="@+id/loading" android:layout_width="48dp" android:layout_height="48dp" android:indeterminate="true" android:visibility="@{viewModel.isLoading ? View.VISIBLE : View.GONE}" app:layout_constraintBottom_toTopOf="@+id/refresh_button" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <android.support.v7.widget.RecyclerView android:id="@+id/repository_rv" android:layout_width="0dp" android:layout_height="0dp" android:indeterminate="true" android:visibility="@{viewModel.isLoading ? View.GONE : View.VISIBLE}" app:layout_constraintBottom_toTopOf="@+id/refresh_button" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" tools:listitem="@layout/rv_item_repository" /> <Button android:id="@+id/refresh_button" android:layout_width="160dp" android:layout_height="40dp" android:layout_marginBottom="8dp" android:layout_marginEnd="8dp" android:layout_marginStart="8dp" android:layout_marginTop="8dp" android:onClick="@{() -> viewModel.loadRepositories()}" android:clickable="@{viewModel.isLoading ? false : true}" android:text="Refresh" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintVertical_bias="1.0" /> </android.support.constraint.ConstraintLayout> </layout>
äžéšã®TextViewèŠçŽ ãåé€ãããã¿ã³ãrefreshã§ã¯ãªãloadRepositoriesãèµ·åããããšã«æ³šæããŠãã ããã
button.xml
<Button android:id="@+id/refresh_button" android:onClick="@{() -> viewModel.loadRepositories()}" ... />
äžèŠãªå Žåã¯ã MainViewModelããrefreshã¡ãœãããã RepoModelããrefreshDataãåé€ããŸãããã
次ã«ãRecyclerViewã®ã¢ããã¿ãŒãäœæããå¿ èŠããããŸãã
RepositoryRecyclerViewAdapter.kt
class RepositoryRecyclerViewAdapter(private var items: ArrayList<Repository>, private var listener: OnItemClickListener) : RecyclerView.Adapter<RepositoryRecyclerViewAdapter.ViewHolder>() { override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): ViewHolder { val layoutInflater = LayoutInflater.from(parent?.context) val binding = RvItemRepositoryBinding.inflate(layoutInflater, parent, false) return ViewHolder(binding) } override fun onBindViewHolder(holder: ViewHolder, position: Int) = holder.bind(items[position], listener) override fun getItemCount(): Int = items.size interface OnItemClickListener { fun onItemClick(position: Int) } class ViewHolder(private var binding: RvItemRepositoryBinding) : RecyclerView.ViewHolder(binding.root) { fun bind(repo: Repository, listener: OnItemClickListener?) { binding.repository = repo if (listener != null) { binding.root.setOnClickListener({ _ -> listener.onItemClick(layoutPosition) }) } binding.executePendingBindings() } } }
ViewHolderã¯Viewã§ã¯ãªãRvItemRepositoryBindingåã®ã€ã³ã¹ã¿ã³ã¹ãåããããåèŠçŽ ã®ViewHolderã«ããŒã¿ãã€ã³ãã£ã³ã°ãå®è£ ã§ããããšã«æ³šæããŠãã ããã åäžè¡æ©èœïŒ1è¡ïŒã«å°ããªãã§ãã ããã
override fun onBindViewHolder(holder: ViewHolder, position: Int) = holder.bind(items[position], listener)
ããã¯ã以äžã®çããšã³ããªãŒã§ãã
override fun onBindViewHolder(holder: ViewHolder, position: Int){ return holder.bind(items[position], listener) }
ãããŠã items [position]ã¯ã€ã³ããã¯ã¹æŒç®åã®å®è£ ã§ãã items.getïŒpositionïŒã«äŒŒãŠããŸãã
ããªããæ··ä¹±ããããããããªãå¥ã®è¡ïŒ
binding.root.setOnClickListener({ _ -> listener.onItemClick(layoutPosition) })
䜿çšããŠããªãå Žåã¯ããã©ã¡ãŒã¿ãŒã_ã«çœ®ãæããããšãã§ããŸãã ããã§ãã
ã¢ããã¿ãäœæããŸãããã MainActivityã®recyclerViewã«ã¯ãŸã é©çšããŠããŸããã
MainActivity.kt
class MainActivity : AppCompatActivity(), RepositoryRecyclerViewAdapter.OnItemClickListener { lateinit var binding: ActivityMainBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = DataBindingUtil.setContentView(this, R.layout.activity_main) val viewModel = ViewModelProviders.of(this).get(MainViewModel::class.java) binding.viewModel = viewModel binding.executePendingBindings() binding.repositoryRv.layoutManager = LinearLayoutManager(this) binding.repositoryRv.adapter = RepositoryRecyclerViewAdapter(viewModel.repositories, this) } override fun onItemClick(position: Int) { TODO("not implemented") //To change body of created functions use File | Settings | File Templates. } }
ã¢ããªã±ãŒã·ã§ã³ãå®è¡ãã
ããã¯å€ã§ãã ã©ãããã®ïŒ
- ã¢ã¯ãã£ããã£ãäœæããããããäºå®äžç©ºã®ãªããžããªã§æ°ããã¢ããã¿ãäœæãããŸãã
- ãã¿ã³ãæŒã
- loadRepositoriesãåŒã³åºããé²è¡ç¶æ³ã衚瀺
- 2ç§åŸããªããžããªãååŸãããé²è¡ç¶æ³ã¯é衚瀺ã«ãªããŸããã衚瀺ãããŸããã ããã¯ã notifyDataSetChangedãã¢ããã¿ãŒã§åŒã³åºãããªãããã§ãã
- ç»é¢ãå転ããããšãæ°ããã¢ã¯ãã£ããã£ãäœæãããŸãããã®ããã repositoriesãã©ã¡ãŒã¿ãŒãšããŒã¿ãå«ãæ°ããã¢ããã¿ãŒãäœæãããŸã
ãããã£ãŠã MainViewModelã¯MainActivityã«æ°ããã¢ã€ãã ãéç¥ããå¿ èŠãããããã notifyDataSetChangedãåŒã³åºãããšãã§ããŸããïŒ
ã§ããŸããã
ããã¯æ¬åœã«éèŠã§ããMainViewModelã¯MainActivityããŸã£ããç¥ããªãã¯ãã§ãã
MainActivityã¯MainViewModelã®ã€ã³ã¹ã¿ã³ã¹ãæã£ãŠãããããå€æŽããªãã¹ã³ãã ã¢ããã¿ãŒã«å€æŽãéç¥ããå¿ èŠããããŸãã
ãããããããè¡ãæ¹æ³ã¯ïŒ
ãªããžããªãŒã芳å¯ã§ãããããããŒã¿ãå€æŽããåŸãã¢ããã¿ãŒãå€æŽã§ããŸãã
ãã®æ±ºå®ã®äœãåé¡ã«ãªã£ãŠããŸããïŒ
次ã®ã±ãŒã¹ãèŠãŠã¿ãŸãããã
- MainActivityã§ã¯ ããªããžããªã芳å¯ããŸããå€æŽãçºçãããšã notifyDataSetChangedãå®è¡ããŸã
- ãã¿ã³ãæŒã
- ããŒã¿ã®å€æŽãåŸ ã£ãŠããéã«ã æ§æã®å€æŽã«ããMainActivityãåäœæãããå ŽåããããŸãã
- MainViewModelã¯ãŸã çããŠããŸã
- 2ç§åŸã ãªããžããªãã£ãŒã«ãã¯æ°ããã¢ã€ãã ãåãåããããŒã¿ãå€æŽãããããšããªãã¶ãŒããŒã«éç¥ããŸã
- ãªãã¶ãŒããŒã¯ãããååšããªãã¢ããã¿ãŒã§notifyDataSetChangedãå®è¡ããããšããŸãã MainActivityãåäœæãããŸãã
ãŸããç§ãã¡ã®æ±ºå®ã¯ååã§ã¯ãããŸããã
LiveDataã®æŠèŠ
LiveDataã¯ã ã©ã€ããµã€ã¯ã«ãèªèããå¥ã®ã³ã³ããŒãã³ãã§ãã ãViewã©ã€ããµã€ã¯ã«ã«ã€ããŠç¥ã£ãŠãããªãã¶ãŒããã«ã«åºã¥ããŠããŸãã ãã®ãããæ§æã®å€æŽã«ããActivityãç Žæ£ãããå Žåã LiveDataã¯ãããèªèããŠãããããç Žæ£ãããActivityãããªãã¶ãŒããŒãåé€ããŸãã
MainViewModelã§å®è£ ããŸã ã
MainViewModel.kt
class MainViewModel : ViewModel() { var repoModel: RepoModel = RepoModel() val text = ObservableField("old data") val isLoading = ObservableField(false) var repositories = MutableLiveData<ArrayList<Repository>>() fun loadRepositories() { isLoading.set(true) repoModel.getRepositories(object : OnRepositoryReadyCallback { override fun onDataReady(data: ArrayList<Repository>) { isLoading.set(false) repositories.value = data } }) } }
MainActivityã®ç£èŠãéå§ããŸãã
MainActivity.kt
class MainActivity : LifecycleActivity(), RepositoryRecyclerViewAdapter.OnItemClickListener { private lateinit var binding: ActivityMainBinding private val repositoryRecyclerViewAdapter = RepositoryRecyclerViewAdapter(arrayListOf(), this) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = DataBindingUtil.setContentView(this, R.layout.activity_main) val viewModel = ViewModelProviders.of(this).get(MainViewModel::class.java) binding.viewModel = viewModel binding.executePendingBindings() binding.repositoryRv.layoutManager = LinearLayoutManager(this) binding.repositoryRv.adapter = repositoryRecyclerViewAdapter viewModel.repositories.observe(this, Observer<ArrayList<Repository>> { it?.let{ repositoryRecyclerViewAdapter.replaceData(it)} }) } override fun onItemClick(position: Int) { TODO("not implemented") //To change body of created functions use File | Settings | File Templates. } }
ããã¯ã©ãããæå³ã§ããïŒ é¢æ°ã«ãã©ã¡ãŒã¿ãŒã1ã€ãããªãå Žåããã®ãã©ã¡ãŒã¿ãŒãžã®ã¢ã¯ã»ã¹ã¯ãitããŒã¯ãŒãã䜿çšããŠååŸã§ããŸãã ãããã£ãŠã2ãæããã©ã ãåŒããããšããŸãã
((a) -> 2 * a)
次ã®ããã«çœ®ãæããããšãã§ããŸãã
(it * 2)
ä»ããã¢ããªã±ãŒã·ã§ã³ãèµ·åãããšããã¹ãŠãæ©èœããããšã確èªã§ããŸãã
...
ãªãMVPãããMVVMã奜ãã®ã§ããïŒ
- Viewã«ã¯éå±ãªã€ã³ã¿ãŒãã§ã€ã¹ã¯ãããŸããã ViewModelã«ã¯Viewãžã®åç §ããããŸãã
- Presenterã«ã¯éå±ãªã€ã³ã¿ãŒãã§ã€ã¹ã¯ãããŸãããããã¯å¿ èŠãããŸãã
- æ§æå€æŽã®åŠçãã¯ããã«ç°¡å
- MVVMã䜿çšãããšãã¢ã¯ãã£ããã£ããã©ã°ã¡ã³ããªã©ã®ã³ãŒããå°ãªããªããŸãã
...
ãªããžããªãã¿ãŒã³
ã¹ããŒã
å ã»ã©èšã£ãããã«ã Modelã¯ããŒã¿ãæºåããŠããã¬ã€ã€ãŒã®åãªãæœè±¡åã§ãã éåžžããªããžããªãšããŒã¿ã¯ã©ã¹ãå«ãŸããŸãã åãšã³ãã£ãã£ïŒããŒã¿ïŒã¯ã©ã¹ã«ã¯ã察å¿ãããªããžããªã¯ã©ã¹ããããŸãã ããšãã°ã Userã¯ã©ã¹ãšPostã¯ã©ã¹ãããå Žåã UserRepositoryãšPostRepositoryãå¿ èŠã§ã ã ãã¹ãŠã®ããŒã¿ã¯ããããååŸãããŸãã ViewãŸãã¯ViewModelããShared PreferencesãŸãã¯DBã®ã€ã³ã¹ã¿ã³ã¹ãåŒã³åºããªãã§ãã ããã
ãã®ãããRepoModelã®ååãGitRepoRepositoryã«å€æŽã§ããŸããGitRepoã¯GithubãªããžããªããååŸããRepositoryã¯ãªããžããªãã¿ãŒã³ããååŸããŸãã
RepoRepositories.kt
class GitRepoRepository { fun getGitRepositories(onRepositoryReadyCallback: OnRepositoryReadyCallback) { var arrayList = ArrayList<Repository>() arrayList.add(Repository("First", "Owner 1", 100, false)) arrayList.add(Repository("Second", "Owner 2", 30, true)) arrayList.add(Repository("Third", "Owner 3", 430, false)) Handler().postDelayed({ onRepositoryReadyCallback.onDataReady(arrayList) }, 2000) } } interface OnRepositoryReadyCallback { fun onDataReady(data: ArrayList<Repository>) }
ããããŸãããMainViewModelã¯GitRepoRepsitoriesãããªããžããªã®Githubãªã¹ããååŸããŸããã GitRepoRepositoriesã¯ã©ãããååŸããŸããïŒ
ã€ã³ã¹ã¿ã³ã¹ãããªããžããªã§ã¯ã©ã€ã¢ã³ããŸãã¯DBãçŽæ¥åŒã³åºãããšãã§ããŸãããããã¯ãŸã ãã¹ããã©ã¯ãã£ã¹ã§ã¯ãããŸããã ã¢ããªã±ãŒã·ã§ã³ã¯ã§ããã ãã¢ãžã¥ãŒã«åããå¿ èŠããããŸãã VolleyãRetrofitã«çœ®ãæããããã«å¥ã®ã¯ã©ã€ã¢ã³ãã䜿çšããããšã«ããå Žåã¯ã©ããªããŸããïŒ å éšã«äœããã®ããžãã¯ãããå Žåããªãã¡ã¯ã¿ãªã³ã°ãè¡ãã®ã¯å°é£ã§ãã ãªããžããªã¯ããªã¢ãŒãããŒã¿ã®ååŸã«äœ¿çšããŠããã¯ã©ã€ã¢ã³ããç¥ãå¿ èŠã¯ãããŸããã
- ãªããžããªãç¥ãå¿ èŠãããã®ã¯ãããŒã¿ããªã¢ãŒããŸãã¯ããŒã«ã«ã«å°çããããšã ãã§ãã ãã®ãªã¢ãŒããŸãã¯ããŒã«ã«ããŒã¿ãååŸããæ¹æ³ãç¥ãå¿ èŠã¯ãããŸããã
- å¿ èŠãªãã¥ãŒã¢ãã«ã¯ããŒã¿ã®ã¿ã§ã
- ãã¥ãŒãè¡ãã¹ãããšã¯ããã®ããŒã¿ã衚瀺ããããšã ãã§ãã
Androidã§ã®éçºãå§ããã°ããã®é ãã¢ããªã±ãŒã·ã§ã³ããªãã©ã€ã³ã§ã©ã®ããã«æ©èœããããŒã¿åæãã©ã®ããã«æ©èœããã®ããšæã£ãŠããŸããã åªããã¢ããªã±ãŒã·ã§ã³ã¢ãŒããã¯ãã£ã«ããããããç°¡åã«è¡ãããšãã§ããŸãã ããšãã°ãã€ã³ã¿ãŒãããæ¥ç¶ãããå Žåã«ViewModelã® loadRepositoriesãåŒã³åºããããšã GitRepoRepositoriesã¯ãªã¢ãŒãããŒã¿ãœãŒã¹ããããŒã¿ãåä¿¡ããããŒã«ã«ããŒã¿ãœãŒã¹ã«ä¿åã§ããŸãã é»è©±ããªãã©ã€ã³ã®ãšãã GitRepoRepositoryã¯ããŒã«ã«ã¹ãã¬ãŒãžããããŒã¿ãåä¿¡ã§ããŸãã ãã®ããã ãªããžããªã«ã¯RemoteDataSourceããã³LocalDataSourceã®ã€ã³ã¹ã¿ã³ã¹ãšããã®ããŒã¿ã®ååŸå ã§ããããžãã¯åŠçãå¿ èŠã§ãã
ããŒã«ã«ããŒã¿ãœãŒã¹ãè¿œå ããŸã ã
GitRepoLocalDataSource.kt
class GitRepoLocalDataSource { fun getRepositories(onRepositoryReadyCallback: OnRepoLocalReadyCallback) { var arrayList = ArrayList<Repository>() arrayList.add(Repository("First From Local", "Owner 1", 100, false)) arrayList.add(Repository("Second From Local", "Owner 2", 30, true)) arrayList.add(Repository("Third From Local", "Owner 3", 430, false)) Handler().postDelayed({ onRepositoryReadyCallback.onLocalDataReady(arrayList) }, 2000) } fun saveRepositories(arrayList: ArrayList<Repository>){ //todo save repositories in DB } } interface OnRepoLocalReadyCallback { fun onLocalDataReady(data: ArrayList<Repository>) }
ããã«ã¯2ã€ã®æ¹æ³ããããŸãã1ã€ç®ã¯åœã®ããŒã«ã«ããŒã¿ãè¿ãæ¹æ³ã§ã2ã€ç®ã¯æ¶ç©ºã®ããŒã¿ã¹ãã¬ãŒãžçšã§ãã
ãªã¢ãŒãããŒã¿ãœãŒã¹ãè¿œå ããŸã ã
GitRepoRemoteDataSource.kt
class GitRepoRemoteDataSource { fun getRepositories(onRepositoryReadyCallback: OnRepoRemoteReadyCallback) { var arrayList = ArrayList<Repository>() arrayList.add(Repository("First from remote", "Owner 1", 100, false)) arrayList.add(Repository("Second from remote", "Owner 2", 30, true)) arrayList.add(Repository("Third from remote", "Owner 3", 430, false)) Handler().postDelayed({ onRepositoryReadyCallback.onRemoteDataReady(arrayList) }, 2000) } } interface OnRepoRemoteReadyCallback { fun onRemoteDataReady(data: ArrayList<Repository>) }
åœã®ãªã¢ãŒãããŒã¿ãè¿ãã¡ãœããã¯1ã€ã ãã§ãã
ããã§ããªããžããªã«ããã€ãã®ããžãã¯ãè¿œå ã§ããŸãã
GitRepoRepository.kt
class GitRepoRepository { val localDataSource = GitRepoLocalDataSource() val remoteDataSource = GitRepoRemoteDataSource() fun getRepositories(onRepositoryReadyCallback: OnRepositoryReadyCallback) { remoteDataSource.getRepositories( object : OnRepoRemoteReadyCallback { override fun onDataReady(data: ArrayList<Repository>) { localDataSource.saveRepositories(data) onRepositoryReadyCallback.onDataReady(data) } }) } } interface OnRepositoryReadyCallback { fun onDataReady(data: ArrayList<Repository>) }
ãããã£ãŠããœãŒã¹ãå ±æãããšãããŒã¿ãããŒã«ã«ã«ç°¡åã«ä¿åã§ããŸãã
ãããã¯ãŒã¯ããã®ããŒã¿ã®ã¿ãå¿ èŠãªå Žåã§ãããªããžããªãã³ãã¬ãŒãã䜿çšããå¿ èŠããããŸããïŒ ã¯ã ããã«ãããã³ãŒãã®ãã¹ãã容æã«ãªããä»ã®éçºè ãã³ãŒããããããç解ã§ããããã«ãªããããè¿ éã«ãµããŒãã§ããŸãïŒ
...
AndroidãããŒãžã£ãŒã©ãããŒ
GitRepoRepositoryã§ã€ã³ã¿ãŒãããæ¥ç¶ã確èªããŠãã©ãããããŒã¿ããªã¯ãšã¹ãããããç¥ãããå Žåã¯ã©ãããŸããïŒViewModelãšModelã«Androidé¢é£ã®ã³ãŒããé 眮ããã¹ãã§ã¯ãªããšæ¢ã«è¿°ã¹ãã®ã§ããã®åé¡ã«ã©ã®ããã«å¯ŸåŠããŸããïŒ
ã€ã³ã¿ãŒãããæ¥ç¶çšã®ã©ãããŒãäœæããŸãããã
NetManager.ktïŒåæ§ã®ãœãªã¥ãŒã·ã§ã³ãä»ã®ãããŒãžã£ãŒãããšãã°NfcManagerã«é©çšãããŸãïŒ
class NetManager(private var applicationContext: Context) { private var status: Boolean? = false val isConnectedToInternet: Boolean? get() { val conManager = applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager val ni = conManager.activeNetworkInfo return ni != null && ni.isConnected } }
ãã®ã³ãŒãã¯ããããã§ã¹ãã«ã¢ã¯ã»ã¹èš±å¯ãè¿œå ããå Žåã«ã®ã¿æ©èœããŸãã
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
æã ã¯ãã³ã³ããã¹ãïŒæã£ãŠããªãå Žåããããã©ã®ããã«ããªããžããªå ã®ã€ã³ã¹ã¿ã³ã¹ãäœæããã³ã³ããã¹ãã¶ã»ãïŒïŒã³ã³ã¹ãã©ã¯ã¿ã§ãªã¯ãšã¹ãã§ããŸãïŒ
GitRepoRepository.kt
class GitRepoRepository (context: Context){ val localDataSource = GitRepoLocalDataSource() val remoteDataSource = GitRepoRemoteDataSource() val netManager = NetManager(context) fun getRepositories(onRepositoryReadyCallback: OnRepositoryReadyCallback) { remoteDataSource.getRepositories(object : OnRepoRemoteReadyCallback { override fun onDataReady(data: ArrayList<Repository>) { localDataSource.saveRepositories(data) onRepositoryReadyCallback.onDataReady(data) } }) } } interface OnRepositoryReadyCallback { fun onDataReady(data: ArrayList<Repository>) }
ViewModelã®GitRepoRepositoryã®æ°ããã€ã³ã¹ã¿ã³ã¹ã®åã«äœæããŸãããNetManagerã®ã³ã³ããã¹ããå¿ èŠãªãšãã«ãViewModelã«NetManagerãå«ããã«ã¯ã©ãããã°ããã§ããïŒã³ã³ããã¹ããæã€Lifecycle察å¿ã³ã³ããŒãã³ãã©ã€ãã©ãªã®AndroidViewModelã䜿çšã§ããŸããããã¯ã¢ããªã±ãŒã·ã§ã³ã®ã³ã³ããã¹ãã§ãããã¢ã¯ãã£ããã£ã§ã¯ãããŸããã
MainViewModel.kt
class MainViewModel : AndroidViewModel { constructor(application: Application) : super(application) var gitRepoRepository: GitRepoRepository = GitRepoRepository(NetManager(getApplication())) val text = ObservableField("old data") val isLoading = ObservableField(false) var repositories = MutableLiveData<ArrayList<Repository>>() fun loadRepositories() { isLoading.set(true) gitRepoRepository.getRepositories(object : OnRepositoryReadyCallback { override fun onDataReady(data: ArrayList<Repository>) { isLoading.set(false) repositories.value = data } }) } }
ãã®è¡ã§
constructor(application: Application) : super(application)
MainViewModelã®ã³ã³ã¹ãã©ã¯ã¿ãŒãå®çŸ©ããŸãããAndroidViewModelã¯ã³ã³ã¹ãã©ã¯ã¿ãŒã§ã¢ããªã±ãŒã·ã§ã³ã®ã€ã³ã¹ã¿ã³ã¹ãèŠæ±ããããããããå¿ èŠã§ãããã®ãããã³ã³ã¹ãã©ã¯ã¿ãŒã§ã¯ãã¹ãŒããŒãã¥ãŒã¡ãœãããåŒã³åºããŸãããã®ã¡ãœããã¯ãAndroidViewModelã³ã³ã¹ãã©ã¯ã¿ãŒãåŒã³åºããããããç¶æ¿ããŸãã
泚ïŒæ¬¡ã®å Žåã1è¡ãåé€ã§ããŸãã
class MainViewModel(application: Application) : AndroidViewModel(application) { ... }
ãããŠãGitRepoRepositoryã« NetManagerã€ã³ã¹ã¿ã³ã¹ãããã®ã§ãã€ã³ã¿ãŒãããæ¥ç¶ã確èªã§ããŸãã
GitRepoRepository.kt
class GitRepoRepository(val netManager: NetManager) { val localDataSource = GitRepoLocalDataSource() val remoteDataSource = GitRepoRemoteDataSource() fun getRepositories(onRepositoryReadyCallback: OnRepositoryReadyCallback) { netManager.isConnectedToInternet?.let { if (it) { remoteDataSource.getRepositories(object : OnRepoRemoteReadyCallback { override fun onRemoteDataReady(data: ArrayList<Repository>) { localDataSource.saveRepositories(data) onRepositoryReadyCallback.onDataReady(data) } }) } else { localDataSource.getRepositories(object : OnRepoLocalReadyCallback { override fun onLocalDataReady(data: ArrayList<Repository>) { onRepositoryReadyCallback.onDataReady(data) } }) } } } } interface OnRepositoryReadyCallback { fun onDataReady(data: ArrayList<Repository>) }
ãããã£ãŠãã€ã³ã¿ãŒãããã«æ¥ç¶ããŠããå Žåãåé€ãããããŒã¿ãåä¿¡ããŠââããŒã«ã«ã«ä¿åããŸããã€ã³ã¿ãŒãããã«æ¥ç¶ããŠããªãå Žåã¯ãããŒã«ã«ããŒã¿ãååŸããŸãã
Kotlinã®æ³šæïŒãªãã¬ãŒã¿ãèãããŠãã«ã®ããã®ãã§ãã¯ãããå ã®å€ãè¿ãITã
次ã®ããããã®èšäºã§ãäŸåæ§æ³šå ¥ãViewModelã§ãªããžããªã€ã³ã¹ã¿ã³ã¹ãäœæããã®ãããã«æªãããAndroidViewModelã®äœ¿çšãé¿ããæ¹æ³ã«ã€ããŠèª¬æããŸãããŸããçŸåšã³ãŒãã«ããå€ãã®åé¡ã«ã€ããŠã説æããŸããçç±ã®ããã«ããããæ®ããŸãã...
ãããã®ã©ã€ãã©ãªããã¹ãŠäººæ°ãããçç±ãšãããã䜿çšããçç±ãç解ã§ããããã«ãåé¡ã衚瀺ããããšããŠããŸãã
PSç§ã¯ãããããŒïŒã«ã€ããŠã®ç§ã®å¿ãå€ããããããŒïŒãããã«ã€ããŠã¯ã次ã®èšäºã§èª¬æããããšã«ããŸããã