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

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?"

Related

Passing data from one fragment to another (Kotlin)

I have a project with 2 fragments. I am looking to pass an iterable from the first fragment to the second. Using navArgs is not an option, since it makes the program crash. Bundle seems to only work with primary data types. Is there a way to go about it, without using some super hacky solution, like passing a string of all the data separated by commas as a string?
The modern way to do this is with a ViewModel (here and here or with the FragmentResult API (last link). Otherwise you're looking at doing it manually through the parent Activity - call a function on the Activity that passes your data to the other Fragment, that kind of thing.
If these Fragments are in separate Activities then you're looking at making your data Parcelable so it can go in a Bundle, or serialisation (e.g. the Kotlin Serialization library) so you can put it in a Bundle as a String, or persist it on disk so you can load it from the next Activity. Serialisation libraries are a robust way of turning objects and data into a stream of text (and other formats if you like) but there's nothing wrong with a String and some separator character if it's all you need, e.g. storing a list of indices or IDs
You can use a shared view model.
In your first fragment:
<code>class FirstFragment : Fragment() {
private lateinit var viewModel: SharedViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel = ViewModelProviders.of(requireActivity()).get(SharedViewModel::class.java)
viewModel.setData(yourIterable)
}
}
</code>
In your second fragment:
<code>class SecondFragment : Fragment() {
private lateinit var viewModel: SharedViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel = ViewModelProviders.of(requireActivity()).get(SharedViewModel::class.java)
val data = viewModel.getData()
}
}
</code>
And your <code>SharedViewModel</code>:
<code>class SharedViewModel : ViewModel() {
private val _data

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

Hilt - cannot be initialized ViewModelFactory in fragment

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 { ... }

Kotlin Android - saving mutableMap in savedInstanceState?

Is there a way to save map/mutableMap into savedInstanceState?
other than using ViewModel functions. I'm curious to know his point...
thanks in advance
I worked on it. wonder why i didn't find anyone asking about this before, probably there are better ways. but in case that someone needs to know how this is done. i did it this way...
i used onSaveInstanceState to check if the map was initialized, then proceeded
override fun onSaveInstanceState(savedInstanceState: Bundle) {
super.onSaveInstanceState(savedInstanceState)
if (::answersMap.isInitialized){
val keys = (answersMap.keys).toIntArray()
val values = (answersMap.values).toBooleanArray()
savedInstanceState.putIntArray(KEYS, keys)
savedInstanceState.putBooleanArray(VALUE, values)
}
}
also on the onCreate function I retrieved the arrays and created the mutablemap i needed
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val keysArray = savedInstanceState?.getIntArray(KEYS) ?: IntArray(0)
val valuesArray = savedInstanceState?.getBooleanArray(VALUE) ?: BooleanArray(0)
answersMap = mutableMapOf<Int, Boolean>().apply {
for (i in keysArray.indices) this [keysArray[i]] = valuesArray[i]
}
in case you were wondering about the answerMap type
private lateinit var answersMap : MutableMap<Int, Boolean>
IT WORKED!!!

findViewById() unresolved reference in codelabs

Good day, y'all!
Today I was doing the Google Codelabs for Kotlin. I'm trying to use findViewById() in a function but it shows as an unresolved reference. I bet it's a fairly easy problem but I can't figure it out. this is my code
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
findViewById<Button>(R.id.done_button).setOnClickListener { addNickname(it) }
}
}
private fun addNickname(view: View) {
val editText = findViewById<EditText>(R.id.nickname_edit)
val nicknameTextView = findViewById<TextView>(R.id.nickname_text)
nicknameTextView.text = editText.text
nicknameTextView.visibility = View.VISIBLE
editText.visibility = View.GONE
view.visibility = View.GONE
}
When I try to assign the editText and the nickmaneTextView vals to the views I use findViewById() but it shows as unresolved reverence.
it's quite basic so I don't know where is the issue
I'll be very grateful for your help
Edit:
if I use
val editText = view.findViewById<EditText>(R.id.nickname_edit)
val nicknameTextView = view.findViewById<TextView>(R.id.nickname_text)
I get an error saying that the nicknameTextView is null and it crashes
You should add view
val editText = view.findViewById<EditText>(R.id.nickname_edit)
val nicknameTextView = view.findViewById<TextView>(R.id.nickname_text)
Ok I figured it out, the fun must be created inside the
class MainActivity: AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
'here'
}
I knew it was a dumb problem :(
Thank you for your help

Resources