如何使用Hilt注入NavController

发布于 2025-01-21 20:20:57 字数 4043 浏览 0 评论 0原文

我是新手。试图从匕首转换为刀柄。但是我坚持将navController注入活动

因此,要创建navController实例,我需要访问活动和主机片段ID。在匕首中,这是我创建该实例的方法。

创建activityModule

@Module
class ActivityModule {

    @ActivityScope
    @Provides
    fun navigation(activity: MainActivity, hosFragment: Int) =
        Navigation.findNavController(activity,hosFragment)
}

首先,在第二步中

@ActivityScope
@Subcomponent(modules = [ActivityModule::class])
interface ActivitySubComponent {

    @Subcomponent.Factory
    interface Factory{
        fun create(@BindsInstance activity: MainActivity,
                   @BindsInstance hostFragment: Int): ActivitySubComponent
    }

    fun inject(MainActivity: MainActivity)
}

activitysubcomponent具有参加活动并主机片段ID作为参数的能力。将此子组件与其他子组件添加到ApplicationComponent中,您可以将您的实例获取navController的实例。

class MainActivity : AppCompatActivity() {

    @Inject
    lateinit var navigationController: NavController
    lateinit var viewBinding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        viewBinding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(viewBinding.root)

        injector.activityComponent().create(this, R.id.hostFragment).inject(this)

    }
}

喷油器来自以下类。

interface InjectorProvider {
  val component: ApplicationComponent
}

val Activity.injector get() = (application as InjectorProvider).component
val Fragment.injector get() = (requireActivity().application as InjectorProvider).component

当我试图过渡到Hilt时,我遇到的第一个问题是如何向模块提供参数。不像在匕首中,我找不到子组件工厂的替代方法。甚至可以向刀柄上的模块提供参数吗?

无论如何,我决定将fragmentContainerview ID提供给navController,因为它没有更改。这样,我来到了下面的解决方案。

@Module
@InstallIn(ActivityComponent::class)
object NavigationModule {

    @Provides
    fun navigationController(activity: Activity) =
        activity.findNavController(R.id.hostFragment)
}

并试图将其注入我的mainActvity如下所示。

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {

    @Inject
    lateinit var navigationController: NavController
    lateinit var viewBinding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {

        super.onCreate(savedInstanceState)

        viewBinding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(viewBinding.root)

        initialization()

    }

    private fun initialization() {

        setSupportActionBar(viewBinding.toolbar)
        NavigationUI.setupActionBarWithNavController(this, navigationController)

    }

}

应用程序成功地构建了,但是现在我遇到了一个运行时间错误,说“ ID不用引用此活动中的视图”,但是我的活动布局的构建如下所示。

<LinearLayout
    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"
    android:orientation="vertical">

    <androidx.appcompat.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/purple_700"
        app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
        app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" />

    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/hostFragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:defaultNavHost="true"
        app:navGraph="@navigation/main_navigation" />

</LinearLayout>

错误指向super.oncreate(Savedinstancestate)MainActivity上。请让我知道我在这里想念什么。谢谢。

I'm new to Hilt. Trying to convert from Dagger to Hilt. But I'm stuck on injecting NavController to the Activity.

So to create NavController instance I need to have access to the activity and the host fragment Id. In Dagger this was my approach to create that instance.

First, create ActivityModule

@Module
class ActivityModule {

    @ActivityScope
    @Provides
    fun navigation(activity: MainActivity, hosFragment: Int) =
        Navigation.findNavController(activity,hosFragment)
}

In the second step ActivitySubComponent has the ability to take activity and host fragment id as parameters.

@ActivityScope
@Subcomponent(modules = [ActivityModule::class])
interface ActivitySubComponent {

    @Subcomponent.Factory
    interface Factory{
        fun create(@BindsInstance activity: MainActivity,
                   @BindsInstance hostFragment: Int): ActivitySubComponent
    }

    fun inject(MainActivity: MainActivity)
}

After adding this subcomponent with other subcomponents to the ApplicationComponent from you can get the instance of NavController to the fragment like below.

class MainActivity : AppCompatActivity() {

    @Inject
    lateinit var navigationController: NavController
    lateinit var viewBinding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        viewBinding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(viewBinding.root)

        injector.activityComponent().create(this, R.id.hostFragment).inject(this)

    }
}

injector coming from the following class.

interface InjectorProvider {
  val component: ApplicationComponent
}

val Activity.injector get() = (application as InjectorProvider).component
val Fragment.injector get() = (requireActivity().application as InjectorProvider).component

When I was trying to transition into Hilt, the first issue I faced was how to provide parameters to the module. Not like in Dagger I could not find an alternative for Subcomponent Factory. Is it even possible to provide parameters to a module on Hilt?

Anyways, I decided to provide the FragmentContainerView id to the NavController manually since it's not changing. With that, I came to the below solution in Hilt.

@Module
@InstallIn(ActivityComponent::class)
object NavigationModule {

    @Provides
    fun navigationController(activity: Activity) =
        activity.findNavController(R.id.hostFragment)
}

And tried to inject it into my MainActvity like below.

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {

    @Inject
    lateinit var navigationController: NavController
    lateinit var viewBinding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {

        super.onCreate(savedInstanceState)

        viewBinding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(viewBinding.root)

        initialization()

    }

    private fun initialization() {

        setSupportActionBar(viewBinding.toolbar)
        NavigationUI.setupActionBarWithNavController(this, navigationController)

    }

}

Application built successfully but now I'm getting a run time error saying "ID does not reference a View inside this Activity" but my activity layout is constructed like below.

<LinearLayout
    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"
    android:orientation="vertical">

    <androidx.appcompat.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/purple_700"
        app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
        app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" />

    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/hostFragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:defaultNavHost="true"
        app:navGraph="@navigation/main_navigation" />

</LinearLayout>

Error pointing at super.onCreate(savedInstanceState) line on the MainActivity. Please let me know what am I missing here. Thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

野稚 2025-01-28 20:20:57

我能够弄清楚。上述问题必须是由于在尝试注入navController实例时无法使用的UI引起的。因此,刀柄具有定义接口并将该接口注入活动中的方法。我们可以通过实现该接口来定义类。这使我们有机会提供在可用时构建实例所需的参数。

首先,创建下面的接口。

interface AppNavigator {
    
    fun getNaveHostFragment(hostFragmentId: Int): NavController
    
}

其次,创建一个实现类。

class AppNavigatorImplementation @Inject constructor(private val activity: FragmentActivity): AppNavigator {

    override fun getNaveHostFragment(hostFragmentId: Int): NavController {
        val navHostFragment = activity.supportFragmentManager.findFragmentById(R.id.hostFragment) as NavHostFragment
        return navHostFragment.navController
    }

}

之后,定义navigationModule如下所示。

@InstallIn(ActivityComponent::class)
@Module
abstract class NavigationModule {

    @Binds
    abstract fun bindNavigator(impl: AppNavigatorImplementation): AppNavigator

}

最后,您可以将该实例注入下面的活动。

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {

    @Inject
    lateinit var navigator: AppNavigator
    lateinit var viewBinding: ActivityMainBinding
    private lateinit var navigationController: NavController

    override fun onCreate(savedInstanceState: Bundle?) {

        super.onCreate(savedInstanceState)

        viewBinding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(viewBinding.root)

        initialization()

    }

    private fun initialization() {

        navigationController = navigator.getNaveHostFragment(R.id.hostFragment)

        setSupportActionBar(viewBinding.toolbar)
        NavigationUI.setupActionBarWithNavController(this, navigationController)

    }
}

I was able to figure it out. The above issue must be caused by the fact the UI is not available at the time it tries to inject the NavController instance. So Hilt has a way of defining an interface and Injecting that interface into the Activity. And we can define the classes by that implementing that interface. This gives us a chance to provide the parameters necessary to build our instance when they are available.

First, create the interface like below.

interface AppNavigator {
    
    fun getNaveHostFragment(hostFragmentId: Int): NavController
    
}

Secondly, create an implementation class.

class AppNavigatorImplementation @Inject constructor(private val activity: FragmentActivity): AppNavigator {

    override fun getNaveHostFragment(hostFragmentId: Int): NavController {
        val navHostFragment = activity.supportFragmentManager.findFragmentById(R.id.hostFragment) as NavHostFragment
        return navHostFragment.navController
    }

}

After that define NavigationModule like below.

@InstallIn(ActivityComponent::class)
@Module
abstract class NavigationModule {

    @Binds
    abstract fun bindNavigator(impl: AppNavigatorImplementation): AppNavigator

}

Finally, you can inject that instance to the activity like below.

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {

    @Inject
    lateinit var navigator: AppNavigator
    lateinit var viewBinding: ActivityMainBinding
    private lateinit var navigationController: NavController

    override fun onCreate(savedInstanceState: Bundle?) {

        super.onCreate(savedInstanceState)

        viewBinding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(viewBinding.root)

        initialization()

    }

    private fun initialization() {

        navigationController = navigator.getNaveHostFragment(R.id.hostFragment)

        setSupportActionBar(viewBinding.toolbar)
        NavigationUI.setupActionBarWithNavController(this, navigationController)

    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文