0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

DataBindingのバインディング式でsealed classを使う

Last updated at Posted at 2021-03-17

バインディング式でsealed classを使う方法です。

sealed classは、アプリ アーキテクチャ ガイド で紹介されているResourceを使います。

instanceofを使う

==を使いたくなってしまうところですが、instanceofを使います。

android:visibility="@{viewModel.resource instanceof Resource.Success ? View.VISIBLE : View.GONE}"

ちなみに上記は「成功したらウィジェットを表示」「処理中またはエラーが発生したら表示しない」という実装です。

まとめ

MVVMを使った実装例を載せておきます(どこか間違えていたら教えて下さい...)

ネットワークから記事を取得するイメージです。

1bcb318dba24116959061a48a500e052.gif

app/build.gradle
plugins {
    id 'com.android.application'
    id 'kotlin-android'
    // 追加
    id 'kotlin-kapt'
}

android {
    compileSdkVersion 30
    buildToolsVersion "30.0.3"

    defaultConfig {
        applicationId "com.example.databindingsealedsample"
        minSdkVersion 23
        targetSdkVersion 30
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    kotlinOptions {
        jvmTarget = '1.8'
    }

    // 追加: DataBindingで使用
    buildFeatures {
        dataBinding true
    }
}

dependencies {

    implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
    implementation 'androidx.core:core-ktx:1.3.2'
    implementation 'androidx.appcompat:appcompat:1.2.0'
    implementation 'com.google.android.material:material:1.3.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.0.4'

    // 追加: ViewModelScopeで使用
    implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.0'
    // 追加: ViewModelの初期化で使用
    implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'

    // 追加: 記事取得処理を遅延させるために使用
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.2'
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.2'

    testImplementation 'junit:junit:4.+'

    androidTestImplementation 'androidx.test.ext:junit:1.1.2'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}
MainActivity.kt
class MainActivity : AppCompatActivity() {

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

    private val binding: ActivityMainBinding by lazy {
        DataBindingUtil.setContentView(this, R.layout.activity_main)
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        binding.apply {
            lifecycleOwner = this@MainActivity
            // MainViewModelをバインドする
            viewModel = this@MainActivity.viewModel
        }

        // 記事を取得する
        viewModel.fetchArticle(articleId = 1)
    }
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<layout>

    <data>

        <variable
            name="viewModel"
            type="com.yass.databindingsealedsample.MainViewModel" />

        <import type="android.view.View" />

        <import type="com.yass.databindingsealedsample.Resource" />
    </data>

    <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=".MainActivity">

        <ProgressBar
            android:id="@+id/progress"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:visibility="@{viewModel.resource instanceof Resource.Loading ? View.VISIBLE : View.GONE}"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <androidx.constraintlayout.widget.ConstraintLayout
            android:id="@+id/article_layout"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent">

            <TextView
                android:id="@+id/article_title"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:gravity="center"
                android:text="@{viewModel.resource.data.title}"
                android:textSize="20sp"
                android:textStyle="bold"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="parent" />

            <TextView
                android:id="@+id/article_description"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="10dp"
                android:text="@{viewModel.resource.data.description}"
                android:visibility="@{viewModel.resource instanceof Resource.Success ? View.VISIBLE : View.GONE}"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@+id/article_title" />

        </androidx.constraintlayout.widget.ConstraintLayout>

        <androidx.constraintlayout.widget.ConstraintLayout
            android:id="@+id/error_layout"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:visibility="@{viewModel.resource instanceof Resource.Error ? View.VISIBLE : View.GONE}"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent">

            <ImageView
                android:id="@+id/error_image"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:background="@color/purple_500"
                android:src="@drawable/ic_launcher_foreground"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="parent" />

            <TextView
                android:id="@+id/error_message"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="10dp"
                android:text="データの取得に失敗しました"
                android:textSize="16sp"
                app:layout_constraintTop_toBottomOf="@+id/error_image" />

        </androidx.constraintlayout.widget.ConstraintLayout>

    </androidx.constraintlayout.widget.ConstraintLayout>

</layout>
MainViewModel.kt
class MainViewModel : ViewModel() {

    private val repository: ArticleRepository by lazy {
        ArticleRepository()
    }

    private val _resource = MutableLiveData<Resource<Article>>()
    val resource: LiveData<Resource<Article>> = _resource

    fun fetchArticle(articleId: Int) {

        // ローディング開始
        _resource.postValue(Resource.Loading())

        viewModelScope.launch(Dispatchers.IO) {
            val article = repository.fetchArticle(articleId)
            _resource.postValue(article)
        }
    }
}
ArticleRepository.kt
class ArticleRepository {

    private val remote: RemoteDataSource by lazy {
        RemoteDataSource()
    }

    suspend fun fetchArticle(articleId: Int): Resource<Article> = remote.fetchArticle(articleId)
}
RemoteDataSource.kt
class RemoteDataSource {

    suspend fun fetchArticle(articleId: Int): Resource<Article> {

        var article: Article? = null

        // ネットワークからデータを取得すると仮定して、5秒遅延させます
        GlobalScope.launch(Dispatchers.IO) {
            delay(5000)
            // 取得した記事
            article = Article(id = articleId, title = "記事のタイトル", description = "記事の説明")
        }.join()

        // 記事が存在すれば「Success」とします
        return article?.let {
            Resource.Success(it)
        } ?: Resource.Error(message = "Failed to get the article")
    }
}
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?