ViewBinding - Included Layout Binding Resulting in Unresolved Reference - android

I am implementing ViewBinding in one of my fragments. This fragment has a layout included like so:
...
<androidx.core.widget.NestedScrollView
android:id="#+id/sv_sudf_container"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="#+id/btn_sudf_continue"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="#+id/eav_sudf_avatar">
<include
android:id="#+id/l_sudf_details"
layout="#layout/layout_sign_up_details_fields"/>
</androidx.core.widget.NestedScrollView>
...
I have followed this answer but it also does not work.
The generated view binding class for the fragment has the binding inside, however, the type for the attribute is View. When I then reference the View using binding.lSudfDetails the type is LayoutSignUpDetailFieldsBinding. Where this type is coming from I can't work out as there is no generated class with that name however, I would expect it would assign it the proper binding type. Here is the attribute in the FragmentSignUpDetailsBinding.java.
#NonNull
public final View lSudfDetails;
The bindings are all correctly setup however and it allow me to reference views within the nested layout but when I come to build I get unresolved reference errors. Lint does not complain when I reference them like this:
binding.lSudfDetails.etSudfDob
The compiler does fail however with errors such as this
Unresolved reference: etSudfDob
The binding itself is created according to the Android docs:
private var _binding : FragmentSignUpDetailsBinding? = null
private val binding get() = _binding!!
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
_binding = FragmentSignUpDetailsBinding.inflate(inflater,container,false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.tvSudfWelcome.text = getString(R.string.sign_up_welcome,getString(R.string.app_name))
binding.lSudfDetails.etSudfDob.setOnClickListener {
showYearSelection()
}
}
The tvSudfWelcome binding works its the nested binding it doesn't like.

If you're using Android Studio 3.6.0 sometimes gradle plugin fails to generate ViewBinding fields for included layouts. Please update to Android Studio 3.6.1 and gradle plugin version to 3.6.1.

If someone has similar problem... I solved my problem with adding width and height for this included view. It helped, I do not have any idea why, but this would be my solution:
<include
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:id="#+id/l_sudf_details"
layout="#layout/layout_sign_up_details_fields"/>

If someone has a similar problem - I solved mine using:
execute gradle task : prepareKotlinBuildScriptModel

changing the id name to the same as the layout name worked for me.
For eg:
<include
android:id="#+id/layout_sign_up_details_fields"
layout="#layout/layout_sign_up_details_fields"/>
I was working with databinding instead of viewbinding though

Related

How do I access my xml element in Fragment

I have { id 'kotlin-android'
id 'kotlin-android-extensions'
} plugins my build.gradle. However, I am not being able to access my xml elements
You need to define a variable first before assigning a listener to it .
For eg : If assumed that one is a button , and in the xml the id assigned to it is "one" like this :
<Button
android:id="#+id/one"
android:layout_width="200dp"
android:layout_height="50dp"
android:layout_marginTop="5dp"
android:layout_marginEnd="10dp"
android:text="Button"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
As you can see id for the button in the xml file is one
Then in your fragment you need to create a variable and assign to it using findViewById before using it : For eg :
class PinViewScreen : Fragment(){
// variable one is defined by type Button , you can define it of the type you have //designed it in xml
private lateinit var one : Button
Now , in your onViewCreated you have to initialize and use it :
override fun onViewCreated(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
one = view.findViewById(R.id.one)
one.setOnClickListener(pinClickListener)
}
It seems that your project uses kotlin - synthetics, Since they are deprecated I would not recommend you to use that and instead use the above method or viewBinding / databinding
Kotlin synthetic is deprecated in the latest kotlin version.
So to access the XML id you need to use data binding of ViewBinding.
for data binding
For view binding

DataBindingUtil inflates layout as null

I am developing a single activity application using the Android Jetpack navigation component. On one of the fragments I utilize the built-in data binding tools. Strangely enough, even though it was working just the week before, it just completely broke today for seemingly no reason.
The setup:
The fragment I use with binding has the following layout file:
<?xml version="1.0" encoding="utf-8"?>
<layout
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">
<data>
</data>
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/example_layout_root"
tools:context=".example.ExampleFragment"
android:background="#color/main_1"
>
...
</ScrollView>
</layout>
I have stripped the main content but it shows that I have a <layout> element as the root with both the data and the fragment layout part defined.
The fragment code is the following:
class ExamleFragment : Fragment() {
private val viewModel: ExampleViewModel by sharedViewModel()
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
// Inflate the layout for this fragment
val binding : FragmentExampleBinding = DataBindingUtil.inflate(inflater, R.layout.fragment_example, container, false)
// This one also does not work
// val binding = FragmentExampleBinding.inflate(inflater, container, false)
binding.vm = viewModel
binding.lifecycleOwner = this
return binding.root
}
}
I use Koin to inject the viewmodel into the fragment.
When I try to inflate the layout (either with DataBindingUtil or the generated FragmentExampleBinding class) it results in the following exception:
java.lang.IllegalStateException: FragmentExampleBin…flater, container, false) must not be null at
com.example.fragments.ExampleFragment.onCreateView(ExampleFragment.kt:38)
at androidx.fragment.app.Fragment.performCreateView(Fragment.java:2698)
at ...
And the stack goes straight down into OS territory so basically it doesn't give any useful information. I can't figure out why the result is null.
What I have tried:
I have tried inflating the layout with inflater.inflate(...) and it
works just fine but that way I can't use the data bindings.
I have tried removing EVERYTHING from the layout description that's
not absolutely neccessary and it still does not work.
I have tried reverting to previous commits (as far back as the first
working version with binding) and even though it used to work before,
it does not work now.
I even restarted and reset the emulator, tried different images, but
to no avail.
Has anyone encountered anything similar to this? The code really 'broke' over the weekend as I have just tested it the Friday before and it worked properly.
EDIT:
Okay, so I somehow managed to fix it.
I started experimenting with creating another fragment with data binding to see whether things are broken for all fragments or just that specific one. The new one seemed to work just fine but strangely, the old, broken fragment still refused to work even though they were basically the same.
So I did Clean + Rebuild (which I swear I performed as a first attempt to fix the issue) and it kind of just fixed it.
In my multi-module project, the reason for this problem was the fact that there was some kind of package name clash.
If you are getting this error without an apparent reason, double check the AndroidManifest.xml files of your data binding enabled modules.
Making sure all of the data binding enabled modules use unique package names solved the problem for me.
I spent hours to find the root of this problem hopefully others read this answer and won't spend that much.
Dont use DataBindingUtil for fragments, run your app once and android studio will generate a binding class for that fragment based off the name of your xml file, in your case the xml file is call fragment_example, so the class generated will be FragmentExampleBinding, then you call FragmentExampleBinding.inflate(....) like this:
val binding = FragmentExampleBinding.inflate(inflater, container, false);
Do it this way :
class ExamleFragment : Fragment() {
private val viewModel: ExampleViewModel by sharedViewModel()
lateinit binding:FragmentExmaple
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
// Inflate the layout for this fragment
binding = DataBindingUtil.inflate(inflater, R.layout.fragment_example, container, false)
binding.vm = viewModel
binding.lifecycleOwner = this
return binding.root
}
}
please I need you to paste your error here
in my case I just forgot to add #AndroidEntryPoint to my fragment class

Databinding, Binding class can't find RealmObjects

I was using databinding with realm objects and after updating android studio and gradle the auto generated Binding classes can't find the RealmDb classes or directory.
I tried removing the realm object out of the databinding but the project won't override the existing auto generated class and i keep getting the same error.
This is how i'm binding the data
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val binding = DataBindingUtil.inflate(inflater, R.layout.fragment_article_info, container, false) as FragmentArticleInfoBinding
val view = binding.root
viewModel = ArticleInfoVM(arguments?.getInt(KEY_ARTICLE_ID)!!)
binding.article = viewModel?.article.get()
return view
}
Why isn't the project able to find the classes?
Sometimes Android Studio has not found directories, if are there any error about different issues.
You can try to delete "RealmDB" package and clean All project then retry to insert emtyp RealmDB package. If are there any error, fix in order.

Can't access EditText or other UI components with Kotlin

I'm using Android Studio 3.0 RC2 & Kotlin.
When I try to access a UI component the app crashes unless I first write findViewById. I thought Kotlin was supposed to get rid of having to write findViewById line? The UI is a fragment and I'm trying to access from the same fragment code. Is there a way to not have to write findViewById?
These lines work:
var userNameField = view?.findViewById<EditText>(R.id.userNameTextField) as EditText
userNameField.setText("hello world")
This line doesn't work without findViewById line
userNameTextField.setText("hello world")
I even have
import kotlinx.android.synthetic.main.fragment_sign_in.*
The onCreateView() code:
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
// Inflate the layout for this fragment
var view = inflater!!.inflate(R.layout.fragment_sign_in, container, false)
var userNameField = view?.findViewById<EditText>(R.id.userNameTextField) as EditText
userNameField.setText("hello world")
return view
}
In the onCreateView just return the inflated view.
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
// Inflate the layout for this fragment
return inflater!!.inflate(R.layout.fragment_sign_in, container, false)
}
In the onViewCreated you can access your view components
override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
userNameField.setText("hello world")
}
I was brought here by searching for the same issue. I missed to add the kotlin-android-extensions to the build.gradle:
apply plugin: 'kotlin-android-extensions'
Was unable to access the ui components like OP faced looks like below was needed inside app gradle:
plugins {
...
id 'kotlin-android-extensions'
}
Even though after adding this line android studio was still not able to auto resolve the imports for kotlin synthetics so you may need to invalidate cache and restart.
If still not working then import manually depending on views
activity /fragment view: import
kotlinx.android.synthetic.main.<your_activity_view>.*
normal views :
import kotlinx.android.synthetic.main.<your_layout_view>.view.*
I faced a similar problem.
I had a private function. I'd also imported the kotlinx library. But I was not able to access the editText from that function.
I just removed the function and defined it again.
Voila! It worked.
For those who have this problem in 2021:
I know this question is asked in the past but since it is still relevant here I want to help others:
today I ran to this problem and tried the suggested ways and unfortunately, the
plugins { ... id 'kotlin-android-extensions' }
is not working anymore or more accurately is deprecated, so try to use the Jetpack view binding approach and you'll be good.
The message I got after adding the Gradle Kotlin Android extension dependency is:
The 'kotlin-android-extensions' Gradle plugin is deprecated. Please use this migration guide (click here) to start working with View Binding. (click here) and the 'kotlin-parcelize' plugin.

Kotlin Android extensions and retained fragment

I am using Kotlin Android extensions in my project and I came across some behavior that I am unable to understand. I use this code to retain my fragment in the activity:
val fragment = fragmentManager.findFragmentByTag("hello") ?: HelloFragment()
fragmentManager.beginTransaction()
.replace(R.id.fragment_container, fragment, "hello")
.commit()
This is the retained Fragment:
import kotlinx.android.synthetic.hello.*
public class HelloFragment : Fragment() {
val text = "Hello world!"
override fun onCreate(savedInstanceState: Bundle?) {
super<Fragment>.onCreate(savedInstanceState)
setRetainInstance(true)
}
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater?.inflate(R.layout.hello, container, false)
}
override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
super<Fragment>.onViewCreated(view, savedInstanceState)
text_view.setText(text) // <- does not work when retained
}
}
and its XML layout hello.xml:
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/text_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center" />
Everything works as expected – the text_view.setText() displays Hello world! on screen on the first launch. But when you rotate the screen the text_view.setText() does not work. This is weird because text_view is not nullable and has to reffer to some view. If you remove setRetainInstance(true) and leave the fragment recreate every time this problem disappears. Any thoughts what might cause this problem?
UPD: The issue is fixed now. You don't have to call clearFindViewByIdCache() manually anymore.
View cache is not cleared after calling onDestroyView(). There is an open issue.
For now, you can explicitly invoke clearFindViewByIdCache() in onDestroyView() to clear the cache. This method is part of the synthetic package so you have to import it
import kotlinx.android.synthetic.*
I have found the answer myself. The Fragment class does not inflate the layout directly – it has property view: View? which holds it. This should be pretty obvious since it is created with onCreateView. In order to access the properties within the view you have to set the import
import kotlinx.android.synthetic.hello.view.*
and then access the properties as follows
view?.text_view?.setText(text)
Note that these properties are nullable.
Just to clarify. The issue is fixed now. You don't have to pass clearFindViewByIdCache() anylonger. Please see issue tracker: https://youtrack.jetbrains.com/oauth?state=%2Fissue%2FKT-8073

Categories

Resources