18

I want to send an extra parameter to my ViewModel, but this extends from AndroidViewModel. How can I add this parameter to the ViewModelFactory class ?

ViewModel

class ProjectViewModel(application: Application) : AndroidViewModel(application) {

    // need a param for project id...
}

ViewModelFactory

class ProjectViewModelFactory(val projectId: Int): ViewModelProvider.Factory {

    override fun <T : ViewModel?> create(modelClass: Class<T>): T {
        // need to send this...
        return ProjectViewModel(projectId) as T
    }
}

Note: I notice that in the documentation its says: AndroidViewModel Subclasses must have a constructor which accepts Application as the only parameter.

So I don't know if it is posible (or good) to do what I'm trying to do.

Slimane amiar
  • 733
  • 9
  • 24
mposadar
  • 181
  • 1
  • 1
  • 6

2 Answers2

17

Get ViewModel:

        viewModel = ViewModelProviders.of(this,
                new BListFactory(getActivity().getApplication(), 1))
                .get(BListViewModel.class);

Factory:

class BListFactory extends ViewModelProvider.NewInstanceFactory {

    @NonNull
    private final Application application;

    private final long id;

    public BListFactory(@NonNull Application application, long id) {
        this.application = application;
        this.id = id;
    }

    @NonNull
    @Override
    public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
        if (modelClass == BListViewModel.class) {
            return (T) new BListViewModel(application, id);
        }
        return null;
    }
}

AndroidViewModel:

public class BListViewModel extends AndroidViewModel {

    private final long id;

    public BListViewModel(@NonNull Application application, final long id) {
        super(application);
        this.id = id;
    }
}
Andrey K
  • 183
  • 1
  • 7
  • I've been trying to figure this out for 2 days know thanks @Andrey k, do you have any links to docs explaining this I've googled **viewModelProviders.Factory** any documents that explained how Factory works. Maybe I'm searching for the wrong term. – Shawn Aug 14 '19 at 01:56
-1

I referred to AndroidViewModelFactory class and use it like this

1.AndroidViewModelFactory

class AndroidViewModelFactory private constructor(
    private val mApplication: Application,
    private val mName: String
) : ViewModelProvider.NewInstanceFactory() {

    override fun <T : ViewModel?> create(modelClass: Class<T>): T {
        return if (AndroidViewModel::class.java.isAssignableFrom(modelClass)) {
            try {
                modelClass.getConstructor(
                    Application::class.java, String::class.java
                ).newInstance(mApplication, mName)
            } catch (e: Exception) {
                throw RuntimeException("Cannot create an instance of $modelClass", e)
            }
        } else super.create(modelClass)
    }

    companion object {
        private var sInstance: AndroidViewModelFactory? = null
        fun getInstance(
            application: Application,
            name: String
        ): AndroidViewModelFactory {
            if (sInstance == null) {
                sInstance = AndroidViewModelFactory(application, name)
            }
            return sInstance as AndroidViewModelFactory
        }
    }
}

2.ViewModelFactory

object ViewModelFactory {

    fun <T : ViewModel?> createViewModel(
        activity: FragmentActivity,
        application: Application,
        name: String,
        cls: Class<T>?
    ): T {
        return ViewModelProvider(
            activity,
            AndroidViewModelFactory.getInstance(application, name)
        ).get(cls!!)
    }
}

3.NameViewModel

class NameViewModel(application: Application, name: String) : AndroidViewModel(application) {

    val name = ObservableField<String>()

    init {
        this.name.set(name)
    }
}

4.NameActivity

class NameActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding =
            DataBindingUtil.setContentView(this, R.layout.activity_name) as ActivityNameBinding
        val viewModel = ViewModelFactory.createViewModel(
            this,
            application,
            "AndroidViewModel",
            NameViewModel::class.java
        )
        binding.viewModel = viewModel
    }
}

5.activity_name

<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>
        <variable
            name="viewModel"
            type="cn.eli.demo.viewmodel.NameViewModel" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".viewmodel.NameActivity">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            android:text="@{viewModel.name}"/>
    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>