Home > Software engineering >  How can I access view data from class in kotlin
How can I access view data from class in kotlin

Time:04-05

I'm new in kotlin and ı 'm trying to apply mvvm design pattern in my simple project to understand. I have a model,view,viewmodel. view package has mainactivity and 2 fragments. There are tablayout and viewpager views in mainactivity, I am trying to access them from stopwatchviewmodel in viewmodel, but I cannot access them. stopwatchviewmodel is a class but I can access from fragments and mainactivity. How can ı solve this issue?

MainActivity.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
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=".view.MainActivity">

<com.google.android.material.tabs.TabLayout
    android:id="@ id/tabLayout"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent">

    <com.google.android.material.tabs.TabItem
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="kronometre" />

    <com.google.android.material.tabs.TabItem
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="zamanlayıcı" />


</com.google.android.material.tabs.TabLayout>

<androidx.viewpager.widget.ViewPager
    android:id="@ id/viewPager"      <!-- the data ı want to reach-->
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_below="@id/tabLayout">
</androidx.viewpager.widget.ViewPager>   

StopWatchViewModel.kt

class StopWatchViewModel(val context: Context):ViewModel() {



private fun setUpTabs()
{

    val fragmentManager = (context as FragmentActivity).supportFragmentManager
    val adapter = ViewPageModelAdapter(fragmentManager)
    adapter.addFragment(StopWatchFragment(),"stopWatchFragment")
    adapter.addFragment(TimerFragment(),"timerFragment")

    viewpager.adapter = adapter  // <--- ı cannot access viewpager

}
}

build.gradle

 plugins {
    id 'com.android.application'
    id 'org.jetbrains.kotlin.android'
    id 'kotlin-android-extensions'  //  <--- I also added kotlin extensions.
}

android {
    compileSdk 31

    defaultConfig {
        applicationId "com.enestigli.kronometre"
        minSdk 21
        targetSdk 31
        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'
    }

    buildFeatures{
        dataBinding = true
        viewBinding = true
    }
}

dependencies {

    implementation 'androidx.core:core-ktx:1.7.0'
    implementation 'androidx.appcompat:appcompat:1.4.1'
    implementation 'com.google.android.material:material:1.5.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
    implementation 'androidx.legacy:legacy-support-v4:1.0.0'
    testImplementation 'junit:junit:4.13.2'
    androidTestImplementation 'androidx.test.ext:junit:1.1.3'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
    implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.1"
}

CodePudding user response:

Setting up Views and Fragments is part of the 'View' layer so it shouldn't really be in de ViewModel in the first place. Let this code live in the Fragment or Activity classes that actually contain these views.


That being said, generally speaking if your ViewModel needs anything you can pass it those dependencies either in the constructor, in the method that uses them or through some dependency injection framework.
In this case, you could pass the Context and ViewPager objects to the setUpTabs function:

fun setUpTabs(context: Context, viewPager: ViewPager) {
  ...
}

And then call it from the fragment with the correct values.


Now, since setting up Views is not part of the ViewModels responsibility, you could refactor it to some setup where the ViewModel decides which fragments should be added, but the view actually handles the implementation.

The ViewModel decides what to show, but doesn't require any of the Views dependencies:

class SomeViewModel() : ViewModel() {

    fun getTabsToShow(): Map<String, Fragment> {
        return mapOf(
            "stopWatchFragment" to StopWatchFragment(),
            "timerFragment" to TimerFragment())
    }
}

The Fragment, for example it could also be an Activity, then uses this information to build up the correct views:

class SomeFragment : Fragment() {
    override fun onViewCreated(view: View) {
        val tabs = viewModel.getTabsToShow()

        val fragmentManager = requireActivity().supportFragmentManager
        val adapter = ViewPageModelAdapter(fragmentManager)
        for ((tag, fragment) in tabs) {
            adapter.addFragment(fragment, tag)
        }

        viewpager.adapter = adapter
    }
}
  • Related