如何show/hide FloatingActionButton based on the fragment selected in Kotlin?
How to show/hide FloatingActionButton based on the fragment selected in Kotlin?
我有一个 activity,底部有 BottomNavigationView
、FloatingActionButton
和四个 fragment
。我只想在 'fragment1' 和 'fragment2 上显示 FloatingActionButton
并在选择 'fragment3' 或 'fragment4' 时隐藏它。启动应用程序时,'fragment1' 与 FloatingActionButton
一起显示,当我导航到 'fragment3' 时,我设法隐藏了它。但问题是当我从 'fragment3' 或 'fragment4' 导航回 'fragment1' 时,FloatingActionButton
仍然隐藏。
以下是我的'HomeActivity'
的xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.bottomappbar.BottomAppBar
android:id="@+id/bottom_appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom">
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/nav_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginEnd="16dp"
android:background="@android:color/transparent"
app:menu="@menu/bottom_nav_menu" />
</com.google.android.material.bottomappbar.BottomAppBar>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab_one"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_hide_category"
app:layout_anchor="@id/bottom_appbar"/>
<fragment
android:id="@+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="50dp"
app:defaultNavHost="true"
app:navGraph="@navigation/mobile_navigation"
app:layout_anchor="@id/bottom_appbar"/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
以下是我 'HomeActivity' 中的代码,我从 fragment
的 onCreateView
中调用它来隐藏它。
fun hideFabOne(){
binding.fabOne.hide()
}
然后我在 'HomeActivity' 中添加以下代码并从 fragment
调用它以显示 FAB,应用程序在启动时崩溃。
fun showFabOne(){
binding.fabOne.show()
}
编辑:
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.abc.xyz/com.abc.xyz.ui.activities.HomeActivity}: android.view.InflateException: Binary XML file line #112 in com.abc.xyz:layout/activity_home: Binary XML file line #112 in com.abc.xyz:layout/activity_home: Error inflating class fragment
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3835)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:4011)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2325)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:246)
at android.app.ActivityThread.main(ActivityThread.java:8633)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:602)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1130)
Caused by: android.view.InflateException: Binary XML file line #112 in com.abc.xyz:layout/activity_home: Binary XML file line #112 in com.abc.xyz:layout/activity_home: Error inflating class fragment
Caused by: android.view.InflateException: Binary XML file line #112 in com.abc.xyz:layout/activity_home: Error inflating class fragment
Caused by: kotlin.UninitializedPropertyAccessException: lateinit property binding has not been initialized
at com.abc.xyz.ui.activities.HomeActivity.showFabOne(HomeActivity.kt:110)
at com.abc.xyz.ui.fragments.HomeFragment.onCreateView(HomeFragment.kt:102)
at androidx.fragment.app.Fragment.performCreateView(Fragment.java:2963)
at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:518)
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:1647)
at androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:3128)
at androidx.fragment.app.FragmentManager.dispatchViewCreated(FragmentManager.java:3065)
at androidx.fragment.app.Fragment.performViewCreated(Fragment.java:2988)
at androidx.fragment.app.FragmentStateManager.ensureInflatedView(FragmentStateManager.java:392)
at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:281)
at androidx.fragment.app.FragmentLayoutInflaterFactory.onCreateView(FragmentLayoutInflaterFactory.java:140)
at androidx.fragment.app.FragmentController.onCreateView(FragmentController.java:135)
at androidx.fragment.app.FragmentActivity.dispatchFragmentsOnCreateView(FragmentActivity.java:319)
at androidx.fragment.app.FragmentActivity.onCreateView(FragmentActivity.java:298)
at android.view.LayoutInflater.tryCreateView(LayoutInflater.java:1067)
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:995)
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:959)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:1121)
at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:1082)
at android.view.LayoutInflater.inflate(LayoutInflater.java:680)
at android.view.LayoutInflater.inflate(LayoutInflater.java:532)
at com.abc.xyz.databinding.ActivityHomeBinding.inflate(ActivityHomeBinding.java:59)
at com.abc.xyz.databinding.ActivityHomeBinding.inflate(ActivityHomeBinding.java:53)
at com.abc.xyz.ui.activities.HomeActivity.onCreate(HomeActivity.kt:38)
2021-11-10 11:23:40.557 13300-13300/com.abc.xyz E/AndroidRuntime: at android.app.Activity.performCreate(Activity.java:8207)
at android.app.Activity.performCreate(Activity.java:8191)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1309)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3808)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:4011)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2325)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:246)
at android.app.ActivityThread.main(ActivityThread.java:8633)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:602)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1130)
由于您正在使用 app:navGraph
,因此请使用 addOnDestinationChangedListener
来控制 FloatingActionButton
的可见性。
在HomeActivity
里面做这个
navController.addOnDestinationChangedListener { _, destination, _ ->
if(destination.id == /*FRAGMENT ID*/) {
// hide
} else {
// show
}
}
我不知道你的代码,我用了一个演示来尝试重现崩溃和你想要的功能。
我重复使用了代码并将您的 XML 添加到我的 XML 文件中。
这是什么原因
我在不同的方法中添加了一些日志来检查调用顺序以查找崩溃原因。
我的解决方法:将代码移到Fragment中的onResume方法中
这是我的代码 MainActivity
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
lateinit var fab: FloatingActionButton
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.d("MainActivity", "TANCOLO===> onCreate 000 ")
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
val navView: BottomNavigationView = binding.navView
fab = findViewById(R.id.fab_one)
Log.d("MainActivity", "TANCOLO===> onCreate 111, fab = $fab ")
val navController = findNavController(R.id.nav_host_fragment_activity_main)
val appBarConfiguration = AppBarConfiguration(
setOf(
R.id.navigation_home, R.id.navigation_dashboard, R.id.navigation_notifications
)
)
setupActionBarWithNavController(navController, appBarConfiguration)
navView.setupWithNavController(navController)
}
HomeFragment
class HomeFragment : Fragment() {
private lateinit var homeViewModel: HomeViewModel
private var _binding: FragmentHomeBinding? = null
// This property is only valid between onCreateView and
// onDestroyView.
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
Log.d("HomeFragment", "TANCOLO===> onCreateView() ")
homeViewModel =
ViewModelProvider(this).get(HomeViewModel::class.java)
_binding = FragmentHomeBinding.inflate(inflater, container, false)
val root: View = binding.root
val textView: TextView = binding.textHome
homeViewModel.text.observe(viewLifecycleOwner, Observer {
textView.text = it
})
return root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
Log.d("HomeFragment", "TANCOLO===> onViewCreated ")
//get the FAB and show it.
// (requireActivity() as MainActivity).fab.apply {
// Log.d("HomeFragment", "TANCOLO===> onViewCreated, fab = $this ")
// visibility = View.VISIBLE
// }
}
override fun onResume() {
super.onResume()
Log.d("HomeFragment", "TANCOLO===> onResume ")
//get the FAB and show it.
(requireActivity() as MainActivity).fab.apply {
Log.d("HomeFragment", "TANCOLO===> onResume, fab = $this ")
visibility = View.VISIBLE // or View.GONE
}
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
我有一个 activity,底部有 BottomNavigationView
、FloatingActionButton
和四个 fragment
。我只想在 'fragment1' 和 'fragment2 上显示 FloatingActionButton
并在选择 'fragment3' 或 'fragment4' 时隐藏它。启动应用程序时,'fragment1' 与 FloatingActionButton
一起显示,当我导航到 'fragment3' 时,我设法隐藏了它。但问题是当我从 'fragment3' 或 'fragment4' 导航回 'fragment1' 时,FloatingActionButton
仍然隐藏。
以下是我的'HomeActivity'
的xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.bottomappbar.BottomAppBar
android:id="@+id/bottom_appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom">
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/nav_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginEnd="16dp"
android:background="@android:color/transparent"
app:menu="@menu/bottom_nav_menu" />
</com.google.android.material.bottomappbar.BottomAppBar>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab_one"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_hide_category"
app:layout_anchor="@id/bottom_appbar"/>
<fragment
android:id="@+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="50dp"
app:defaultNavHost="true"
app:navGraph="@navigation/mobile_navigation"
app:layout_anchor="@id/bottom_appbar"/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
以下是我 'HomeActivity' 中的代码,我从 fragment
的 onCreateView
中调用它来隐藏它。
fun hideFabOne(){
binding.fabOne.hide()
}
然后我在 'HomeActivity' 中添加以下代码并从 fragment
调用它以显示 FAB,应用程序在启动时崩溃。
fun showFabOne(){
binding.fabOne.show()
}
编辑:
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.abc.xyz/com.abc.xyz.ui.activities.HomeActivity}: android.view.InflateException: Binary XML file line #112 in com.abc.xyz:layout/activity_home: Binary XML file line #112 in com.abc.xyz:layout/activity_home: Error inflating class fragment
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3835)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:4011)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2325)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:246)
at android.app.ActivityThread.main(ActivityThread.java:8633)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:602)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1130)
Caused by: android.view.InflateException: Binary XML file line #112 in com.abc.xyz:layout/activity_home: Binary XML file line #112 in com.abc.xyz:layout/activity_home: Error inflating class fragment
Caused by: android.view.InflateException: Binary XML file line #112 in com.abc.xyz:layout/activity_home: Error inflating class fragment
Caused by: kotlin.UninitializedPropertyAccessException: lateinit property binding has not been initialized
at com.abc.xyz.ui.activities.HomeActivity.showFabOne(HomeActivity.kt:110)
at com.abc.xyz.ui.fragments.HomeFragment.onCreateView(HomeFragment.kt:102)
at androidx.fragment.app.Fragment.performCreateView(Fragment.java:2963)
at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:518)
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:1647)
at androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:3128)
at androidx.fragment.app.FragmentManager.dispatchViewCreated(FragmentManager.java:3065)
at androidx.fragment.app.Fragment.performViewCreated(Fragment.java:2988)
at androidx.fragment.app.FragmentStateManager.ensureInflatedView(FragmentStateManager.java:392)
at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:281)
at androidx.fragment.app.FragmentLayoutInflaterFactory.onCreateView(FragmentLayoutInflaterFactory.java:140)
at androidx.fragment.app.FragmentController.onCreateView(FragmentController.java:135)
at androidx.fragment.app.FragmentActivity.dispatchFragmentsOnCreateView(FragmentActivity.java:319)
at androidx.fragment.app.FragmentActivity.onCreateView(FragmentActivity.java:298)
at android.view.LayoutInflater.tryCreateView(LayoutInflater.java:1067)
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:995)
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:959)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:1121)
at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:1082)
at android.view.LayoutInflater.inflate(LayoutInflater.java:680)
at android.view.LayoutInflater.inflate(LayoutInflater.java:532)
at com.abc.xyz.databinding.ActivityHomeBinding.inflate(ActivityHomeBinding.java:59)
at com.abc.xyz.databinding.ActivityHomeBinding.inflate(ActivityHomeBinding.java:53)
at com.abc.xyz.ui.activities.HomeActivity.onCreate(HomeActivity.kt:38)
2021-11-10 11:23:40.557 13300-13300/com.abc.xyz E/AndroidRuntime: at android.app.Activity.performCreate(Activity.java:8207)
at android.app.Activity.performCreate(Activity.java:8191)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1309)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3808)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:4011)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2325)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:246)
at android.app.ActivityThread.main(ActivityThread.java:8633)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:602)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1130)
由于您正在使用 app:navGraph
,因此请使用 addOnDestinationChangedListener
来控制 FloatingActionButton
的可见性。
在HomeActivity
里面做这个
navController.addOnDestinationChangedListener { _, destination, _ ->
if(destination.id == /*FRAGMENT ID*/) {
// hide
} else {
// show
}
}
我不知道你的代码,我用了一个演示来尝试重现崩溃和你想要的功能。
我重复使用了代码并将您的 XML 添加到我的 XML 文件中。
这是什么原因
我在不同的方法中添加了一些日志来检查调用顺序以查找崩溃原因。
我的解决方法:将代码移到Fragment中的onResume方法中
这是我的代码 MainActivity
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
lateinit var fab: FloatingActionButton
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.d("MainActivity", "TANCOLO===> onCreate 000 ")
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
val navView: BottomNavigationView = binding.navView
fab = findViewById(R.id.fab_one)
Log.d("MainActivity", "TANCOLO===> onCreate 111, fab = $fab ")
val navController = findNavController(R.id.nav_host_fragment_activity_main)
val appBarConfiguration = AppBarConfiguration(
setOf(
R.id.navigation_home, R.id.navigation_dashboard, R.id.navigation_notifications
)
)
setupActionBarWithNavController(navController, appBarConfiguration)
navView.setupWithNavController(navController)
}
HomeFragment
class HomeFragment : Fragment() {
private lateinit var homeViewModel: HomeViewModel
private var _binding: FragmentHomeBinding? = null
// This property is only valid between onCreateView and
// onDestroyView.
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
Log.d("HomeFragment", "TANCOLO===> onCreateView() ")
homeViewModel =
ViewModelProvider(this).get(HomeViewModel::class.java)
_binding = FragmentHomeBinding.inflate(inflater, container, false)
val root: View = binding.root
val textView: TextView = binding.textHome
homeViewModel.text.observe(viewLifecycleOwner, Observer {
textView.text = it
})
return root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
Log.d("HomeFragment", "TANCOLO===> onViewCreated ")
//get the FAB and show it.
// (requireActivity() as MainActivity).fab.apply {
// Log.d("HomeFragment", "TANCOLO===> onViewCreated, fab = $this ")
// visibility = View.VISIBLE
// }
}
override fun onResume() {
super.onResume()
Log.d("HomeFragment", "TANCOLO===> onResume ")
//get the FAB and show it.
(requireActivity() as MainActivity).fab.apply {
Log.d("HomeFragment", "TANCOLO===> onResume, fab = $this ")
visibility = View.VISIBLE // or View.GONE
}
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}