Issue
I am currently working my way through Udemy course The Complete Android 12 & Kotlin Development Masterclass. In one of the tutorial applications suddenly we start accessing layout views without any binding or findVievByID just by typing in id of the view in the kotlin activity. When I was coding along the tutorial I did have some differences in the projects eg. CompileSDK 31 instead of 29 but when I reached the point where I was supposed to add OnClickListener to the FloatingActionButton like this:
fabAddHappyPlace.setOnClickListener {
val intent = Intent(this@MainActivity, AddHappyPlaceActivity::class.java)
startActivity(intent)
}
I received an error. Android Studio did not want to allow me to use the object without binding or adding the view by its id. When I opened downloaded project I was able to access any view just like an object in any activity.
Please, explain to me how is this even possible. Below you can find following files from working project: build.gradle; activity_main.xml; mainActivity.kt; AndroidManifest.xml
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.happyplaces">
<application
android:allowBackup="false"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme"
tools:ignore="AllowBackup">
<activity
android:name=".MainActivity"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".AddHappyPlaceActivity"
android:label="ADD HAPPY PLACE"
android:screenOrientation="portrait"
android:theme="@style/CustomNoActionBarTheme"/>
</application>
</manifest>
build.gradle
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android {
compileSdkVersion 29
defaultConfig {
applicationId "com.happyplaces"
minSdkVersion 21
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'androidx.core:core-ktx:1.2.0'
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
// Adding an google material design dependency for using material components
implementation 'com.google.android.material:material:1.0.0'
testImplementation 'junit:junit:4.13'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fabAddHappyPlace"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="@dimen/fab_margin"
app:srcCompat="@drawable/ic_action_add_24dp" />
</FrameLayout>
MainActivity.kt
package com.happyplaces
import android.content.Intent
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
/**
* This function is auto created by Android when the Activity Class is created.
*/
override fun onCreate(savedInstanceState: Bundle?) {
//This call the parent constructor
super.onCreate(savedInstanceState)
// This is used to align the xml view to this class
setContentView(R.layout.activity_main)
// TODO (Step 1: Adding an click event to Fab button and calling the AddHappyPlaceActivity.)
// START
// Setting an click event for Fab Button and calling the AddHappyPlaceActivity.
fabAddHappyPlace.setOnClickListener {
val intent = Intent(this@MainActivity, AddHappyPlaceActivity::class.java)
startActivity(intent)
}
// END
}
}
Solution
This is called "Kotlin Synthetics" And unfortunately it is deprecated and no longer recommended, and should be replaced with explicit findViewById
.
Since you asked how it was possible to use it in your code, it's the plugins that you have integrated on top of your app-level gradle file which allows you to use them:
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
Here is an article why it's NOT RECOMMEDNED to use the synthetics plugin and why it got deprecated.
So clearly, the synthetic is NOT the way to go.
As of which is better? View Binding or explicit calling of findViewById?
Performance wise: ViewBinding is the way to go, since it generates public final fields in the generated Binding class for each view declared with an id, and the binding iterate once over the view hierarchy, extracting the inflated view objects with their IDs and assigning them to the fields.
On the other hand, findViewById
will have to iterate over the view hierarchy every time you call it, and that's not great performance wise.
Here's some other points declared in the docs:
Null safety: Since view binding creates direct references to views, there's no risk of a null pointer exception due to an invalid view ID. Additionally, when a view is only present in some configurations of a layout, the field containing its reference in the binding class is marked with @Nullable.
Type safety: The fields in each binding class have types matching the views they reference in the XML file. This means that there's no risk of a class cast exception.
Answered By - Ahmad Hamwi
Answer Checked By - Willingham (JavaFixing Volunteer)