【问题标题】:Bottom Navigation View setup with fragment navigation component not working带有片段导航组件的底部导航视图设置不起作用
【发布时间】:2019-05-30 19:58:13
【问题描述】:

我尝试通过引用Navigation Codelab 来实现底部导航视图以使用导航组件设置片段转换。

点击底部导航视图时片段没有变化。

注意:
我正在尝试在另一个片段中实现底部导航视图。 (不是 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)
    }
}

【问题讨论】:

    标签: android kotlin android-architecture-navigation


    【解决方案1】:

    您似乎从未到达过setupWithNavController 行。您将findFragmentById(R.id.main_fragment_nav_host_fragment) 与活动的FragmentManager 一起使用,但活动中的NavHostFragment 位于id main_activity_nav_host_fragment 下。如果您想在MainFragment 的布局中获得嵌套的NavHostFragment,您应该使用childFragmentManager

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

    请注意,您实际上想要或需要这样的嵌套NavHostFragment 的情况很少。根据Listen for navigation events documentation,通常如果您希望全局导航(例如BottomNavigationView)仅出现在某些屏幕上,您需要添加OnDestinationChangedListener 并在此处更改其可见性。

    【讨论】:

    • OnDestinationChangedListener 是我想要的。不知道有这样的听众。谢谢。
    • @ianhanniballake,非常感谢,我有很多时间。使用childFragmentManager 而不是activity?.supportFragmentManager 解决了我的问题。
    【解决方案2】:

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

    您在片段内进行底部导航似乎很奇怪,根据我的经验,底部导航栏进入活动并且您以编程方式设置第一个片段。使用导航栏进入视图的片段会填充位于 Activity 导航栏上方的 FrameLayout

    这是一个 Java 示例。

    MainActivity.java

    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();
    
    
        }
    
    

    MaiActivity 布局文件

    
    <?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>
    

    【讨论】:

    • 感谢您的回复。但是,1.我的要求是将底部导航添加到片段中。 2.我想使用android-navigation-components。而回答的方法是我熟悉的添加底部导航的默认方式。
    【解决方案3】:

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

    我正在和你一样设置我的底部导航

    // Setup bottom navigation view
    mainFragmentBinding?.mainFragmentBottomNavigationView?.setupWithNavController(navController)
    
    // or with kotlin synthetic
    bottomNavigation.setupWithNavController(findNavController())
    

    注意:我希望 kotlin 合成方式能够工作,因为我从片段的 onActivityCreated() 调用 findNavController() 并且片段在其布局中包含默认的 navHost。

    但是什么也没发生……而且不明白为什么。然后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)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-08-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多