具有片段导航组件的底部导航视图设置不起作用

时间:2019-05-30 19:58:14

标签: android kotlin android-architecture-navigation

我尝试通过引用Navigation Codelab来实现底部导航视图,以使用导航组件进行片段过渡。

单击底部导航视图时,片段没有改变。

注意:
我正在尝试在另一个片段内实现底部导航视图。 (不是代码实验室示例中的活动)

MainActivity.kt:

class MainActivity : AppCompatActivity() {

    // Data binding
    private lateinit var mainActivityBinding: MainActivityBinding

    // On activity creation starting
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // Set activity layout
        mainActivityBinding = DataBindingUtil.setContentView(this, R.layout.main_activity)
    }
}

main_activity.xml

<?xml version="1.0" encoding="utf-8"?>
<layout 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"
    tools:context=".activity.MainActivity">

    <data>

    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/main_activity_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <fragment
            android:id="@+id/main_activity_nav_host_fragment"
            android:name="androidx.navigation.fragment.NavHostFragment"
            android:layout_width="0dp"
            android:layout_height="0dp"
            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/activity_navigation" />

    </androidx.constraintlayout.widget.ConstraintLayout>

</layout>

activity_navigation.xml:

<?xml version="1.0" encoding="utf-8"?>
<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/activity_navigation"
    app:startDestination="@id/mainFragment">

    <fragment
        android:id="@+id/mainFragment"
        android:name="com.appz.abhi.moneytracker.view.main.MainFragment"
        android:label="main_fragment"
        tools:layout="@layout/main_fragment" />

</navigation>

MainFragment.kt:

class MainFragment : Fragment() {

    // Data binding
    private var mainFragmentBinding: MainFragmentBinding? = null

    // On fragment view creation starting
    override fun onCreateView(
            inflater: LayoutInflater, container: ViewGroup?,
            savedInstanceState: Bundle?
    ): View? {

        // Inflate the fragment layout
        mainFragmentBinding = DataBindingUtil
                .inflate(inflater, R.layout.main_fragment, container, false)

        // Return root view
        return mainFragmentBinding!!.root
    }


    // On fragment view creation completion
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        // Initialize UI
        initUI()
    }


    // Initialize UI
    private fun initUI() {

        // Setup action bar
        (activity as AppCompatActivity).setSupportActionBar(mainFragmentBinding?.mainFragmentToolbar)

        // Setup bottom navigation view
        setUpBottomNavigationView()
    }

    // Setup bottom navigation view
    private fun setUpBottomNavigationView() {

        // Nav host fragment
        val host: NavHostFragment = activity?.supportFragmentManager
                ?.findFragmentById(R.id.main_fragment_nav_host_fragment) as NavHostFragment?
                ?: return

        // Set up Action Bar
        val navController = host.navController

        // Setup bottom navigation view
        mainFragmentBinding?.mainFragmentBottomNavigationView?.setupWithNavController(navController)
    }
}

main_fragment.xml:

<?xml version="1.0" encoding="utf-8"?>
<layout 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"
    tools:context=".view.main.MainFragment">

    <data>

    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/main_fragment_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <com.google.android.material.appbar.AppBarLayout
            android:id="@+id/main_fragment_app_bar_layout"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent">

            <androidx.appcompat.widget.Toolbar
                android:id="@+id/main_fragment_toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                android:background="@drawable/toolbar_background"
                android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
                app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
                tools:title="Money Tracker" />

        </com.google.android.material.appbar.AppBarLayout>

        <fragment
            android:id="@+id/main_fragment_nav_host_fragment"
            android:name="androidx.navigation.fragment.NavHostFragment"
            android:layout_width="0dp"
            android:layout_height="0dp"
            app:defaultNavHost="true"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@id/main_fragment_app_bar_layout"
            app:navGraph="@navigation/bottom_navigation_view_navigation" />

        <com.google.android.material.bottomnavigation.BottomNavigationView
            android:id="@+id/main_fragment_bottom_navigation_view"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:background="@color/white"
            app:itemIconTint="@color/bottom_navigation"
            app:itemTextColor="@color/bottom_navigation"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:menu="@menu/bottom_navigation_view_menu"/>

    </androidx.constraintlayout.widget.ConstraintLayout>

</layout>

bottom_navigation_view_navigation.xml:

<?xml version="1.0" encoding="utf-8"?>
<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/bottom_navigation_view_navigation"
    app:startDestination="@id/settingsFragment">

    <fragment
        android:id="@+id/homeFragment"
        android:name="com.appz.abhi.moneytracker.view.home.HomeFragment"
        android:label="Home"
        tools:layout="@layout/home_fragment" />

    <fragment
        android:id="@+id/settingsFragment"
        android:name="com.appz.abhi.moneytracker.view.settings.SettingsFragment"
        android:label="Settings"
        tools:layout="@layout/settings_fragment" />

</navigation>

bottom_navigation_view_menu:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <item
        android:id="@id/homeFragment"
        android:icon="@drawable/ic_home_black_24dp"
        android:title="@string/home"
        app:showAsAction="ifRoom" />

    <item
        android:id="@id/settingsFragment"
        android:icon="@drawable/ic_settings_black_24dp"
        android:title="@string/settings"
        app:showAsAction="ifRoom" />

</menu>

HomeFragment.kt:

class HomeFragment : Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.home_fragment, container, false)
    }
}

SettingsFragment.kt:

class SettingsFragment : Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.settings_fragment, container, false)
    }
}

3 个答案:

答案 0 :(得分:1)

看来您似乎没有到达setupWithNavController行。您将findFragmentById(R.id.main_fragment_nav_host_fragment)与活动的FragmentManager一起使用,但活动中的NavHostFragment在ID main_activity_nav_host_fragment下。如果要在childFragmentManager的布局中获取嵌套的NavHostFragment,则应该使用MainFragment

// Nav host fragment
val host: NavHostFragment = childFragmentManager
        .findFragmentById(R.id.main_fragment_nav_host_fragment) as NavHostFragment?
        ?: return

请注意,在极少数情况下,您实际上需要或需要这样的嵌套NavHostFragment。根据{{​​3}},通常,如果您希望仅在某些屏幕上显示诸如BottomNavigationView之类的全局导航,则可以添加OnDestinationChangedListener并在此处更改其可见性。

答案 1 :(得分:0)

注意:我没有Kotlin的经验,但是我假设Java中的实现足够相似。

您在片段中进行底部导航似乎很奇怪,以我的经验,活动底部的导航栏是您以编程方式设置的。使用导航栏显示的片段将填充FrameLayout,该public class HomeActivity extends AppCompatActivity { private String currentFragmentTag; private BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener = new BottomNavigationView.OnNavigationItemSelectedListener() { @Override public boolean onNavigationItemSelected(@NonNull MenuItem item) { /* I just put some filler code but each fragment type will probably be different */ switch (item.getItemId()) { case R.id.fragment_one: CustomFragment fragment = new CustomFragment(); if (!currentFragmentTag.equals(fragment.fragmentTag)) { switchFragment(customFragment, null); } return true; case R.id.fragment_two: CustomFragment fragment = new CustomFragment(); if (!currentFragmentTag.equals(fragment.fragmentTag)) { switchFragment(customFragment, null); } return true; case .... } return false; } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_home); currentFragmentTag = "FIRST_FRAGMENT"; BottomNavigationView navigation = (BottomNavigationView) findViewById(R.id.navigation); navigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener); } /** * Select fragment. */ public void switchFragment(Fragment fragment, String tag) { getSupportFragmentManager().beginTransaction().replace(R.id.home_frame, fragment, tag) .addToBackStack(tag).setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE).commit(); } 位于活动中导航栏的上方。

这是Java中的示例。

MainActivity.java


<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".activities.MainActivity">

    <android.support.design.widget.BottomNavigationView
        android:id="@+id/navigation"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:background="@color/background_grey"
        app:itemIconTint="@color/white"
        app:itemTextColor="@color/white"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:menu="@menu/navigation" />

    <FrameLayout
        android:id="@+id/home_frame"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="8dp"
        android:layout_marginBottom="8dp"
        app:layout_constraintBottom_toTopOf="@+id/navigation"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">

    </FrameLayout>

</android.support.constraint.ConstraintLayout>

MaiActivity布局文件

geom_boxplot

答案 2 :(得分:0)

无论出于何种原因,findNavController(R.id.nav_host_fragment)setupActionBarWithNavController()都是您要在Activity中而不是在Fragment中调用的方法。 (我在文档中找不到对此的任何引用,所以如果有人这样做,请在评论中添加only stated on SO for now)。

我和您一样设置我的bottomNavigation

// Setup bottom navigation view
mainFragmentBinding?.mainFragmentBottomNavigationView?.setupWithNavController(navController)

// or with kotlin synthetic
bottomNavigation.setupWithNavController(findNavController())

注意:由于我希望从片段的findNavController()调用onActivityCreated(),并且片段在其布局中包含默认的navHost,因此可以使用kotlin合成方式。

但是什么也没发生...也无法理解原因。然后this questionthis great article给了我有关如何访问navHost的提示。

解决方法
到达活动,然后您可以在findNavController(R.id.nav_host_fragment)

中指定navHost的ID
// find navController using navHostFragment
val navController = requireActivity().findNavController(R.id.main_fragment_nav_host_fragment)

// setup navigation with root destinations and toolbar
NavigationUI.setupWithNavController(bottomNavigation, navController)