Access view on content_main.xml from a fragment?

橙三吉。 提交于 2021-01-07 01:28:02

问题


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 Viewss. 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 Views 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, Views 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!