问题
In my app I use NavController
with Fragments. In some fragments I need to access views on MainActivity
layout. I do that in this way:
val fab: FloatingActionButton = requireActivity().findViewById(R.id.fab)
This works properly as expected. However, one view does not want to be called and the findViewById()
returns null.
The layout structure is like this. In activity_main.xml
<androidx.drawerlayout.widget.DrawerLayout
<androidx.coordinatorlayout.widget.CoordinatorLayout
....>
<FrameLayout
android:id="@+id/frame"
.... />
<include
android:id="@+id/include"
layout="@layout/app_bar_main" />
</androidx.constraintlayout.widget.ConstraintLayout>
<com.google.android.material.navigation.NavigationView
android:id="@+id/nav"
.... />
</androidx.drawerlayout.widget.DrawerLayout>
is included app_bar_main.xml
<androidx.coordinatorlayout.widget.CoordinatorLayout
....>
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar"
....>
<com.google.android.material.appbar.CollapsingToolbarLayout
android:id="@+id/collapsing"
....>
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar"
.... />
</com.google.android.material.appbar.CollapsingToolbarLayout>
</com.google.android.material.appbar.AppBarLayout>
<include
android:id="@+id/content"
layout="@layout/content_main" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab"
.... />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
which includes content_main.xml
<androidx.core.widget.NestedScrollView
android:id="@+id/nested"
....>
<androidx.fragment.app.FragmentContainerView
android:id="@+id/host"
.... />
</androidx.core.widget.NestedScrollView>
The problem is, I need to access the NestedScrollView
and although all other views can be accessed, this one on the content_main.xml
cannot be accessed and returns null:
java.lang.NullPointerException: requireActivity().findViewById(R.id.nested) must not be null
EDIT:
class HomeFragment : Fragment() {
private lateinit var drawer: DrawerLayout
private lateinit var nested: NestedScrollView
private lateinit var fab: FloatingActionBar
private lateinit var bng: FragmentHomeBinding
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
setHasOptionsMenu(true)
bng = FragmentHomeBinding.inflate(layoutInflater, container, false)
return bng.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
drawer = requireActivity().findViewById(R.id.drawer)
fab = requireActivity().findViewById(R.id.fab)
nested = requireActivity().findViewById(R.id.nested) // This line throws exception
.........
}
}
As you can see, I use viewBinding
to handle Fragment's Views
s.
I do not think that this might be problem.
回答1:
So, if someone should look for a solution, here is an one that worked for me. First of all, I found out that I needed to reorder my XML
layouts so that put the contents of the content_main.xml
and the app_bar_layout.xml
directly into the activity_main.xml
like this:
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.widget.DrawerLayout 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/drawer"
...>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="@+id/coordinator"
...>
<com.google.android.material.appbar.AppBarLayout
... >
<com.google.android.material.appbar.CollapsingToolbarLayout
android:id="@+id/collapsing"
... >
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar"
... />
</com.google.android.material.appbar.CollapsingToolbarLayout>
</com.google.android.material.appbar.AppBarLayout>
<androidx.core.widget.NestedScrollView
android:id="@+id/nested"
... >
<androidx.fragment.app.FragmentContainerView
android:id="@+id/host"
... />
</androidx.core.widget.NestedScrollView>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab"
... />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
<com.google.android.material.navigation.NavigationView
android:id="@+id/nav"
... />
</androidx.drawerlayout.widget.DrawerLayout>
It might be not an optimal way, but without this step, the View
s on content_main.xml
could not be accessed, which is really a bullshit as before it properly worked. Actually, no idea, but it seems that Google changed something in its libraries or whatever that should be.
As a next step, View
s you want to access on the activity_main.xml
from a certain Fragment
, you have to make them public:
MainActivity.kt
class MainActivity : AppCompatActivity() {
...
lateinit var fab: FloatingActionButton
lateinit var nested: NestedScrollView
...
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
...
nested = findViewById(R.id.nested)
fab = findViewById(R.id.fab)
...
}
}
Now, they can be used in a Fragment
like this:
MyFragment.kt
class MyFragment : Fragment() {
...
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_my, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
...
var fab: FloatingActionButton
var nested: NestedScrollView
(activity as MainActivity).apply {
fab = this.fab
nested = this.nested
}
...
}
}
That's all guys! In this way, I could achieve my goals.
As mentioned above, this might not be an optimal way, but it worked for me.
Hopefully, this helps someone to fix this kind of problem.
Any improvements or suggestions are welcome and appreciated.
来源:https://stackoverflow.com/questions/65304976/access-view-on-content-main-xml-from-a-fragment