Usually we do not use findViewById (R.id.listView) in kotlin because Android studio do it for us automatically (we do not need to find view).
But this example shows that we need to you it (in this line of code):
val listView = findViewById<ListView>(R.id.listView) as ListView.
Why do we use this line in this example? How to not use it?
If you're using findViewById from Kotlin, you should never need a cast (from API level 26 and up). You should use it one of these two ways:
val myTV1 = findViewById<TextView>(R.id.myTextView)
val myTV2: TextView = findViewById(R.id.myTextView)
And then you can access its properties via these variables:
myTV1.text = "testing"
This is a perfectly valid way of getting View references and using them in Kotlin as it is.
However, if you also have Kotlin Android Extensions enabled in the project (by the apply plugin: 'kotlin-android-extensions' line in your module level build.gradle file), you also can refer to your Views by their IDs via the synthetic properties it provides, just make sure you have the correct imports, for example:
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
myTextView.text = "testing"
}
}
Note that Kotlin Android Extensions is entirely optional to use, and if you do use it, findViewById of course is still available if for whatever reason you want to mix the two methods.
In general, when you need a view from a layout file, you can import the following:
kotlinx.android.synthetic.main.<layout filename>.<id of view>
If you need all of the views from a layout file, you can use:
kotlinx.android.synthetic.main.<layout filename>.*
It's only applicable for Kotlin
Step 1:
Add this Kotlin Android Extensions in our code
Gradle Scripts -> build.gradle(Module)
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
NOTE: Now you can directly access the view id without using findViewbyId, in case if you face any red line press alt+enter, or import this in KT file.
import kotlinx.android.synthetic.main.activity_your_layout.*
Related
Following an upgrade of android-studio, kotlin plugins and sourceCompatibility to Java 8 in an android project, many layout ids are not any more recognized (but strangely not all).
It looks like field name with underscores are not recognized in binding.
For instance:
whith the following declaration in the layout
<TextView
android:id="#+id/alert_hist_item__equipement"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="5px"
android:text="#string/Device"
android:textStyle="bold"
/>
in the code
alert_hist_item__equipement.text = "x"
is not recognized but
alertHistItemEquipement.text = "x"
is recognized.
If I change the field names in the layouts to equivalent camel case names (either only in the code, with the names in the layout still having underscores; or both in code and layout) the fields are recognized.
It is so painful for many reasons:
I have dozens of layouts all with id names containing underscore
I find names with underscore easier to read (and my team too)
it was never specified that android:id fields should follow certain rules
is this possible to change that behavior ? Could it be a bug ?
Please add following id 'kotlin-android-extensions' to you app level gradle.
plugins {
id 'kotlin-android-extensions'
}
According to Kotlin documentation they are deprecated but they are still working for me.
As and alternate they provide us with data and view binding. For you View Binding should work!
here is the example:
add following to your app level gradle:
buildFeatures{
viewBinding true
}
then in you activity declare as following (Assuming you activity name is ManuelYguel)
class ManuelYguel : AppCompatActivity() {
private lateinit var binding: ActivityManuelYguelBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityManuelYguelBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.alertHistItemEquipement.text = "x"
}
}
It is a sad and unfortunate constraint that comes from the new autogenerated binding class. This is specified in https://developer.android.com/topic/libraries/data-binding/expressions
it says:
Note: The binding class converts IDs to camel case.
I followed multiple tutorials on how to implement this new nonsense.
I removed
apply plugin: `kotlin-android-extensions`
from gradle, added:
android {
...
buildFeatures {
viewBinding true
}
}
and then in activity (not MainActivity but another one because it's the first one that creates error on rebuild):
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
totorials are showing that the views have to be used this way now:
binding.whatEverView
but I still have unresolved reference on those views.
Now I'm wondering if the correct class gets automatically imported which is:
import de.blaa.blaaaa.databinding.ActivityMainBinding
None of the tutorials are showing what actual class supposed to be imported so is this the correct one? What am I missing?
Check that the id of "whateEverView" doesn't have a leading underscore or something.
In my case, I found that my field had an id of "_what_ever_view" in the layout xml. Changing it to "what_ever_view" fixed it.
Not sure if the problem is limited to leading underscores, or even if it's a specific version of the plug-in, but it does appear to be a bug. In my case, the issue field appears totally valid during code editing (code completion knows about the field), but the "unresolved" issue appears during build time.
What is the official coding standard for declaring and initializing views in Android using Kotlin? If it is case by case bases what cases would you use what?
I've seen these ways:
//Kotlin extensions
onCreate
...
btn_xml_tag.setOnClickListener {
...
}
//More like java
private lateinit var button : Button
...
onCreate
...
button = findViewById(R.id.button)
button?.setOnClickListener{...}
//Lazy
private val button : Button by lazy {
findViewById(R.id.button_view) as Button
}
...
onCreate
...
button.setOnClickListener{...}
You should try Android Data Binding. That is the standard way of doing it. As per the new architecture components, you don't need to access the instance of a view from code, but instead bind the data directly in the XML. The new architecture standard follows MVVM architecture.
With respect o your question, the best of the three options would be to use Kotlin Extensions, considering the readability and length of code. That is, the following would be the best approach(Though I would give the view an ID that would match with kotlin naming standards):
btnXmlTag.setOnClickListener {
...
}
You can use android DataBinding and inflating those views directly in your Activity without intialization.
just you need to add these syntax in your gradle file.
dataBinding{ enabled=true }
and in your layout file parent should be in layout tag
I'm able to access my layout views(like button, TextView, EditText etc) directly inside the activity by their ids defined in layout xml file in Kotlin android project.
So, do we need to use findviewbyId(), or butterknife lib in kotlin android project?
StudentActivity.kt
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val studentViewModel = getStudentViewModel()
updateButton.setOnClickListener {
val name = nameEditText.text.toString()
val age = ageEditText.text.toString()
val subject = subjectEditText.text.toString()
studentViewModel.updateStudentRecord(
Student(
name,
Integer.parseInt(age),
subject
)
)
}
}
}```
ButterKnife is an old solution for view binding. It has less boilerplate code than old findviewbyId way but because of annotation processors it impacts build time speed and doesn't provide Null safety and Type safety. A better solution is kotlinx.android.synthetic that you used in your example but it has some problems too. For example, if you set your content view to a layout, then type an id that only exists in a different layout, the IDE lets you autocomplete and add the new import statement. Unless the developer specifically checks to make sure their import statements only import the correct views, there is no safe way to verify that this won’t cause a runtime issue. As everything is global, one has to be careful to make sure that they only use views they are expecting and ignore the autocomplete. DataBinding and ViewBinding are the best solutions for now. They are similar at first look. Both generate binding classes that you can use to reference views directly with support Null safety and Type safety, but there are differences:
DataBinding approach needs you to add <layout> tag to your XML layout in order to enable the data binding process
ViewBinding doesn’t support layout variables or layout expressions, so it can’t be used to bind layouts with data in XML
ViewBinding is faster than DataBinding in build time because it does n't use annotation processors.
I think you will not use anymore, just if you want? But I believe that is not because of the Synthetic Accessors, it's because of the Data Bindings and the announced this year, View Binding
Nope, here is the the magic of kotlin. Just use your id from the layout file (xml) and directly use it. Like:
button.setOnClickListener {}
and so on. hope it will help.
I want to do Kotlin in Android but I have some issues with mutables. Let's say I'm doing a TextView that's accessed across multiple methods.
var tv: TextView? = null
override fun onCreate(savedInstanceState: Bundle?) {
tv = find(R.id.tv)
}
fun clearText(){
tv?.setText("")
}
fun setText(text: String) {
tv?.setText(text)
}
This works, but having tv? every time I use the TextView is a code smell. Is there a better way of doing this? I feel like I should be using val instead of var but I can't find a way to put it in.
There's two ways I know of to solve this. Lateinit and Kotlin's Android extensions.
Lateinit lets you assign a value to a non-null type later in the classes lifespan.
lateinit var textView: TextView
override fun onCreate(savedInstanceState: Bundle?){
textView = findViewById(R.id.view_text)
}
You can now access it without a null check.
Or, you can use Kotlin's extensions.
Add the plugin to your build.gradle
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
And import the generated accessors into your Activity, Fragment or whatever.
import kotlinx.android.synthetic.main.layout_name.*
text_view_id.text = "Hello World!"