如何在底部导航活动中更改片段?

26
我用“底部导航活动”创建了一个新项目: enter image description here 这是生成的代码:
package com.aaron.waller.mrpolitik;

import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.design.widget.BottomNavigationView;
import android.support.v7.app.AppCompatActivity;
import android.view.MenuItem;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    private TextView mTextMessage;

    private BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener
            = new BottomNavigationView.OnNavigationItemSelectedListener() {

        @Override
        public boolean onNavigationItemSelected(@NonNull MenuItem item) {
            switch (item.getItemId()) {
                case R.id.navigation_home:
                    mTextMessage.setText(R.string.title_home);
                case R.id.navigation_dashboard:
                    mTextMessage.setText(R.string.title_dashboard);
                case R.id.navigation_notifications:
                    mTextMessage.setText(R.string.title_notifications);
            }
            return true;
        }

    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mTextMessage = (TextView) findViewById(R.id.message);
        BottomNavigationView navigation = (BottomNavigationView) findViewById(R.id.navigation);
        navigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener);
    }

}

如何使用底部导航栏切换到新的Fragment? 例如,我有3个Fragment: Fragment1 Fragment2和 Fragment3 我想用底部导航栏上的3个按钮切换到它们。 此外,我希望能够通过左右滑动手指来切换Fragments,我该如何做到这一点?


2
关于滑动操作:在内容区域使用滑动手势不会在视图之间导航,请查看底部导航的设计指南,网址为https://material.io/guidelines/components/bottom-navigation.html#bottom-navigation-behavior。 - Ricardo
9个回答

24

我会这样做,首先添加三个类似于此方法的方法(每个方法针对一个碎片)。将布局名称和碎片对象替换为正在切换到的适当碎片:

public void switchToFragment1() {
    FragmentManager manager = getSupportFragmentManager();
    manager.beginTransaction().replace(R.id.your_fragment_layout_name, new Fragment1()).commit();
}

那么你的 switch 语句将会像这样:

        switch (item.getItemId()) {
            case R.id.navigation_home:
                mTextMessage.setText(R.string.title_home);
                switchToFragment1();
                break;

            case R.id.navigation_dashboard:
                mTextMessage.setText(R.string.title_dashboard);                    
                switchToFragment2();
                break;

            case R.id.navigation_notifications:
                mTextMessage.setText(R.string.title_notifications);                     
                switchToFragment3();
                break;
        }

至于通过向两侧滑动来切换片段,我认为您需要一个ViewPager


9
根据Google Material文档,当使用底部导航时,不应该使用滑动(避免使用横向运动来切换视图)。详情请见:https://material.io/guidelines/components/bottom-navigation.html#bottom-navigation-behavior - Minion
@bitvale https://medium.com/@oluwabukunmi.aluko/bottom-navigation-view-with-fragments-a074bfd08711 - mochadwi
对于较新的实现,不要使用片段事务,而是使用:navController.navigate(R.id.thirdFragment)。 - Biswas Khayargoli

11

这很“简单”。

  1. 创建你的片段。假设我们想要3个片段; FragmentAFragmentBFragmentC,其中FragmentA是我们想要放在底部导航视图上的第一个片段。
  2. 在您的Activity中,转到onNavigationItemSelected方法并将内容更改为:

 private BottomNavigationView.OnNavigationItemSelectedListener  
   mOnNavigationItemSelectedListener
       = new BottomNavigationView.OnNavigationItemSelectedListener(){

   @Override
   public boolean onNavigationItemSelected(@NonNull MenuItem item) {

    switch (item.getItemId()) {
        case R.id.frag_a:
            currentFragment = new FragmentA();
            ft = getSupportFragmentManager().beginTransaction();
            ft.replace(R.id.content, currentFragment);
            ft.commit();
            return true;
        case R.id.frag_b:
            currentFragment = new FragmentB();
            ft = getSupportFragmentManager().beginTransaction();
            ft.replace(R.id.content, currentFragment);
            ft.commit();
            return true;
        case R.id.frag_c:
            currentFragment = new FragmentC();
            ft = getSupportFragmentManager().beginTransaction();
            ft.replace(R.id.content, currentFragment);
            ft.commit();
            return true;
    }

    return false;
 }

};
  • 在你的onCreate()方法中,做如下操作:

  •   @Override
      protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_client_profile);
        ft = getSupportFragmentManager().beginTransaction();
        currentFragment = new FragmentA();
        ft.replace(R.id.content, currentFragment);
        ft.commit();
    
        BottomNavigationView navigation = (BottomNavigationView)  
        findViewById(R.id.navigation); navigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener);
      }
    
    如果您在onCreate()中没有添加FragmentA,那么当您第一次启动活动时,该活动将为空白。
    如果您想知道R.id.content指的是什么,它是您活动布局中Framelayout的ID。 它最初包含一个TextView,删除TextView,使其看起来像这样:
    <FrameLayout
        android:id="@+id/content"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"/>
    

    最后,ftcurrentFragment定义如下:

    Fragment currentFragment = null;
    FragmentTransaction ft;
    

    我不确定是否优雅,但这个方法可行。


    9

    最佳方式是使用带有FragmentPagerAdapterViewPager,因为它可以缓存其中的片段。使用setOnNavigationItemSelectedListenerBottomNavigationView一起监听用户的点击。使用viewPager.setCurrentItem(..)在页面之间移动。

    每次用户点击底部导航视图中的项目时都创建一个新片段并不是一个好的解决方案(特别是当用户单击他当前所在屏幕的项目时,上述解决方案即使对于这种情况也会创建一个新片段)。


    1
    您的解决方案看起来很有趣,不过您能提供一份示例代码吗? - Hendy Irawan
    1
    不是一个好的解决方案,很难轻松地禁用ViewPager上的滑动。 - abdu

    7

    2
    //fully tested  
      public class DashBoardActivity extends AppCompatActivity {
    
            Fragment fragment = null;
            FragmentTransaction fragmentTransaction;
    
            private BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener
                    = new BottomNavigationView.OnNavigationItemSelectedListener() {
    
                @Override
                public boolean onNavigationItemSelected(@NonNull MenuItem item) {
                    switch (item.getItemId()) {
                        case R.id.navigation_home:
                            return true;
                        case R.id.navigation_dashboard:
                            fragment = new FragmentDashBoard();
                            switchFragment(fragment);
                            return true;
                        case R.id.navigation_notifications:
                            fragment = new FragmentNotification();
                            switchFragment(fragment);
                            return true;
                    }
                    return false;
                }
            };
    
    
            private void switchFragment(Fragment fragment) {
                fragmentTransaction = getSupportFragmentManager().beginTransaction();
                fragmentTransaction.replace(R.id.content, fragment);
                fragmentTransaction.commit();
            }
    
            @Override
            protected void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                setContentView(R.layout.activity_dashboard);
    
                BottomNavigationView navigation = (BottomNavigationView) findViewById(R.id.navigation);
                navigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener);
                navigation.setSelectedItemId(R.id.navigation_dashboard);
            }
    
        }
    

    2

    使用导航组件是最简单的方法:

     bottom_navigation_view?.setupWithNavController(navController)
    

    是的!这个完美地运作了!但是你能否通过编程方式改变/切换片段吗?有相关文档吗? - Hikki

    1

    还有一种避免重新创建片段的方法 -- fm.beginTransaction().hide(active).show(aimFragment)

    下面是我的示例(刚从我的最新项目中复制):

    public class MainActivity extends AppCompatActivity {
        @BindView(R.id.main_bottom_navigation) BottomNavigationView mBottomNavigationView;
        final Fragment mTaskListFragment = new TaskListFragment();
        final Fragment mUserGroupFragment = new UserGroupFragment();
        final Fragment mUserMeFragment = new UserMeFragment();
        final FragmentManager fm = getSupportFragmentManager();
        Fragment active = mTaskListFragment;
    
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            ButterKnife.bind(this);
            mBottomNavigationView
                    .setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener);
            fm.beginTransaction().add(R.id.main_fragment_container, mUserMeFragment, "3")
                    .hide(mUserMeFragment).commit();
            fm.beginTransaction().add(R.id.main_fragment_container, mUserGroupFragment, "2")
                    .hide(mUserGroupFragment).commit();
            fm.beginTransaction().add(R.id.main_fragment_container, mTaskListFragment, "1").commit();
    
        }
    
    
        private BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener
            = item -> {
    //        TODO: 这种切换方式比较快,但横竖屏切换会出问题,已经
                switch (item.getItemId()) {
                    case R.id.nav_list:
                        fm.beginTransaction().hide(active).show(mTaskListFragment).commit();
                        active = mTaskListFragment;
                        break;
                    case R.id.nav_group:
                        fm.beginTransaction().hide(active).show(mUserGroupFragment).commit();
                        active = mUserGroupFragment;
                        break;
                    case R.id.nav_me:
                        fm.beginTransaction().hide(active).show(mUserMeFragment).commit();
                        active = mUserMeFragment;
                        break;
                }
                return true;
            };
    }
    

    这种方法看起来很有效,但当你旋转手机时就会出现问题。我通过在清单文件中添加以下代码到活动中来保持片段状态(例如,在AndroidManifest.xml中)进行了修复:

    android:configChanges="screenSize|orientation|screenLayout"
    
    

    1

    这是我的建议方式,它具有理想的性能 我使用 naveControllerandroidx.navigation:navigation 来完成了它

    步骤 1:在您的应用级 build.gradle 文件中添加导航依赖项。这将允许您在应用中使用导航组件。

    dependencies {
    def nav_version = "2.3.5"
    implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
    implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
    }
    

    步骤2:为底部导航栏创建一个新的菜单资源文件。该文件将包含在导航栏中显示的菜单项。要创建一个新的菜单资源文件,请在项目面板中右键单击res文件夹,选择New > Android resource file,并设置资源类型为Menu。

    步骤3:在菜单资源文件中添加要在底部导航栏中显示的菜单项。每个菜单项应具有唯一的ID和标题。例如:

    <menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:id="@+id/navigation_home"
        android:title="Home" />
    <item
        android:id="@+id/navigation_dashboard"
        android:title="Dashboard" />
    <item
        android:id="@+id/navigation_notifications"
        android:title="Notifications" />
    

    第四步:在您想要显示底部导航栏的布局文件中添加一个BottomNavigationView元素。例如:
       <com.google.android.material.bottomnavigation.BottomNavigationView
    android:id="@+id/bottom_navigation_view"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:menu="@menu/bottom_navigation_menu"
    />
    

    请注意,app:menu属性设置为在步骤2中创建的菜单资源文件的名称。
    第5步:在将承载底部导航栏的活动或片段中,使用findNavController()方法创建NavController的实例,并将BottomNavigationView作为参数传递进去。例如:
    <navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    app:startDestination="@id/navigation_home">
    
    <fragment
        android:id="@+id/navigation_home"
        android:name="com.example.myapp.HomeFragment"
        android:label="@string/title_home" />
    
    <fragment
        android:id="@+id/navigation_dashboard"
        android:name="com.example.myapp.DashboardFragment"
        android:label="@string/title_dashboard" />
    
    <fragment
        android:id="@+id/navigation_notifications"
        android:name="com.example.myapp.NotificationsFragment"
        android:label="@string/title_notifications" />
    

    请注意,每个片段都有一个唯一的ID,该ID对应于底部导航栏中菜单项的ID。
    就是这样!通过这些步骤,您现在应该拥有一个基本的底部导航栏,可以用来替换应用程序中的不同片段。

    -2

    你可以使用这个

                        fragmentManager = getFragmentManager();
                        transaction = fragmentManager.beginTransaction();
    
                        FragmentA a = new FragmentA();
                        transaction.replace(R.id.frame, a);
                        transaction.commit();
    

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接