Android导航组件-向上导航可打开相同的片段

时间:2019-04-05 16:07:56

标签: android kotlin fragment-backstack android-architecture-navigation

执行时遇到问题

findNavController(R.id.main_nav_host).navigateUp()

findNavController(R.id.main_nav_host).popBackStack()

它没有返回到上一个堆栈中的最后一个片段,而是重新打开/导航到相同/当前的片段。

有人可以指出正确的方向为什么会发生这种情况吗?

导航图:

<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/main_navigation_root"
            app:startDestination="@+id/dest_main">

    <fragment
            android:id="@+id/dest_main"
            android:name="com.example.popularmovies.ui.main.views.MainMoviesFragment"
            android:label="@string/home"
            tools:layout="@layout/fragment_main_movies">

        <action
                android:id="@+id/action_dest_main_to_dest_movie_details"
                app:destination="@+id/dest_movie_details"
                app:enterAnim="@anim/slide_in_right"
                app:exitAnim="@anim/slide_out_left"
                app:popEnterAnim="@anim/slide_in_left"
                app:popExitAnim="@anim/slide_out_right" />

    </fragment>

    <fragment
            android:id="@+id/dest_movie_details"
            android:name="com.example.popularmovies.ui.details.movie.view.MovieDetailsFragment"
            android:label="@string/movie_details"
            tools:layout="@layout/fragment_movie_details"/>

</navigation>

MainActivity布局:

<FrameLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">

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

</FrameLayout>

MainActivity:

class MainActivity : AppCompatActivity(), HasSupportFragmentInjector {

    @Inject
    lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Fragment>

    private lateinit var appBarConfiguration: AppBarConfiguration

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        setSupportActionBar(toolbar)

        initNavUi()
    }

    override fun onBackPressed() {

        findNavController(R.id.main_nav_host).popBackStack()
    }

    override fun onCreateOptionsMenu(menu: Menu): Boolean {
        // Inflate the menu; this adds items to the action bar if it is present.
        menuInflater.inflate(R.menu.menu_main, menu)
        return true
    }

    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        return when (item.itemId) {
            R.id.action_settings -> true
            else -> super.onOptionsItemSelected(item)
        }
    }

    override fun onSupportNavigateUp(): Boolean {

        return findNavController(R.id.main_nav_host).navigateUp()
    }

    override fun supportFragmentInjector(): AndroidInjector<Fragment> {

        return dispatchingAndroidInjector
    }

    private fun initNavUi() {

        val navController = Navigation.findNavController(this, R.id.main_nav_host)
        appBarConfiguration = AppBarConfiguration(
            setOf(R.id.dest_main)
        )

        NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration)
    }

}

目标家庭片段:

class MainMoviesFragment : Fragment(), Injectable, MovieViewHolder.MovieClickListener {

    @Inject
    lateinit var viewModelFactory: ViewModelProvider.Factory

    private lateinit var fragmentViewModel: MainMoviesFragmentViewModel

    private lateinit var moviesRv: RecyclerView
    private lateinit var moviesAdapter: MainMoviesAdapter

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

        return inflater.inflate(R.layout.fragment_main_movies, container, false)
    }

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

        initViews(view)
    }

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)

        fragmentViewModel = ViewModelProviders.of(this,viewModelFactory).get(MainMoviesFragmentViewModel::class.java)
        fragmentViewModel.start()

        observe()
    }

    override fun onMovieClicked(position: Int) {

        fragmentViewModel.onMovieClicked(position)
    }

    private fun initViews(view: View) {

        moviesRv = view.findViewById<RecyclerView>(R.id.fragment_main_movies_rv).apply{

            layoutManager = LinearLayoutManager(context)
            setHasFixedSize(true)

            moviesAdapter = MainMoviesAdapter(this@MainMoviesFragment)
            adapter = moviesAdapter
        }
    }

    private fun observe() {

        fragmentViewModel.moviesLiveData.observe(this, Observer { moviesAdapter.submitList(it) })
        fragmentViewModel.onMovieClickedLiveEvent.observe(this, Observer { handleMovieClickedEvent(it) })

    }

    private fun handleMovieClickedEvent(movieModel: MovieModel?){

        val action = MainMoviesFragmentDirections.actionDestMainToDestMovieDetails()
        findNavController().navigate(action)
    }

}

目标目标片段:

class MovieDetailsFragment : Fragment() {

    private lateinit var viewModel: MovieDetailsFragmentViewModel

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

        return inflater.inflate(R.layout.fragment_movie_details, container, false)
    }

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)

        viewModel = ViewModelProviders.of(this).get(MovieDetailsFragmentViewModel::class.java)
    }

}

可以在here上找到GitHub上的项目代码

1 个答案:

答案 0 :(得分:1)

adb tcpip 5555 adb reverse tcp:8081 tcp:8081 adb connect 192.168.1.113 保存当前值,因此您每次onMovieClickedLiveEvent时都会触发MainMoviesFragmentViewModel中使用的MainMoviesFragment。这意味着MutableLiveData可以正常工作,但是您立即转到导航至详细信息页面(注意:您仍要删除popBackStack()中的代码,因为现在您无法退出点击返回按钮)。

根据SingleLiveEvent class,似乎应该使用this blog post,而不是直接使用onBackPressed(),尤其是使用变量名。

当然,在这种情况下,没有特别的理由使用LiveData或完全通过ViewModel。您的MutableLiveData可以将MovieViewHolder直接传递给MovieModel,后者可以直接调用onMovieClicked。这样可以避免使用LiveData(旨在存储状态,而不是存储事件),并且可以更好地为您实际想要实现的目标建模:事件侦听器。