默认Fragment调用requireActivity().findViewById()返回null崩溃求助
项目中使用BottomNavigationView,默认/首个Fragment调用requireActivity().findViewById()获取父Activity的视图时返回null并崩溃,但该操作在其他Fragment中可正常执行,仅初始Fragment出现此问题。
相关代码
首个Fragment代码
override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { _binding = FragmentCallregBinding.inflate(inflater, container, false) return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) progress = requireActivity().findViewById(R.id.progress_circle) }
主Activity代码
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityHomeBinding.inflate(layoutInflater) setContentView(binding.root) setSupportActionBar(binding.toolbar) navView = binding.navView progress = binding.progressCircle val navController = findNavController(R.id.nav_host_fragment_activity_home) navView.setupWithNavController(navController) }
activity_main.xml代码
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" xmlns:app="http://schemas.android.com/apk/res-auto"> <androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:tools="http://schemas.android.com/tools" android:id="@+id/container" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_above="@id/nav_view" android:layout_alignParentEnd="true" android:layout_alignParentTop="true" android:layout_alignParentStart="true"> <com.google.android.material.appbar.AppBarLayout android:id="@+id/appbarlayout" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:fitsSystemWindows="true"> <androidx.appcompat.widget.Toolbar android:id="@+id/toolbar" android:background="@color/daynight" android:layout_width="match_parent" android:layout_height="wrap_content" app:layout_scrollFlags="scroll|enterAlways|snap"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent"> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:adjustViewBounds="true" android:scaleType="centerCrop" android:visibility="visible" android:src="@drawable/logo"/> </LinearLayout> </androidx.appcompat.widget.Toolbar> </com.google.android.material.appbar.AppBarLayout> <androidx.core.widget.NestedScrollView android:layout_width="match_parent" android:layout_height="match_parent" android:fillViewport="true" app:layout_behavior="@string/appbar_scrolling_view_behavior" tools:showIn="@layout/activity_home"> <fragment android:id="@+id/nav_host_fragment_activity_home" android:name="androidx.navigation.fragment.NavHostFragment" android:layout_width="match_parent" android:layout_height="match_parent" app:defaultNavHost="true" app:layout_constraintBottom_toTopOf="@id/nav_view" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:navGraph="@navigation/mobile_navigation" /> </androidx.core.widget.NestedScrollView> <ProgressBar android:id="@+id/progress_circle" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:indeterminate="true" android:indeterminateTint="@color/french_blue" android:indeterminateTintMode="src_in"/> </androidx.coordinatorlayout.widget.CoordinatorLayout> <com.google.android.material.bottomnavigation.BottomNavigationView android:id="@+id/nav_view" android:layout_width="match_parent" android:layout_height="wrap_content" app:menu="@menu/bottom_nav_menu" app:labelVisibilityMode="labeled" android:elevation="@dimen/_10sdp" android:layout_gravity="bottom" android:layout_alignParentBottom="true" android:layout_alignParentStart="true" android:layout_alignParentEnd="true" /> </RelativeLayout>
fragment布局说明
当前fragment布局为空
mobile_navigation.xml代码
<?xml version="1.0" encoding="utf-8"?> <navigation 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/mobile_navigation" app:startDestination="@+id/navigation_callreg"> <fragment android:id="@+id/navigation_callreg" android:name="neo.starcool.app.ui.fragments.CallRegFragment" android:label="@string/title_home" tools:layout="@layout/fragment_callreg" /> <fragment android:id="@+id/navigation_open" android:name="neo.starcool.app.ui.fragments.OpenCallFragment" android:label="@string/title_home" tools:layout="@layout/fragment_closed" /> <fragment android:id="@+id/navigation_pending" android:name="neo.starcool.app.ui.fragments.PendingFragment" android:label="@string/title_dashboard" tools:layout="@layout/fragment_opencall" /> <fragment android:id="@+id/navigation_closed" android:name="neo.starcool.app.ui.fragments.ClosedFragment" android:label="@string/title_notifications" tools:layout="@layout/fragment_pending" /> </navigation>
原因
初始Fragment是在Activity初始化NavController时立即加载的,其onViewCreated生命周期方法可能在Activity的视图层级完全构建完成之前执行,导致findViewById无法找到目标视图。而其他Fragment是在用户点击BottomNavigation时才加载,此时Activity视图已经完全初始化,因此可以正常获取。
解决方案
方案1:延迟到Activity视图完全初始化后获取
将视图获取逻辑移到Fragment的onActivityCreated方法中,该方法会在Activity的onCreate执行完成后调用:
override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) progress = requireActivity().findViewById(R.id.progress_circle) }
方案2:通过View.post延迟执行
利用View的post方法,将获取操作放到主线程消息队列末尾,确保Activity视图已构建完成:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) view.post { progress = requireActivity().findViewById(R.id.progress_circle) } }
方案3:通过Activity提供统一访问方法(更安全)
在主Activity中封装获取ProgressBar的方法,Fragment调用该方法避免直接操作Activity视图:
- 主Activity添加代码:
fun getProgressCircle(): ProgressBar { return binding.progressCircle }
- Fragment中调用:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) view.post { progress = (requireActivity() as HomeActivity).getProgressCircle() } }
方案4:使用共享ViewModel(推荐解耦方式)
通过ViewModel在Activity和Fragment之间共享视图引用或状态,避免Fragment直接依赖Activity视图:
- 创建共享ViewModel:
class MainViewModel : ViewModel() { lateinit var progressBar: ProgressBar }
- 主Activity中初始化:
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityHomeBinding.inflate(layoutInflater) setContentView(binding.root) // ...其他代码 val viewModel = ViewModelProvider(this)[MainViewModel::class.java] viewModel.progressBar = binding.progressCircle }
- Fragment中获取:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) val viewModel = ViewModelProvider(requireActivity())[MainViewModel::class.java] progress = viewModel.progressBar }
内容的提问来源于stack exchange,提问作者Abhinand A K
相关产品推荐
相关产品推荐

