Android多Fragment导航:横竖屏适配及跨Fragment导航疑问
问题描述
我为竖屏和横屏分别准备了两种布局:
竖屏布局代码
<?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:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <androidx.fragment.app.FragmentContainerView android:id="@+id/container" android:name="androidx.navigation.fragment.NavHostFragment" android:layout_width="match_parent" android:layout_height="match_parent" app:defaultNavHost="true" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:navGraph="@navigation/nav_graph" /> </androidx.constraintlayout.widget.ConstraintLayout>
横屏布局代码
<?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:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <androidx.fragment.app.FragmentContainerView android:id="@+id/container_channel" android:layout_width="0dp" android:layout_height="0dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toStartOf="@id/container_messages" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent"/> <androidx.fragment.app.FragmentContainerView android:id="@+id/container_messages" android:layout_width="0dp" android:layout_height="0dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toEndOf="@id/container_channel" app:layout_constraintTop_toTopOf="parent"/> </androidx.constraintlayout.widget.ConstraintLayout>
我使用的是androidx.navigation:navigation-fragment-ktx:2.4.1版本,竖屏模式下Fragment切换正常。现在希望点击第一个Fragment中的按钮改变第二个Fragment的状态,当前编写的代码如下:
if (resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) { supportFragmentManager.beginTransaction() .replace(R.id.container_channel, ChannelsFragment()) .replace(R.id.container_messages, MessagesFragment()) .commit() } else { val navHostFragment = supportFragmentManager.findFragmentById(R.id.container) as NavHostFragment navController = navHostFragment.navController }
请问:
- 是否可以使用Navigation库实现横竖屏不同的导航逻辑?
- 是否可以通过该库从第一个Fragment管理第二个Fragment的导航?
回答
1. 可以用Navigation实现横竖屏不同的导航逻辑
Navigation库完全支持适配横竖屏的差异化导航需求,推荐两种实现方式:
- 条件导航适配:在同一个导航图中,结合屏幕方向判断,通过
app:popUpTo、app:launchSingleTop等属性动态调整导航行为。比如竖屏用单容器栈式导航,横屏切换为双容器并列导航。 - 多NavHost配置:在横屏布局的两个
FragmentContainerView中分别设置NavHostFragment,绑定对应导航图(或共享同一导航图)。示例修改横屏布局:
然后在Activity中根据方向获取对应NavController:<!-- 左侧频道容器 --> <androidx.fragment.app.FragmentContainerView android:id="@+id/container_channel" android:name="androidx.navigation.fragment.NavHostFragment" android:layout_width="0dp" android:layout_height="0dp" app:defaultNavHost="true" app:navGraph="@navigation/nav_graph_channels" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toStartOf="@id/container_messages" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent"/> <!-- 右侧消息容器 --> <androidx.fragment.app.FragmentContainerView android:id="@+id/container_messages" android:name="androidx.navigation.fragment.NavHostFragment" android:layout_width="0dp" android:layout_height="0dp" app:navGraph="@navigation/nav_graph_messages" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toEndOf="@id/container_channel" app:layout_constraintTop_toTopOf="parent"/>override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) if (resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) { val channelNavHost = supportFragmentManager.findFragmentById(R.id.container_channel) as NavHostFragment val messageNavHost = supportFragmentManager.findFragmentById(R.id.container_messages) as NavHostFragment channelNavController = channelNavHost.navController messageNavController = messageNavHost.navController } else { val navHostFragment = supportFragmentManager.findFragmentById(R.id.container) as NavHostFragment navController = navHostFragment.navController } }
2. 可以通过Navigation库从第一个Fragment管理第二个Fragment的导航
三种可靠实现方式:
- 共享ViewModel:创建共享ViewModel,第一个Fragment更新状态,第二个Fragment观察状态变化触发导航或UI更新。示例:
// 共享ViewModel class SharedNavigationViewModel : ViewModel() { val messageDestination = MutableLiveData<Int>() } // 第一个Fragment(频道列表) class ChannelsFragment : Fragment() { private val sharedViewModel: SharedNavigationViewModel by activityViewModels() fun onMessageItemClick(messageId: Int) { sharedViewModel.messageDestination.value = R.id.action_to_message_detail } } // 第二个Fragment(消息列表) class MessagesFragment : Fragment() { private val sharedViewModel: SharedNavigationViewModel by activityViewModels() private lateinit var navController: NavController override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) navController = NavHostFragment.findNavController(this) sharedViewModel.messageDestination.observe(viewLifecycleOwner) { destinationId -> navController.navigate(destinationId) } } } - Activity中转:第一个Fragment定义回调接口,Activity实现接口后控制第二个容器的NavController导航。
- 深层链接导航:在导航图中为第二个Fragment配置深层链接,第一个Fragment通过
navController.navigate(Uri.parse("app://your.app/messages/detail"))触发导航,第二个NavHost自动匹配目的地。
内容的提问来源于stack exchange,提问作者Vadim Nagibin
相关产品推荐
相关产品推荐

