使用Navigation切换Fragment

2,498 阅读3分钟

前言:

当前大多数应用都是MainActivity + Fragment 通过ViewPager+BottomNavigationView和来实现界面导航。

界面布局:

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.widget.DrawerLayout 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/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    tools:openDrawer="start">

    <include
        layout="@layout/app_bar_main"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <com.google.android.material.navigation.NavigationView
        android:id="@+id/nav_view"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:fitsSystemWindows="true"
        app:headerLayout="@layout/nav_header_main"
        app:menu="@menu/activity_main_drawer" />

</androidx.drawerlayout.widget.DrawerLayout>

app_bar_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
    android:orientation="vertical"
    android:background="@android:color/white">

    <com.jiuyin.dianjing.view.CustomViewPager
        android:id="@+id/vp_view_container"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"/>

    <com.google.android.material.bottomnavigation.BottomNavigationView
        android:id="@+id/bottom_nav_view"
        android:layout_width="match_parent"
        android:layout_height="200px"
        android:layout_gravity="bottom"
        android:layout_marginStart="0dp"
        android:layout_marginEnd="0dp"
        android:background="?android:attr/windowBackground"
        app:itemIconTint="@null"
        app:itemTextAppearanceActive="@style/nav_active"
        app:itemTextAppearanceInactive="@style/nav_inactive"
        app:itemTextColor="@color/color_state_menu_nav"
        app:labelVisibilityMode="labeled"
        app:menu="@menu/main_bottom_nav_menu"/>

</LinearLayout>

MainActivity.java

    @BindView(R.id.vp_view_container)
    ViewPager mViewPager;
    @BindView(R.id.bottom_nav_view)
    BottomNavigationView mBottomNavView;
    @BindView(R.id.drawer_layout)
    DrawerLayout mDrawerLayout;
    @BindView(R.id.nav_view)
    NavigationView mNavView;

    private SectionsPagerAdapter mSectionsPagerAdapter;
    private final List<Fragment> listFragment = new ArrayList<>();
    private final CusReceiver mCusReceiver = new CusReceiver();
    private final OrderReceiver mOrderReceiver = new OrderReceiver();
    private final BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener
            = new BottomNavigationView.OnNavigationItemSelectedListener() {
        @Override
        public boolean onNavigationItemSelected(@NonNull MenuItem item) {
            switch (item.getItemId()) {
                case R.id.navigation_first:
                    mViewPager.setCurrentItem(0);
                    return true;
                case R.id.navigation_second:
                    mViewPager.setCurrentItem(1);
                    return true;
                case R.id.navigation_third:
                    mViewPager.setCurrentItem(2);
                    return true;
                case R.id.navigation_four:
                    mViewPager.setCurrentItem(3);
                    return true;
                case R.id.navigation_five:
                    mViewPager.setCurrentItem(4);
                    return true;
                default:
                    break;
            }
            return false;
        }
    };

    @Override
    protected int setLayoutId() {
        return R.layout.activity_main;
    }

    @Override
    protected void initData() {
        registerReceiver();
        listFragment.add(new FragmentFirst());
        listFragment.add(new FragmentFirst());
        listFragment.add(new FragmentThird());
        listFragment.add(new FragmentFirst());
        listFragment.add(new FragmentFirst());
        mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager(), listFragment);
        mViewPager.setAdapter(mSectionsPagerAdapter);
        mViewPager.addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
            @Override
            public void onPageSelected(int position) {
                mBottomNavView.setSelectedItemId(mBottomNavView.getMenu().getItem(position).getItemId());
            }
        });
        mViewPager.setOffscreenPageLimit(listFragment.size() - 1);
        mBottomNavView.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener);
    }

    @Override
    protected void initView() {
        ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
                this, mDrawerLayout, R.string.app_name, R.string.app_name) {
            @Override
            public void onDrawerSlide(View drawerView, float slideOffset) {
                //可以重新侧滑方法,该方法实现侧滑动画,整个布局移动效果
                //获取mDrawerLayout中的第一个子布局,也就是布局中的RelativeLayout
                //获取抽屉的view
                View mContent = mDrawerLayout.getChildAt(0);
                float scale = 1 - slideOffset;
                float endScale = 0.8f + scale * 0.2f;
                float startScale = 1 - 0.3f * scale;

                //设置左边菜单滑动后的占据屏幕大小
                drawerView.setScaleX(startScale);
                drawerView.setScaleY(startScale);
                //设置菜单透明度
                drawerView.setAlpha(0.6f + 0.4f * (1 - scale));

                //设置内容界面水平和垂直方向偏转量
                //在滑动时内容界面的宽度为 屏幕宽度减去菜单界面所占宽度
                mContent.setTranslationX(drawerView.getMeasuredWidth() * (1 - scale));
                //设置内容界面操作无效(比如有button就会点击无效)
                mContent.invalidate();
                //设置右边菜单滑动后的占据屏幕大小
                mContent.setScaleX(endScale);
                mContent.setScaleY(endScale);
            }
        };

        toggle.syncState();
        mDrawerLayout.addDrawerListener(toggle);
        mNavView.setItemIconTintList(null);
        //设置item的点击事件
        mNavView.setNavigationItemSelectedListener(item -> {
            int id = item.getItemId();
            switch (id) {
                case R.id.nav_home:
                    ToastUtil.showShort("nav_home");
                    break;
                case R.id.nav_gallery:
                    ToastUtil.showShort("nav_gallery");
                    break;
                case R.id.nav_slideshow:
                    ToastUtil.showShort("nav_slideshow");
                    break;
                case R.id.nav_tools:
                    ToastUtil.showShort("nav_tools");
                    break;
                default:
                    break;
            }
            mDrawerLayout.closeDrawer(GravityCompat.START);
            return true;
        });
    }

    /**
     * 主页按返回键返回桌面,程序不退出再次点击图标直接进入程序
     * 类似支付宝效果
     */
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK) {
            moveTaskToBack(true);
        } else {
            return false;
        }
        return true;
    }

    protected void registerReceiver() {
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(ACTION_NEED_LOGIN);
        intentFilter.addAction(ACTION_ORDER_UPLOAD);
        LocalBroadcastManager.getInstance(getContext()).registerReceiver(mCusReceiver, intentFilter);
        IntentFilter orderIntentFilter = new IntentFilter();
        orderIntentFilter.addAction(ACTION_ORDER_UPLOAD);
        registerReceiver(mOrderReceiver, orderIntentFilter);
    }

    protected void unRegisterReceiver() {
        LocalBroadcastManager.getInstance(getContext()).unregisterReceiver(mCusReceiver);
        unregisterReceiver(mOrderReceiver);
    }

    @Override
    protected void onDestroy() {
        unRegisterReceiver();
        super.onDestroy();
    }

    /**
     * token过期,重新登录
     */
    private void reLogin() {
        PMUtil.getInstance().finishAllAC();
        Context context = HelperApplication.getContext();
        Intent intent = new Intent(context, LoginMobileActivity.class);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        context.startActivity(intent);
    }

    private class CusReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (action == null) {
                return;
            }
            LogUtil.log("MainActivity onReceive action " + action);
            if (ACTION_NEED_LOGIN.equals(action)) {
            }
        }
    }

通过ViewPager来绑定和切换Fragment.

如果有不需要ViewPager的地方,可以用其他的方式来实现动态切换。

比如项目中要做的搜索页面包括:

SearchActivity.java

SearchResultFragment.java //搜索结果
SearchSuggestFragment.java //搜索默认界面
SearchWordFragment.java     //输入匹配界面

搜索这个界面就需要SearchActivity来控制和切换上述的Fragment。

google新出了一个框架,可以通过fragment来控制和切换Fragment

SearchActivity的布局文件如下:

<fragment xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/nav_host_fragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    android:name="androidx.navigation.fragment.NavHostFragment"
    app:defaultNavHost="true"
    app:navGraph="@navigation/mobile_navigation"/>

其中代理了个navGraph导航控件,fragment写在mobile_navigation这个文件中。

<?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"
    android:id="@+id/mobile_navigation"
    app:startDestination="@+id/nav_search_suggest">

    <fragment
        android:id="@+id/nav_search_suggest"
        android:name="com.jiuyin.dianjing.ui.fragment.search.SearchSuggestFragment" />

    <fragment
        android:id="@+id/nav_search_wold"
        android:name="com.jiuyin.dianjing.ui.fragment.search.SearchWordFragment" />

    <fragment
        android:id="@+id/nav_search_result"
        android:name="com.jiuyin.dianjing.ui.fragment.search.SearchResultFragment" />
</navigation>

定义SearchActivity所需要的三个Fragment,app:startDestination="@+id/nav_search_suggest" 这个指定第一个Fragment。

SearchActivity.java主要代码:


   mNavController = Navigation.findNavController(this, R.id.nav_host_fragment);
 

上述代码主要初始化Navigation。

private void switchFragment(int id, Bundle bundle) {
        int current = mNavController.getCurrentDestination().getId();
        if (id != R.id.nav_search_suggest) {
            if (current != id) {
                mNavController.popBackStack();
                mNavController.navigate(id);
            }
        } else {
            mNavController.popBackStack();
            mNavController.navigate(id, bundle);
        }
    }

通过上述方法切换Fragment,因为切换的时候需要指定id,切换的时候,根据需要指定id,也就是navigation中定义的id。

通过上述切换Fragment会有个问题,就是切换的Fragment会重新创建,如果你需要当前只有一个的话,不需要其他的保持的话,可以通过上述的mNavController.popBackStack();来解决这个问题。