Hilt - cannot be initialized ViewModelFactory in fragment - android-studio

i'am using retrofit library to get info from internet api , then i put the data in the repository then get it in viewmodel which it will be instatiate by viewmodelfactory ;
so i'am trying to inject HitViewModelFactory by using Dagger-Hilt in Fragment but it show me an error when to inject it in the fragment
lateinit property hitViewModelFactory has not been initialized
i make an application class and anotate it with #HiltAndroidApp and give the name of application in manifest. and make anotate to activity that host this fragment but the problem not solved
#AndroidEntryPoint
class BaseActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
.......
}
}
i provide all dependencies that i need in the module class :
#Module
#InstallIn(ApplicationComponent::class)
object AppModule {
#Singleton
#Provides
fun provideHitPhotoApi() = Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(HitApi::class.java)
#Singleton
#Provides
fun provideHitRepository(hitApi: HitApi) = HitRepository(hitApi)
#Singleton
#Provides
fun provideHitViewModelFactory(hitRepository: HitRepository) : HitViewModelFactory = HitViewModelFactory(hitRepository)
}
and try to inject ViewModelFactory in this fragment
#AndroidEntryPoint
class TripsFragment : Fragment() {
#Inject
lateinit var hitViewModelFactory: HitViewModelFactory
lateinit var hitViewModel: HitViewModel
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
hitViewModel = ViewModelProvider(requireActivity(),hitViewModelFactory)[HitViewModel::class.java ]
}
dependency for hilt :
Dagger - Hilt
implementation "com.google.dagger:hilt-android:2.28-alpha"
kapt "com.google.dagger:hilt-android-compiler:2.28-alpha"
implementation "androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-
alpha03"
kapt "androidx.hilt:hilt-compiler:1.0.0"

I also faced same issue earlier. Not sure why this fails but it seems code is correct.
So I have done with different approach.
I created view model like this below. And I inject HitApi directly in viewmodel constructor. Then just initialize like below. It takes care of creating viewmodel instance and hilt takes care of injection.No need of creating factory.
ViewModel:
#HiltViewModel
class HitViewModel #Inject constructor(
var api: HitApi) : ViewModel() {
}
Fragment:
#AndroidEntryPoint
class TripsFragment : Fragment() {
private val viewModel by viewModels<HitViewModel>() //No need of inject annotation.
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
}
}
Make sure you have these dependancies.
//For activity
implementation 'androidx.activity:activity-ktx:1.5.1'
//For fragment
implementation 'androidx.fragment:fragment-ktx:1.5.1'

the problem solved when i change the dependency of Hilt to :
implementation "com.google.dagger:hilt-android:2.38.1"
kapt "com.google.dagger:hilt-compiler:2.38.1"
implementation "androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha03"
kapt "androidx.hilt:hilt-compiler:1.0.0"
and for these dependencies it should replace the InstallIn anotation for module class to SingletonComponent::class like
#Module
#InstallIn(SingletonComponent::class)
object AppModule { ... }

Related

how to create class of findViewById?

I am a beginner ,https://i.stack.imgur.com/k8z9T.jpgin the android studio whenever I try to use findViewById.It shows an error and they ask me to create its variable but I don't know how to create it . Please tell me, I am stuck here.
You just created a function outside of your MainActivity. You have to create it inside of your activity. According to your screenshot, you just try to create a Top-level Function not a Function because it's outside of your activity. When you need to create a Function you have to create that inside your activity. Keep your eyes on Curley Brackets {}.
See the explanation to understand.
In your screenshot
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
} /* Your activity end's on here*/
private fun addNickName(view: View) {
// Your instance
// val editText = findViewById<EditText>(R.id.nickName_edit)
}
So the answer is
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
private fun addNickName(view: View) {
// Your instance
// val editText = findViewById<EditText>(R.id.nickName_edit)
}
} /* Your activity end's on here*/
Hope you understand!. Thank you

How can I preview a Compose view when it need a parameter in Android Studio?

I can run Code A in Android Studio, I hope to preview UI when I'm designing, so I added Code B to Code A.
But Code B can't work, why? How can I fix it?
Code A
class MainActivity : ComponentActivity() {
private val handleMeter by viewModels<HandleMeter>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
SoundMeterTheme {
Surface(color = MaterialTheme.colors.background) {
Greeting(handleMeter)
}
}
}
}
}
#Composable
fun Greeting(handleMeter: HandleMeter) {
...
}
Code B
#Preview(showBackground = true)
#Composable
fun DefaultPreview() {
SoundMeterTheme {
val handleMeter by viewModels<HandleMeter>()
Greeting(handleMeter)
}
}
Unfortunately, you can't.
Preview does not support creating ViewModels and NavHost yet, for our bad.
But what you can do instead is to use a fake data or a static data into ui, until the design finish and when you are ready to actually run it, replace the static data with the view model data.
You can use dagger and hilt to inject the view model constructor and then call up the hilt view model in the preview e.g.
#HiltViewModel
class DataFieldsViewModel #Inject constructor(
) : ViewModel() {
Then in your preview code for your composable
#Preview(showBackground = true)
#Composable
fun PreviewDataFieldsScreen() {
DataFieldsScreen(
navController = rememberNavController(),
viewModel = hiltViewModel()
)
}

Accessing a public class (Kotlin)

I am new to kotlin and android studio's (which I am using), so this is very simple, but I ran into this problem while working on a tutorial
The issue is very simple:
I have two kotlin classes (MainActivity and why). Why contains a function test that I want to call in MainActivity.
How do I do this?
In the tutorial I simply call it like so in MainActivity
why.test()
(full code below)
But when I try to run doing this I get the error:
"Unresolved reference: test on line 13" (where I call test).
Why is this happening? How do I get this to work?
Code:
MainActivity class:
package com.example.tester
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
work()
}
private fun work() {
why.test()
}
}
test function in why class: (test does nothing in this example)
package com.example.tester
class why {
fun test() {
var i = 0;
}
}
I think "test" should be static (or create object of why), like so:
package com.example.tester
class why {
companion object {
fun test() {
var i = 0;
}
}
}
See What is the equivalent of Java static methods in Kotlin?

Struggling to transition code from AppcompatActivity to Fragment. Unable to use setSupportActionBar(toolbar)

I'm trying to transition from using AppcomatActivity to Fragment, because I'm updating my app but I've run into a problem. I get a "Unresolved reference: supportFragmentManager" and "Unresolved reference: setSupportActionBar"(That's all the Logcat shows me) when I try to run my app. Now I chose to switch to using fragments, because I also want to change the apps UI, usage is quicker than the previous version. Anyway here's my code:
Recorder Fragment
package com.khumomashapa.notes.fragments
import android.os.Build
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toolbar
import androidx.annotation.RequiresApi
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentPagerAdapter
import androidx.viewpager.widget.ViewPager
import com.astuetz.PagerSlidingTabStrip
import com.khumomashapa.notes.R
import kotlinx.android.synthetic.main.toolbar3.*
class RecorderFragment : Fragment() {
private var tabs: PagerSlidingTabStrip? = null
private var pager: ViewPager? = null
#RequiresApi(Build.VERSION_CODES.LOLLIPOP)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
activity?.title = "Recorder";
pager = pager?.findViewById<View>(R.id.pager) as ViewPager
pager!!.adapter = MyAdapter(supportFragmentManager)
tabs = tabs?.findViewById<View>(R.id.tabs) as PagerSlidingTabStrip
tabs!!.setViewPager(pager)
val toolbar = toolbar?.findViewById<View>(R.id.toolbar) as Toolbar
toolbar.popupTheme = R.style.ThemeOverlay_AppCompat_Light
setSupportActionBar(toolbar)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_recorder, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
}
inner class MyAdapter(fm: FragmentManager?) : FragmentPagerAdapter(
fm!!
) {
private val titles = arrayOf(
getString(R.string.tab_title_record),
getString(R.string.tab_title_saved_recordings)
)
override fun getItem(position: Int): Fragment {
when (position) {
0 -> {
return RecordFragment.newInstance(position)
}
1 -> {
return FileViewerFragment.newInstance(position)
}
}
return null!!
}
override fun getCount(): Int {
return titles.size
}
override fun getPageTitle(position: Int): CharSequence? {
return titles[position]
}
}
companion object {
private val LOG_TAG = RecorderFragment::class.java.simpleName
}
}
The purpose of this class is to show a view pager that can switch between to other fragments I've already created. I was able to fix the other errors relating to this like the "Unresolved reference for findViewById" and the "MyAdapter class".
For a Fragment, there are actually two relevant fragment managers. Deciding which one to use depends on your use case.
The Child Fragment Manager
A fragment has a child fragment manager which is responsible for managing its child/nested fragments. You can obtain this with:
Fragment.getChildFragmentManager()
The Parent's Fragment Manager
A fragment also holds reference to it's parent's fragment manager. If the fragment is a direct child of an activity then his represents the activities fragment manager. Otherwise if the fragment is a child of another fragment, it represents the child fragment manager of the parent fragment. This can be obtained with:
Fragment.getParentFragmentManager()
Note that although Fragment has the method Fragment.getFragmentManager(), this is deprecated in favour of Fragment.getParentFragmentManager() so it shouldn't be used.
You can also technically get the activities fragment manager regardless by obtaining a reference to the fragment's activity with Fragment.getAcitivity() and then calling Activity.getSupportFragmentManager(). But generally the parent fragment manager is more useful and clear.

"lateinit" or "by lazy" when defining global android.widget var/val

When defining a global android.widget variable, e.g. TextView, is it preferable to use lateinit or by lazy? I initially thought using by lazy would be preferred as its immutable but I'm not entirely sure.
by lazy example:
class MainActivity: AppCompatActivity() {
val helloWorldTextView by lazy { findViewById(R.id.helloWorldTextView) as TextView }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
updateTextView(helloWorldTextView)
}
fun updateTextView(tv: TextView?) {
tv?.setText("Hello?")
}
}
lateinit example:
class MainActivity: AppCompatActivity() {
lateinit var helloWorldTextView: TextView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
helloWorldTextView = findViewById(R.id.helloWorldTextView) as TextView
updateTextView(helloWorldTextView)
}
fun updateTextView(tv: TextView?) {
tv?.setText("Hello?")
}
}
Are there any benefits of using one over the other when defining a global android.widget var/val? Are there any pitfalls with using by lazy to define a android.widget val? Is the decision just based on whether you want a mutable or immutable value?
There's one pitfall with by lazy. The widget property would be read-only and therefore technically final (in Java terms). But there's no documented guarantee that onCreate() is called only once for an instance. Also findViewById() could return null.
So using lateinit is preferable and you'll get an exception to tell you if the val was used before onCreate().
A third possibility would be Android synthetic properties. Then you don't need to worry about the variables at all.
import kotlinx.android.synthetic.main.activity_main.*
helloWorldTextView.text = "Hello?"

Resources