如果我使用视图绑定,如何找到 NavController?
How can I find NavController if I use view binding?
通常,我在代码A中使用val navController: NavController = findNavController(R.id.nav_host_fragment)
来查找NavController,它基于R.id.nav_host_fragment
。
现在我在应用程序中使用视图绑定就像代码 B,如果我使用视图绑定,我如何使用 NavController?
顺便说一句,在我看来 R.id.nav_host_fragment
在视图绑定中不可用,对吧?
代码A
class TasksActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.tasks_act)
val navController: NavController = findNavController(R.id.nav_host_fragment)
}
}
代码B
class TasksActivity : AppCompatActivity() {
private lateinit var binding: TasksActBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = TasksActBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
//val navController: NavController = findNavController(R.id.nav_host_fragment)
}
}
tasks_act.xml
<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_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".tasks.TasksActivity"
tools:openDrawer="start">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?attr/actionBarSize"
android:theme="@style/Toolbar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
</com.google.android.material.appbar.AppBarLayout>
<fragment
android:id="@+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="@navigation/nav_graph" />
</LinearLayout>
..
</androidx.drawerlayout.widget.DrawerLayout>
我们正在使用 ViewBinding 来获取视图本身的引用。如果您在 TextView 中使用视图绑定,您将获得视图的引用。
在 NavCotroller 的情况下,如果您使用 Viewbinding,您将获得对片段的引用,而 findNavController 需要整数类型的 ViewId。
findNavController(@IdRes viewId: int)
代码 B 应该仍然可以正常工作。
我查看了 findNavController()
。这是一个用于简化代码的扩展函数,扩展函数代码为
fun Activity.findNavController(@IdRes viewId: Int): NavController =
Navigation.findNavController(this, viewId)
现在查看 Navigation
中 findNavController()
的代码,我们看到如下
@NonNull
public static NavController findNavController(@NonNull Activity activity, @IdRes int viewId) {
View view = ActivityCompat.requireViewById(activity, viewId);
NavController navController = findViewNavController(view);
if (navController == null) {
throw new IllegalStateException("Activity " + activity
+ " does not have a NavController set on " + viewId);
}
return navController;
}
我们在参数中传递的 viewId 在第一行使用
View view = ActivityCompat.requireViewById(activity, viewId);
现在查看 ActivityCompat
内部的 requireViewById()
我们看到
@NonNull
public static <T extends View> T requireViewById(@NonNull Activity activity, @IdRes int id) {
if (Build.VERSION.SDK_INT >= 28) {
return activity.requireViewById(id);
}
T view = activity.findViewById(id);
if (view == null) {
throw new IllegalArgumentException("ID does not reference a View inside this Activity");
}
return view;
}
for api 28 加上被调用的方法是
@NonNull
public final <T extends View> T requireViewById(@IdRes int id) {
T view = findViewById(id);
if (view == null) {
throw new IllegalArgumentException("ID does not reference a View inside this Activity");
}
return view;
}
因此,只要将视图(其中存在 nav_host_fragment
)附加到 activity,您为查找导航控制器而编写的代码就应该可以完全正常工作。
class TasksActivity : AppCompatActivity() {
private lateinit var binding: TasksActBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = TasksActBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view) //you are attaching view to activity here , make sure you always call this before the next line , else you will get IllegalStateException
val navController: NavController = findNavController(R.id.nav_host_fragment)
}
}
我没有亲自测试代码,但据我所知,它应该工作得很好。
正确的工作方式如下:
必须从 supportFragmentManager 访问 FragmentContainerView:
val navHostFragment = supportFragmentManager.findFragmentById(R.id.fragment) as NavHostFragment
val navController = navHostFragment.navController
通常,我在代码A中使用val navController: NavController = findNavController(R.id.nav_host_fragment)
来查找NavController,它基于R.id.nav_host_fragment
。
现在我在应用程序中使用视图绑定就像代码 B,如果我使用视图绑定,我如何使用 NavController?
顺便说一句,在我看来 R.id.nav_host_fragment
在视图绑定中不可用,对吧?
代码A
class TasksActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.tasks_act)
val navController: NavController = findNavController(R.id.nav_host_fragment)
}
}
代码B
class TasksActivity : AppCompatActivity() {
private lateinit var binding: TasksActBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = TasksActBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
//val navController: NavController = findNavController(R.id.nav_host_fragment)
}
}
tasks_act.xml
<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_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".tasks.TasksActivity"
tools:openDrawer="start">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?attr/actionBarSize"
android:theme="@style/Toolbar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
</com.google.android.material.appbar.AppBarLayout>
<fragment
android:id="@+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="@navigation/nav_graph" />
</LinearLayout>
..
</androidx.drawerlayout.widget.DrawerLayout>
我们正在使用 ViewBinding 来获取视图本身的引用。如果您在 TextView 中使用视图绑定,您将获得视图的引用。
在 NavCotroller 的情况下,如果您使用 Viewbinding,您将获得对片段的引用,而 findNavController 需要整数类型的 ViewId。
findNavController(@IdRes viewId: int)
代码 B 应该仍然可以正常工作。
我查看了 findNavController()
。这是一个用于简化代码的扩展函数,扩展函数代码为
fun Activity.findNavController(@IdRes viewId: Int): NavController =
Navigation.findNavController(this, viewId)
现在查看 Navigation
中 findNavController()
的代码,我们看到如下
@NonNull
public static NavController findNavController(@NonNull Activity activity, @IdRes int viewId) {
View view = ActivityCompat.requireViewById(activity, viewId);
NavController navController = findViewNavController(view);
if (navController == null) {
throw new IllegalStateException("Activity " + activity
+ " does not have a NavController set on " + viewId);
}
return navController;
}
我们在参数中传递的 viewId 在第一行使用
View view = ActivityCompat.requireViewById(activity, viewId);
现在查看 ActivityCompat
内部的 requireViewById()
我们看到
@NonNull
public static <T extends View> T requireViewById(@NonNull Activity activity, @IdRes int id) {
if (Build.VERSION.SDK_INT >= 28) {
return activity.requireViewById(id);
}
T view = activity.findViewById(id);
if (view == null) {
throw new IllegalArgumentException("ID does not reference a View inside this Activity");
}
return view;
}
for api 28 加上被调用的方法是
@NonNull
public final <T extends View> T requireViewById(@IdRes int id) {
T view = findViewById(id);
if (view == null) {
throw new IllegalArgumentException("ID does not reference a View inside this Activity");
}
return view;
}
因此,只要将视图(其中存在 nav_host_fragment
)附加到 activity,您为查找导航控制器而编写的代码就应该可以完全正常工作。
class TasksActivity : AppCompatActivity() {
private lateinit var binding: TasksActBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = TasksActBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view) //you are attaching view to activity here , make sure you always call this before the next line , else you will get IllegalStateException
val navController: NavController = findNavController(R.id.nav_host_fragment)
}
}
我没有亲自测试代码,但据我所知,它应该工作得很好。
正确的工作方式如下:
必须从 supportFragmentManager 访问 FragmentContainerView:
val navHostFragment = supportFragmentManager.findFragmentById(R.id.fragment) as NavHostFragment
val navController = navHostFragment.navController