Reputation: 4305
I am trying to migrate my project to view binding and I get an exception when I start my app.
My main activity contains a NavHostFragment like so:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
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:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:orientation="vertical">
...
<androidx.fragment.app.FragmentContainerView
android:id="@+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
app:defaultNavHost="true"
app:navGraph="@navigation/nav_graph" />
...
</LinearLayout>
And the first fragment loaded by default in the NavHostFragment is implemented like so:
class ToolListFragment : Fragment(R.layout.fragment_tool_list) {
...
private var _binding: FragmentToolListBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentToolListBinding.inflate(layoutInflater, container, false)
return binding.root
}
...
}
And here is the relevant part of the fragment's layout:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".ToolListFragment"
android:orientation="vertical"
android:background="@android:color/white">
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabMode="fixed"
app:tabGravity="fill">
<com.google.android.material.tabs.TabItem
android:id="@+id/tab_around_me"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:text="@string/tool_filter_around_me"/>
<com.google.android.material.tabs.TabItem
android:id="@+id/tab_assigned_to_me"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:text="@string/tool_filter_assigned_to_me"/>
<com.google.android.material.tabs.TabItem
android:id="@+id/tab_all_tools"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:text="All"/>
</com.google.android.material.tabs.TabLayout>
...
</LinearLayout>
As you can see, there is a TabItem with id tab_all. And yet here is the exception that crashes my app:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.herontrack.dev, PID: 12477
java.lang.NullPointerException: Missing required view with ID: com.herontrack.dev:id/tab_all_tools
at com.herontrack.databinding.FragmentToolListBinding.bind(FragmentToolListBinding.java:133)
at com.herontrack.databinding.FragmentToolListBinding.inflate(FragmentToolListBinding.java:78)
at com.herontrack.ToolListFragment.onCreateView(ToolListFragment.kt:107)
at androidx.fragment.app.Fragment.performCreateView(Fragment.java:2950)
at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:515)
at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:282)
at androidx.fragment.app.FragmentStore.moveToExpectedState(FragmentStore.java:112)
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1636)
at androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:3112)
at androidx.fragment.app.FragmentManager.dispatchViewCreated(FragmentManager.java:3049)
at androidx.fragment.app.Fragment.performViewCreated(Fragment.java:2975)
at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:543)
at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:282)
at androidx.fragment.app.FragmentStore.moveToExpectedState(FragmentStore.java:112)
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1636)
at androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:3112)
at androidx.fragment.app.FragmentManager.dispatchActivityCreated(FragmentManager.java:3056)
at androidx.fragment.app.FragmentController.dispatchActivityCreated(FragmentController.java:251)
at androidx.fragment.app.FragmentActivity.onStart(FragmentActivity.java:473)
at androidx.appcompat.app.AppCompatActivity.onStart(AppCompatActivity.java:210)
at com.herontrack.MainActivity.onStart(MainActivity.kt:100)
at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1435)
at android.app.Activity.performStart(Activity.java:8024)
at android.app.ActivityThread.handleStartActivity(ActivityThread.java:3475)
at android.app.servertransaction.TransactionExecutor.performLifecycleSequence(TransactionExecutor.java:221)
at android.app.servertransaction.TransactionExecutor.cycleToPath(TransactionExecutor.java:201)
at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:173)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2066)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:223)
at android.app.ActivityThread.main(ActivityThread.java:7656)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
So it crashes on this line in ToolListFragment.onCreateView()
_binding = FragmentToolListBinding.inflate(layoutInflater, container, false)
But I really don't understand why.
Upvotes: 5
Views: 11540
Reputation: 61
I found out if your use in fragment:
getLayoutInflater().inflate(R.layout.example_view,
viewgroup, false);
In side of the onCreateView(LayoutInflater, ViewGroup, Int) method, you can do this and then store var view on it: like:
View variable = getLayoutInflater().inflate(R.layout.example_view,
viewgroup, false);
TabItem item = variable.findViewById(R.id.tabLayoutItem);
/*
Other code
*/
It will work flawlessly. However I hugely agree with @Susan Thapa's answer and currently I am using his solution. Provided this answer just incase's somebody else can't follow that answer, because of xyz reason.
Upvotes: 0
Reputation: 581
The problem that you are facing is that TabItem is just a dummy view. Looking in the source code you can see that
/**
* TabItem is a special 'view' which allows you to declare tab items for a {@link TabLayout} within
* a layout. This view is not actually added to TabLayout, it is just a dummy which allows setting
* of a tab items's text, icon and custom layout. See TabLayout for more information on how to use
* it.
*
* @attr ref com.google.android.material.R.styleable#TabItem_android_icon
* @attr ref com.google.android.material.R.styleable#TabItem_android_text
* @attr ref com.google.android.material.R.styleable#TabItem_android_layout
* @see TabLayout
*/
//TODO(b/76413401): make class final after the widget migration
public class TabItem extends View {
So the ViewBinding fails to find the Tab
with the ID that you have specified as this is not added to the view hierarchy.
To fix this you have to remove the id
from the TabItem. If you need to access Tab
you can use the following code.
// to access first tab
binding.tabs.getTabAt(0)
Upvotes: 29