月: 2022年2月

ViewModelの間違った使い方(その4) 

今度こそ画面回転してもデータが無くならないようにしましょう。

$ git clone git@github.com:saitoyusuke/using_viewmodel_incorrectly.git
$ cd using_viewmodel_incorrectly
$ git checkout 07cbff916075c9845940e69a52a9f1d0667357a1
   implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
class MainViewModel : ViewModel() {
    private val _count: MutableLiveData<Int> = MutableLiveData(0)
    val count: LiveData<Int> = _count

    fun countUp() {
        _count.postValue(_count.value?.plus(1))
    }
}
class MainActivity : AppCompatActivity() {

    private lateinit var viewBinding: ActivityMainBinding
    private val viewModel: MainViewModel by lazy { ViewModelProvider(this).get(MainViewModel::class.java) }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        viewBinding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(viewBinding.root)

        viewBinding.countUp.setOnClickListener {
            viewModel.countUp()
        }

        viewModel.count.observe(this) { count ->
            viewBinding.countView.text = count.toString()
        }
    }
}

実際に動かしてみましょう。

やっと画面回転してもデータが保持されるようになりました!なぜ今回の変更でデータが保持されるようになったかというと「ViewModelがいつまでデータを保持したら良いか(ViewModelProvider)」を指定したためです。それまではViewModelがいつまでデータを保持したら良いかわからなかったので、Activityが再生成されると、ViewModel自体も再生成されていましたが、ViewModelProviderを使うことでいつデータを破棄したら良いかをViewModelが知れるようになったのです。

このようにViewModelを知っているつもりでもちゃんと使い方を知らないと画面回転しても正しく動作しません。ViewModelだけではなく、DaggerHilt(DependencyInjection)やRx○○、CoroutineFlow等、なぜそのライブラリを使うのか、使うことでどういったメリットがあるのか、正しく扱うにはどうしたらよいのか、よく検討してから入れるようにしましょう。ただ流行っているから、みんな使っているからという理由でライブラリを入れることはバグを発生させ、開発スピードを遅くさせてしまいます。


ViewModelの間違った使い方(その3)

さて、気を取り直してLiveDataとMutableLiveDataを使って値を購読するように実装しましょう。

$ git clone git@github.com:saitoyusuke/using_viewmodel_incorrectly.git
$ cd using_viewmodel_incorrectly
$ git checkout 6ecec129966387d5ef0e729f613682e84c80b949
class MainViewModel : ViewModel() {
    private val _count: MutableLiveData<Int> = MutableLiveData(0)
    val count: LiveData<Int> = _count

    fun countUp() {
        _count.postValue(_count.value?.plus(1))
    }
}
class MainActivity : AppCompatActivity() {

    private lateinit var viewBinding: ActivityMainBinding
    private val viewModel = MainViewModel()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        viewBinding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(viewBinding.root)

        viewBinding.countUp.setOnClickListener {
            viewModel.countUp()
        }

        viewModel.count.observe(this) { count ->
            viewBinding.countView.text = count.toString()
        }
    }
}

実際に動かしてみました。

ViewModelもLiveDataもちゃんと使っているのに画面回転するとデータが無くなってしまいます。正しく使えているはずなのになぜでしょう。

次回は画面回転してもデータが保存されるようにします。


ViewModelの間違った使い方(その2)

前回はViewModel内に変数を宣言してデータを変更後に画面回転するとデータが無くなってしまいました。「LiveData使っていないからでしょー。ViewModelとLiveDataはセットで使うのが鉄則でしょー」というツッコミが入るのでLiveDataを使ってみました。

$ git clone git@github.com:saitoyusuke/using_viewmodel_incorrectly.git
$ cd using_viewmodel_incorrectly
$ git checkout c60bf3ed4e45fb0c70afa6dd78daedd834fadef8
class MainViewModel : ViewModel() {
    val count: MutableLiveData<Int> = MutableLiveData(0)
}
class MainActivity : AppCompatActivity() {

    private lateinit var viewBinding: ActivityMainBinding
    private val viewModel = MainViewModel()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        viewBinding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(viewBinding.root)

        viewBinding.countUp.setOnClickListener {
            viewModel.count.value = viewModel.count.value?.plus(1)
            viewBinding.countView.text = viewModel.count.value.toString()
        }
    }
}

実際に動かしてみました

おかしいですね。LiveDataを使っても画面回転するとデータが無くなってしまいます。そもそもLiveDataの使い方が間違っているのでちゃんとドキュメント読んでMutableLiveDataとLiveDataを使いましょう。

参照:https://developer.android.com/topic/libraries/architecture/livedata?hl=ja


ViewModelの間違った使い方(その1)

参照:https://developer.android.com/topic/libraries/architecture/viewmodel?hl=ja

対象:ViewModelを誤解して使っている方(画面回転の対応ができない方)

「ViewModelは画面回転してもデータを保存してくれるところでしょー!ViewModel()を継承して使えばいいんでしょー」という方がいるのでサンプルを作ってみました。

$ git clone git@github.com:saitoyusuke/using_viewmodel_incorrectly.git
$ cd using_viewmodel_incorrectly
$ git checkout 6222eced99c8427c9fccd80cd62beb9b09c7fccf
class MainViewModel : ViewModel() {
    var count: Int = 0
}
class MainActivity : AppCompatActivity() {

    private lateinit var viewBinding: ActivityMainBinding
    private val viewModel = MainViewModel()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        viewBinding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(viewBinding.root)

        viewBinding.countUp.setOnClickListener {
            viewModel.count = viewModel.count + 1
            viewBinding.countView.text = viewModel.count.toString()
        }
    }

ViewModelって簡単ですねー。実際に動かしてみましょう。

おかしいですね。画面回転するとカウントアップしたデータが無くなってしまいます。ViewModelはただデータを保存する場所ではないので、このような使い方はしないようにしましょう。


時間をかけずにViewにアニメーションをつける

下記ライブラリを使用します

https://github.com/daimajia/AndroidViewAnimations

アニメーションをつけたいViewが属するモジュールのbuild.gradleに下記を追加します。

dependencies {
  implementation 'com.daimajia.androidanimations:library:[version]@aar'
}

Sync Project with Gradle Filesを実行します。

Viewにアニメーションを設定します。

YoYo.with(Techniques.Tada).duration(700).repeat(5).playOn(view)

独自で書く方法もありますが調べたりメンテするのも面倒なので、とにかく時間をかけずにViewにアニメーションを付けたい場合はこの方法がおすすめです。ライブラリが2014年から開発されておりスター12000超えなので安泰ですね。