在 MainActivity 中膨胀类 androidx.fragment.app.FragmentContainerView 时出错

Error inflating class androidx.fragment.app.FragmentContainerView in MainActivity

提问人:068-Arsyadana 提问时间:11/14/2023 更新时间:11/14/2023 访问量:29

问:

我一直在尝试制作和运行此代码,但是在膨胀MainActivity时似乎遇到了一些问题。这是错误代码

Process: com.example.cleansound, PID: 3096
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.cleansound/com.example.cleansound.MainActivity}: android.view.InflateException: Binary XML file line #20 in com.example.cleansound:layout/activity_main: Binary XML file line #20 in com.example.cleansound:layout/activity_main: Error inflating class androidx.fragment.app.FragmentContainerView
...
Caused by: android.view.InflateException: Binary XML file line #20 in com.example.cleansound:layout/activity_main: Binary XML file line #20 in com.example.cleansound:layout/activity_main: Error inflating class androidx.fragment.app.FragmentContainerView
Caused by: android.view.InflateException: Binary XML file line #20 in com.example.cleansound:layout/activity_main: Error inflating class androidx.fragment.app.FragmentContainerView
Caused by: androidx.fragment.app.Fragment$InstantiationException: Unable to instantiate fragment com.example.cleansound.ui.auth.LoginFragment: calling Fragment constructor caused an exception
...
Caused by: java.lang.reflect.InvocationTargetException
...
Caused by: java.lang.IllegalStateException: Fragment LoginFragment{f63f08e} (6390a8be-00a8-47bb-9ad0-2de7d86dd3d2) not attached to an activity.
at androidx.fragment.app.Fragment.requireActivity(Fragment.java:1000)
at com.example.cleansound.ui.auth.LoginFragment.<init>(LoginFragment.kt:20)

我一直在搜索这个问题的解决方案并匹配 NavHostFragment 和 Nav_Graph 上的 ID。但我认为 LoginFragment 出了点问题,我不知道那是什么。我可以给你我的一些代码,希望你能更好地理解我的问题

// com.example.cleansound.MainActivity
package com.example.cleansound

class MainActivity : AppCompatActivity() {

...

        val navView: BottomNavigationView = binding.navView
        val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
        navController = navHostFragment.navController

        navView.setupWithNavController(navController)

        val appBarConfiguration = AppBarConfiguration(
            setOf(
                R.id.navigation_home, R.id.navigation_dashboard, R.id.navigation_notifications
            )
        )
        setupActionBarWithNavController(navController, appBarConfiguration)

    }

    override fun onSupportNavigateUp(): Boolean {
        return navController.navigateUp() || super.onSupportNavigateUp()
    }

}
main_activity.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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">

    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/nav_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:defaultNavHost="true"
        app:navGraph="@navigation/mobile_navigation" />

    <com.google.android.material.bottomnavigation.BottomNavigationView
        android:id="@+id/nav_view"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="0dp"
        android:layout_marginEnd="0dp"
        android:background="?android:attr/windowBackground"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:menu="@menu/bottom_nav_menu" />


</androidx.constraintlayout.widget.ConstraintLayout>
// 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/loginFragment">

    <fragment
        android:id="@+id/navigation_home"
        android:name="com.example.cleansound.ui.home.HomeFragment"
        android:label="@string/title_home"
        tools:layout="@layout/fragment_home" />

    <fragment
        android:id="@+id/navigation_dashboard"
        android:name="com.example.cleansound.ui.dashboard.DashboardFragment"
        android:label="@string/title_dashboard"
        tools:layout="@layout/fragment_dashboard" />

    <fragment
        android:id="@+id/navigation_notifications"
        android:name="com.example.cleansound.ui.notifications.NotificationsFragment"
        android:label="@string/title_notifications"
        tools:layout="@layout/fragment_notifications" />

    <fragment
        android:id="@+id/loginFragment"
        android:name="com.example.cleansound.ui.auth.LoginFragment"
        android:label="LoginFragment"
        tools:layout="@layout/fragment_login">
        <action
            android:id="@+id/action_loginFragment_to_navigation_home"
            app:destination="@id/navigation_home"
            app:popUpTo="@id/loginFragment"
            app:popUpToInclusive="true" />
        <action
            android:id="@+id/action_loginFragment_to_registerFragment"
            app:destination="@id/registerFragment"
            app:popUpTo="@id/loginFragment"
            app:popUpToInclusive="true" />
    </fragment>

    <fragment
        android:id="@+id/registerFragment"
        android:name="com.example.cleansound.ui.auth.RegisterFragment"
        android:label="RegisterFragment"
        tools:layout="@layout/fragment_register">
        <action
            android:id="@+id/action_registerFragment_to_loginFragment"
            app:destination="@id/loginFragment" />
    </fragment>

</navigation>

它本身是负责用户的登录输入,并使用 ViewModel 来使用 for 登录。如果你愿意,我可以告诉你后者。LoginFragmentFirebase.authViewModel

// com.example.cleansound.ui.auth.LoginFragment
package com.example.cleansound.ui.auth

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.navigation.fragment.findNavController
import com.example.cleansound.R
import com.example.cleansound.databinding.FragmentLoginBinding
import com.google.firebase.Firebase
import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.auth.auth

class LoginFragment : Fragment() {
    private val viewModel : AuthViewModel = ViewModelProvider(
        this,
            AuthViewModelFactory.getInstance(requireActivity().application)
        ).get(AuthViewModel::class.java)
    private lateinit var auth: FirebaseAuth

    private var _binding: FragmentLoginBinding? = null
    private val binding get() = _binding!!

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {

        _binding = FragmentLoginBinding.inflate(inflater)
        val root = binding.root

        // Setting the Firebase auth instance
        auth = Firebase.auth

        viewModel.userData.observe(viewLifecycleOwner, Observer {firebaseUser ->
            if (firebaseUser != null) {
                findNavController().navigate(R.id.action_loginFragment_to_navigation_home)
            }
        })

        return root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        // Redirecting to the register page
        binding.signInBtn.setOnClickListener {
            val email = binding.emailEditSignUp.text.toString()
            val password = binding.passEditSignUp.text.toString()

            if (!email.isNotEmpty() && !password.isNotEmpty()) {
                viewModel.login(email, password)
            }
        }

        binding.signUpText.setOnClickListener {
            findNavController().navigate(R.id.action_loginFragment_to_registerFragment)
        }
    }


    override fun onDestroyView() {
        super.onDestroyView()
        _binding = null
    }

}


这是我的第一个项目,如果你们能帮助我并在我的工作项目中给我建议,我真的很高兴

android kotlin android-fragments firebase-authentication

评论


答:

1赞 ianhanniballake 11/14/2023 #1

取代:

private val viewModel : AuthViewModel = ViewModelProvider(
    this,
        AuthViewModelFactory.getInstance(requireActivity().application)
    ).get(AuthViewModel::class.java)

使用 by viewModels() 方法,如 ViewModel 作用域 API 指南中所述:

private val viewModel : AuthViewModel by viewModels {
    AuthViewModelFactory.getInstance(requireActivity().application)
}

错误消息中提到了原始代码不起作用的原因:

Caused by: java.lang.IllegalStateException: Fragment LoginFragment{f63f08e} (6390a8be-00a8-47bb-9ad0-2de7d86dd3d2) not attached to an activity.
at androidx.fragment.app.Fragment.requireActivity(Fragment.java:1000)
at com.example.cleansound.ui.auth.LoginFragment.<init>(LoginFragment.kt:20)

这说明,作为 Fragment 初始化的一部分(在 Fragment 实际经历任何生命周期方法之前),您正在调用 - 您正在调用它作为初始化对象的一部分。requireActivity()AuthViewModel

这是一个委托属性 - 实质上它为您控制 ViewModel 的创建。重要的是,它是懒惰的 - 它实际上只在你第一次访问对象时才创建 ViewModel。由于您访问它的第一个位置是 ,因此您的 fragment 已经经历了生命周期方法,这使得懒惰地创建 和调用它成为一件完全有效的事情。by viewModelsviewModelonCreateViewAuthViewModelFactoryrequireActivity()