Unable to set ViewModel on Kotlin - android

New to Kotlin language, trying Android Architecture Components. Trying to set a ViewModel for my LifecycleActivity in Kotlin language:
class FooActivity : LifecycleActivity() {
private var mViewModel: FooViewModel? = null
..
override fun onCreate(savedInstanceState: Bundle?) {
mViewModel = ViewModelProviders.of(this).get(FooViewModel.class) <-- error here
Getting Name expected: and expecting )
What am I missing?

The class usage is wrong. With Kotlin you use: FooViewModel::class.java

Related

Why it says "Cannot create an instance of class com.app.myapp.viewModel" in android jetpack compose?

I am new in adroid , so I have a simple project, I want to create simple register project, so I have viewmodel in my project and I amusing Hilt library also in there, and when I build project it is throw an error for
myViewModel = ViewModelProvider(this)[MyViewModel::class.java]
as a "Cannot create an instance of class com.app.myapp.viewModel", I do not know what I missed?
class Register : ComponentActivity() {
private lateinit var myViewModel: MyViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
myViewModel = [ViewModelProvider(this)::class.java]
setContent {
RegisterScreen(myViewModel)
}
}
}
#Composable
fun RegisterScreen(
myViewModel: MyViewModel
) {
}
Reasons may cause system can not create viewModel:
Your viewModel class is not Public
Your package name which contains viewModel contains special keywords (such a "package.new.feature")
If you are using dagger hilt you should putt annotation #HiltViewModel above the class declaration and create constructor like
#HiltViewModel
class viewModel #Inject constructor() : ViewModel()
With the dagger hilt You should use hiltViewModel() function to create instance for compose instead of viewModel()
dependency: androidx.hilt:hilt-navigation-compose
#Composable
fun MyExample (viewModel: MyViewModel = hiltViewModel())
Your ViewModel class does not extend from androidx.lifecycle.ViewModel
You should create your ViewModel class extending from the ViewModel, something like RegisterViewModel.
Take a look at the documentation for more info:
https://developer.android.com/topic/libraries/architecture/viewmodel
You are trying to create a view model from the base class ViewModel. it doesn't work like this
You need to create your own viewmodel class and extend it from the base class ViewModel like this
class MyViewModel : ViewModel() {
}
So your code will be like
class MyViewModel : ViewModel() {
// your implementation
}
class Register : ComponentActivity() {
private lateinit var viewModel: MyViewModel // changes to this line
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel = ViewModelProvider(this)[MyViewModel::class.java] // changes to this line
setContent {
RegisterScreen(viewModel)
}
}
}
BUT if you are using compose you should look at the integration between viewmodel and compose
to make your composable use the viewModel without you creating it then passing it to the composable
#Composable
fun MyExample(
viewModel: MyViewModel = viewModel()
) {
// use viewModel here
}

Cannot access ViewModel method from fragment

Probably it has a simple solution that I cant see. I have a fragment with a ViewModel, The Viewmodel has a method inside of it that I want to call from my fragment and supply the arguments for. but when I try to call the method it shows an error "Unsolved Reference"
class DetailFragmentViewModel : ViewModel() {
private val repo = Crepository.get()
private val itemIdlivedata = MutableLiveData<UUID>()
var crimeLiveDate: LiveData<Crime?> = Transformations.switchMap(itemIdlivedata){ it ->
repo.getitem(it) }
fun LoadItem(itemuuid:UUID){
itemIdlivedata.value = itemuuid
}
}
Fragment Class:
private val crimeDetailVM : ViewModel by lazy {
ViewModelProvider(this).get(DetailFragmentViewModel::class.java)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
...
crimeDetailVM.LoadItem <- Unsolved Reference
}
Thanks for the help!
EDIT:IT HAS A SIMPLE SOLUTION, I DID NOT CAST THE VIEW MODEL TO THE VIEW MODEL CLASS,THANKS FOR THE HELP EVERYONE
You are doing downcasting DetailFragmentViewModel to ViewModel. That is why you are not accessing to DetailFragmentViewModel methods.
Use
private val crimeDetailVM : DetailFragmentViewModel by lazy {
ViewModelProvider(this).get(DetailFragmentViewModel::class.java)
}
Instead of
private val crimeDetailVM : ViewModel by lazy {
ViewModelProvider(this).get(DetailFragmentViewModel::class.java)
}
Also this way is not idiomatic i suggest you to use kotlin extension
val viewModel by viewModels<DetailFragmentViewModel>()
But before do that you need to add the dependency which is Fragment KTX to your app gradle file.
https://developer.android.com/kotlin/ktx
You need activity context
try:
ViewModelProvider(requireActivity()).get(DetailFragmentViewModel::class.java)
you can use also extend view model by ActivityViewModel
eg.-> class DetailFragmentViewModel(application:Application) : AndroidViewModel(applivation){}

Kotlin, Dagger and Realm - DI concept problem

I'm starting this brand new project only for fun, but at the first steps I got a problem, there it goes:
I have this class "Note", it's a realm class as you can see below
#RealmClass
open class Note
#Inject constructor (#PrimaryKey var id: String,
var text: String,
var badge: NoteBadge?
) : RealmObject() {
fun getRandomNoteText(): String = "supposed to be random"
}
Then I have my Main activity class, where I already got Dagger working.
class MainActivity : AppCompatActivity() {
#Inject lateinit var note: Note
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
DaggerNoteComponent.create().inject(this)
Log.d("MAIN_ACTIVITY", note.getRandomNoteText())
Log.d("MAIN_ACTIVITY", note.badge?.getRandomBadgeText())
}
}
It got tricky in terms of concepts, the code above doesn't compile, I have to add this line to my Note class to make it work:
constructor(): this(UUID.randomUUID().toString(), "", NoteBadge()){}
However there I have NoteBadge() I'm creating an instance of NoteBadge manually, that's bad, I would like dagger did that.
I've tried sending a null value as parameter, but them I have a null NoteBadge at my MainActivity.
So do you have any idea how to fix that and make my dependency injection fully funcional? With no manual instance initializations?
EDIT -> Paste NoteComponent
#Component(modules = [NoteBadgeModule::class])
interface NoteComponent {
fun inject(activity: MainActivity)
}

how to instantiate ViewModel In AndroidX?

I want to initialize ViewModel in Activity using androidx library
I have tried what documentation says but it is not working. the ".of" is not resolved.
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.ViewModelProvider
import com.example.myapplication.databinding.ActivityMainBinding`
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding: ActivityMainBinding = DataBindingUtil.setContentView(
this, R.layout.activity_main)
binding.setLifecycleOwner(this)
var model = ViewModelProvider.of(this).get(SheduleViewModel::class.java)
}
}
of is not resolved, maybe there are another way to do it in androidx
Updated answer:
Things changed a little bit, as the previously needed dependency - ViewModelProviders - got deprecated (see the old answer for details). You can now use the ViewModelProvider constructor directly.
So, in this case, the answer would be:
private val viewModel = ViewModelProvider(this).get(SheduleViewModel::class.java)
Note that, however, if you include the androidx.activity:activity-ktx:$Version dependency (a few of the commonly used AndroidX dependencies already include it for you), you can make use of property delegation:
private val viewModel: SheduleViewModel by viewModels()
Which internally will use ViewModelProvider and scope your ViewModel to your Activity. It's just a more concise way of writing the same thing. You can do the same for a Fragment by including the androidx.fragment:fragment-ktx:$Version dependency instead (again, commonly already included by other AndroidX dependencies).
Both the ViewModelProvider constructor and by viewModels() also accept a factory as a parameter (useful for injecting your ViewModel):
private val viewModel =
ViewModelProvider(this, viewModelFactory).get(SheduleViewModel::class.java)
and
private val viewModel: SheduleViewModel by viewModels { viewModelFactory }
Use the one that best suits you.
Old answer:
Add the androidx.lifecycle:lifecycle-extensions:$lifecycleExtensionsVersion dependency in order to import ViewModelProviders.
Updating ViewModel to Lifecycle Version 2.2.0 and Above
The ViewModels (VMs) may theoretically be initialized as class level instance variables using the Kotlin extension library import androidx.fragment.app.viewModels method by viewmodels(). By initializing the VM as a class level instance var it can be accessed within the class.
Question: Is there a downside to initializing the VMs as class level instance variables instead of inside onCreate?
When creating the VMs with the extension function inside onCreate the VMs are only scoped within onCreate and extra code is required to reassign the class level instance variables.
See documentation
ViewModel Overview
Lifecycle
Initialize VM as Class Instance Val
class Fragment : Fragment() {
private val viewModel: SomeViewModel by viewModels()
private fun observeViewState() {
viewModel.feedViewState.observe(viewLifecycleOwner) { viewState ->
//viewState used here.
}
}
}
Initialize VM in onCreate and Reassign Class Instance Var
class Fragment : Fragment() {
private lateinit var viewModel: SomeViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val viewModel: ContentViewModel by viewModels()
this.viewModel = viewModel
}
private fun observeViewState() {
viewModel.feedViewState.observe(viewLifecycleOwner) { viewState ->
//viewState used here.
}
}
}
Passing Arguments/Parameters
// Override ViewModelProvider.NewInstanceFactory to create the ViewModel (VM).
class SomeViewModelFactory(private val someString: String): ViewModelProvider.NewInstanceFactory() {
override fun <T : ViewModel?> create(modelClass: Class<T>): T = SomeViewModel(someString) as T
}
class SomeViewModel(private val someString: String) : ViewModel() {
init {
//TODO: Use 'someString' to init process when VM is created. i.e. Get data request.
}
}
class Fragment: Fragment() {
// Create VM in activity/fragment with VM factory.
val someViewModel: SomeViewModel by viewModels { SomeViewModelFactory("someString") }
}
Enabling SavedState with Arguments/Parameters
class SomeViewModelFactory(
private val owner: SavedStateRegistryOwner,
private val someString: String) : AbstractSavedStateViewModelFactory(owner, null) {
override fun <T : ViewModel?> create(key: String, modelClass: Class<T>, state: SavedStateHandle) =
SomeViewModel(state, someString) as T
}
class SomeViewModel(private val state: SavedStateHandle, private val someString: String) : ViewModel() {
val feedPosition = state.get<Int>(FEED_POSITION_KEY).let { position ->
if (position == null) 0 else position
}
init {
//TODO: Use 'someString' to init process when VM is created. i.e. Get data request.
}
fun saveFeedPosition(position: Int) {
state.set(FEED_POSITION_KEY, position)
}
}
class Fragment: Fragment() {
// Create VM in activity/fragment with VM factory.
val someViewModel: SomeViewModel by viewModels { SomeViewModelFactory(this, "someString") }
private var feedPosition: Int = 0
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
someViewModel.saveFeedPosition((contentRecyclerView.layoutManager as LinearLayoutManager)
.findFirstVisibleItemPosition())
}
override fun onViewStateRestored(savedInstanceState: Bundle?) {
super.onViewStateRestored(savedInstanceState)
feedPosition = someViewModel.feedPosition
}
}
For me, the only thing that worked:
implementation 'androidx.fragment:fragment:1.2.4'
PS. This is for someone who is using java and got stuck for a while like I did and this SO answer comes up in google all the time.
Apparently, the API has change as of this date (6 May 2020), I had to do this to get it working.
// 1. Create a ViewModel Class Let's call it AppStateViewModel
// 2. Put below code Inside Activity onCreate like this:
ViewModelProvider.Factory factory = new ViewModelProvider.NewInstanceFactory();
appStateManager = new ViewModelProvider(this, factory).get(AppStateViewModel.class);
ViewModelProviders: This class is deprecated. Use the constructors for ViewModelProvider directly.
Examples in Kotlin
This is how you can use ViewModelProvider directly:
If your view-model is extending AndroidViewModel with just one argument, the app - then you can use the default AndroidViewModelFactory and you don't have to write a new Factory. Example:
// Activity / fragment class
private lateinit var viewModel: MyOwnAndroidViewModel
// onCreate
viewModel = ViewModelProvider(
this,
ViewModelProvider.AndroidViewModelFactory(application)
).get(MyOwnAndroidViewModel::class.java)
If your view-model is only extending the ViewModel without extra arguments then use the NewInstanceFactory().
// Activity / fragment class
private lateinit var viewModel: MyOwnViewModel
// onCreate
viewModel = ViewModelProvider(
this,
ViewModelProvider.NewInstanceFactory()
).get(MyOwnViewModel::class.java)
Adam's answer above covers other variations as well.
Disclaimer: Still learning basic Android development - if there's any problem with the code, let me know in comments.
(How to) Use ViewModel from Android Architecture Component :
Add the Google Maven repository (Optional, just verify that)
Android Studio projects aren't configured to access this repository by default.
To add it to your project, open the build.gradle file for your project (not the ones for your app or module) and add the google() repository as shown below:
allprojects {
repositories {
google()
jcenter()
}
}
Declaring dependencies
Open your app-level build.gradle file,
Go to dependencies{} block
Put implementation "androidx.lifecycle:lifecycle-viewmodel:$lifecycle_version" for AndroidX version, $lifecycle_version here is latest version defined.
For Pre-AndroidX use implementation "android.arch.lifecycle:viewmodel:1.1.1" (1.1.1 is the last version from this artifact i guess.)
In your activity, use like this syntax
Import this class :
import androidx.lifecycle.ViewModelProviders; for AndroidX
import android.arch.lifecycle.ViewModelProviders; when using Pre-AndroidX
And obtain your ViewModel like following
ViewModelProviders.of(this).get(ProfileObservableViewModel::class.java) // Kotlin syntax
---- or ----
ViewModelProviders.of(this).get(ProfileObservableViewModel.class); // Java syntax
In your app gradle file make sure you have added below dependencies:
For Activity use:
implementation "androidx.activity:activity-ktx:1.4.1"
For Fragment use:
implementation 'androidx.fragment:fragment:1.4.1'
Paste the code below in build.gradle(:app)
implementation 'androidx.fragment:fragment-ktx:1.4.1'
paste the following or similar(relevant to your settings) in app.gradle under dependencies
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.5'
implementation 'androidx.navigation:navigation-ui-ktx:2.3.5'
I add the last version of this dependency from
https://developer.android.com/kotlin/ktx/extensions-list
implementation "androidx.activity:activity-ktx:1.4.0"

how to instantiate an object from a class in kotlin

I am learning Kotlin, and I googled how to create a class in kotlin. So, I created the below class as a test.
In the main activity, I am trying to instantiate an object from the class Board, but i get the following error:
classifier Board does not have a companion object
please let me know how to intantiate an object of an the class Board?
MainActivity:
class ActMain : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.layout_act_main)
Board board = new Board(name = "ABC");
}
}
Board.kt:
data class Board(val name: String) {
var age: Int = 0
}
Kotlin does not use new.
Board board = new Board(name = "ABC");
is incorrect. Use
val board = Board("ABC")
Your code reflects the Java syntax... sort of. Kotlin has type inference, so you don't need to specify the class type. However, if you do specify it, it's different from Java:
val board: Board = Board("ABC")
Semi-colons are also not generally used in Kotlin, although they won't break the compilation if you use them.
name = "ABC" just isn't valid syntax no matter if it's Java or Kotlin. Actually it is (from #hotkey): https://kotlinlang.org/docs/reference/functions.html#named-arguments
Unlike Java, in Kotlin this is the correct way
MainActivity.kt
class ActMain : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.layout_act_main)
val board = Board("ABC")
board.age = 12
}
}
Board.kt
class Board(val name: String) {
var age: Int = 0
}
try to forget java
val board = Board("name")
in kotlin
when you want to declare new object yon can do like this.
val board = Board("ABC")
if you declare object by using val keyword. it look as you use final in java. the variable which you declared can not recreate again.
var board = Board("ABC")
if you use var to declare it look as normal variable in java
Anyway in kotlin you will see something that It doesn't contain in java such as
scoping function as link below. it will help you write your code is more easily.
https://kotlin.guide/scoping-functions
I hope this help :)

Categories

Resources