Fragment+ViewPager+BottomTab实现页面导航


前文我们介绍了Fragment的应用之一,最简单的页面导航方式: Fragment+BottomTab实现页面导航。这种方式虽然能实现点击底部导航按钮切换页面,但不够友好。我们更期望的是左右滑动页面也能实现页面切换,同时底部按钮的选中状态也跟着切换。本文就将介绍如何通过Fragment+ViewPager+BottomTab实现左右页面滑动切换这一效果。

1. 目标效果

效果截图

2. 案例教学

2.1 主界面布局

首先,依然是主界面的布局。跟前文类似,依然是上下两部分,上面是页面主体内容区域,底部是导航按钮。只不过这次主体内容区域我们使用ViewPager这个控件来盛放多个Fragment。(还不熟悉ViewPager这个控件的小伙伴可以自行学习下它的基本用法,这里不展开讲述了)

布局文件activity_fragment_view_pager_bottom.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"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <androidx.viewpager.widget.ViewPager
        android:id="@+id/vp"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />

    <include layout="@layout/bottom_tab_layout" />

</LinearLayout>

我们直接引用了前文代码的bottom_tab_layout,也就是底部导航按钮布局。这里看出将代码分开写的好处了吧,可以多处引入使用,非常方便。

2.2 准备Fragment

这里的Fragment我们也使用前文中的那个ExampleFragment,不再重复不必要的信息。详细介绍请看前文。

public class ExampleFragment extends Fragment {
    // 改为public,以便于外界能引用到
    public static final String ARG_PARAM1 = "param1";
    public static final String ARG_PARAM2 = "param2";

    private String mParam1;
    private String mParam2;
    
    private TextView mTvContent;

    public ExampleFragment() {
    }
   
    public static ExampleFragment newInstance(String param1, String param2) {
        ExampleFragment fragment = new ExampleFragment();
        Bundle args = new Bundle();
        args.putString(ARG_PARAM1, param1);
        args.putString(ARG_PARAM2, param2);
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (getArguments() != null) {
            mParam1 = getArguments().getString(ARG_PARAM1);
            mParam2 = getArguments().getString(ARG_PARAM2);
        }
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_example, container, false);
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        mTvContent = view.findViewById(R.id.tv_content);
        // 如果传入的参数有值,则设置内容
        if (!TextUtils.isEmpty(mParam1)) {
            mTvContent.setText(mParam1);
        }
    }
}

2.3 主界面MainActivity

接下来是代码部分。首先还是将控件做一些基础的声明和初始化。

/**
 * viewpager+fragment+bottomTab
 * 普通的控件拼接成的底部导航菜单
 */
public class FragmentViewPagerBottomActivity extends AppCompatActivity implements View.OnClickListener {

    private RelativeLayout mRlHome, mRlSetting, mRlMine;
    private TextView mTvHome, mTvFind, mTvMine;
    private ImageView mIvHome, mIvFind, mIvMine;
    private ViewPager mViewPager;

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

        initView();
        initData();
        initEvent();
    }
    
	private void initView() {

        mViewPager = findViewById(R.id.vp);
        mRlHome = findViewById(R.id.rl_home);
        mRlSetting = findViewById(R.id.rl_find);
        mRlMine = findViewById(R.id.rl_mine);

        mTvMine = findViewById(R.id.tv_mine);
        mTvFind = findViewById(R.id.tv_find);
        mTvHome = findViewById(R.id.tv_home);

        mIvMine = findViewById(R.id.iv_mine);
        mIvFind = findViewById(R.id.iv_find);
        mIvHome = findViewById(R.id.iv_home);
    }

    private void initData() {

    }

    private void initEvent() {
    	// 为 底部导航按钮 设置点击事件监听
        mRlHome.setOnClickListener(this);
        mRlSetting.setOnClickListener(this);
        mRlMine.setOnClickListener(this);
    }
    
    @Override
    public void onClick(View v) {
    	// 底部导航按钮点击后响应
        
    }
 }

2.4 ViewPager部分

ViewPager是一种方便多个页面滑动切换的控件,页面可以是View,也可以是Fragment,常常用来做轮播图、引导页面等。像ListView、RecyclerView一样,这种集合型的控件有个共同点:数据和View分离,中间用适配器adapter连接。

View我们已经有了,就是ViewPager,接下来准备数据。这里的数据就是多个Fragment页面

// 全局变量
private List<Fragment> mFragmentList;

// 添加用来测试的Frament,这里添加三个
private void initData() {
    mFragmentList = new ArrayList<>();
    for (int i = 0; i < 3; i++) {
        ExampleFragment fragment = ExampleFragment.newInstance("fragment" + i, "");
        mFragmentList.add(fragment);
    }
}

最后是关键的适配器,将数据和View适配起来。MyViewPagerAdapterForFragment.java:

public class MyViewPagerAdapterForFragment extends FragmentPagerAdapter {

    private List<Fragment> mFragmentList;

    public MyViewPagerAdapterForFragment(@NonNull FragmentManager fm, List<Fragment> fragmentList) {
        super(fm);
        mFragmentList = fragmentList;
    }

    @NonNull
    @Override
    public Fragment getItem(int position) {
        return mFragmentList == null ? null : mFragmentList.get(position);
    }

    @Override
    public int getCount() {
        return mFragmentList == null ? 0 : mFragmentList.size();
    }
}

这个适配器也比较简单,继承FragmentPagerAdapter,然后实现其中的两个方法,getItem、getCount:获取每一个Fragment,获取Fragment的总个数。数据源是mFragmentList,通过构造方法传进来。

看看在Activity中如何把这三者(ViewPager、mFragmentList、FragmentPagerAdapter)连接起来。
代码如下:

// 全局变量
private MyViewPagerAdapterForFragment mMyViewPagerAdapter;

private void initEvent() {
	// 省略上文代码...
	// 创建adapter
    mMyViewPagerAdapter = new MyViewPagerAdapterForFragment(getSupportFragmentManager(), mFragmentList);
    // 为ViewPager设置adapter
    mViewPager.setAdapter(mMyViewPagerAdapter);
}

也简单,只需要创建出adapter,再为ViewPager设置进去就行了。创建的时候传进去了数据源mFragmentList,这样就把三者连接起来了。
到这里,运行代码就已经可以看到三个页面,并且可以滑动左右切换了。但是底部导航按钮还没有变化,别急,我们接着往下看。

2.5 ViewPager联动底部导航按钮

上文我们做了ViewPager的滑动切换页面部分,我们期望的是滑动上面的页面,底部的按钮也跟着状态切换。那么就要求我们监听ViewPager页面滑动,然后响应方法里对按钮做状态切换。
在initEvent方法里继续添加代码:

// 为ViewPager添加页面切换监听
mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
   @Override
   public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
   }

   @Override
   public void onPageSelected(int position) {   
       Toast.makeText(FragmentViewPagerBottomActivity.this, "当前第" + position + "页", Toast.LENGTH_SHORT).show();
       // 页面切换后的处理
       onPagerSelected(position);
   }

   @Override
   public void onPageScrollStateChanged(int state) {
   }
});

主要是 onPageSelected 这个回调方法,当真正的滑到了另一个页面时会调用它,参数position表示当前第几个页面,从0开始计数。
我们在里面加了Toast,方便观察当前第几个页面。接着我们需要处理底部导航按钮状态切换的逻辑,这部分我们封装成一个方法来处理,即:onPagerSelected方法。

private void onPagerSelected(int position) {
    resetBottomTab();
    switch (position) {
        case 0:
            mIvHome.setSelected(true);
            mTvHome.setTextColor(getResources().getColor(R.color.green_500));
            break;
        case 1:
            mIvFind.setSelected(true);
            mTvFind.setTextColor(getResources().getColor(R.color.green_500));
            break;
        case 2:
            mIvMine.setSelected(true);
            mTvMine.setTextColor(getResources().getColor(R.color.green_500));
            break;
        default:
            break;
    }
}

private void resetBottomTab() {
     mTvHome.setTextColor(getResources().getColor(R.color.black));
     mTvFind.setTextColor(getResources().getColor(R.color.black));
     mTvMine.setTextColor(getResources().getColor(R.color.black));

     mIvHome.setSelected(false);
     mIvFind.setSelected(false);
     mIvMine.setSelected(false);
 }

跟前文类似,先重置所有按钮状态(恢复成都不选中),再根据当前的选中的position,设置对应的按钮为绿色。
这样就完成了ViewPager页面切换与底部按钮的联动。

这就结束了吗?
没有,不要忘了ViewPager和底部按钮的联动需要是双向的。上面只是页面切换 --> 底部按钮状态变换,还需要反过来:点击底部导航导航按钮 --> 页面切换。

2.6 底部导航按钮联动ViewPager

这一步也很简单。前文我们已经为底部按钮设置了点击监听,并且预留了onClick方法。接下来只需要在onClick方法中做页面切换的逻辑就行了,我们依然封装成一个方法来实现。

@Override
public void onClick(View v) {
    setBottomTabSelected(v.getId());
}
private void setBottomTabSelected(int tabId) {
    switch (tabId) {
        case R.id.rl_home:
            mViewPager.setCurrentItem(0);
            break;
        case R.id.rl_find:
            mViewPager.setCurrentItem(1);
            break;
        case R.id.rl_mine:
            mViewPager.setCurrentItem(2);
            break;
        default:
            break;
    }
}

可见,切换ViewPager里的页面只需要一行代码:mViewPager.setCurrentItem(position); ViewPager还是很方便的吧。

结束了吗?
还差最后一步,跟前文一样,最后我们还需要设置一个默认进入的页面,也就是让ViewPager默认显示哪一页。
在initEvent方法最后添加:onPagerSelected(0);即可。这就是封装方法的好处,体会到了吗。

3. 完整代码

终于,我们完成了整个案例。下面附上整个主界面FragmentViewPagerBottomActivity的完整代码。
至于其他的资源文件和前文Fragment+BottomTab实现页面导航一样,不再复述。

/**
 * viewpager+fragment+bottomTab
 * 普通的控件拼接成的底部导航菜单
 */
public class FragmentViewPagerBottomActivity extends AppCompatActivity implements View.OnClickListener {

    private RelativeLayout mRlHome, mRlSetting, mRlMine;
    private TextView mTvHome, mTvFind, mTvMine;
    private ImageView mIvHome, mIvFind, mIvMine;
    private ViewPager mViewPager;

    private List<Fragment> mFragments;
    private MyViewPagerAdapterForFragment mMyViewPagerAdapter;


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

        initView();
        initData();
        initEvent();
    }


    private void initData() {
        mFragments = new ArrayList<>();
        for (int i = 0; i < 3; i++) {
            ExampleFragment fragment = ExampleFragment.newInstance("fragment" + i, "");
            mFragments.add(fragment);
        }
    }

    private void initEvent() {
        mRlHome.setOnClickListener(this);
        mRlSetting.setOnClickListener(this);
        mRlMine.setOnClickListener(this);

        mMyViewPagerAdapter = new MyViewPagerAdapterForFragment(getSupportFragmentManager(), mFragments);
        mViewPager.setAdapter(mMyViewPagerAdapter);
        mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

            }

            @Override
            public void onPageSelected(int position) {
                onPagerSelected(position);
                Toast.makeText(FragmentViewPagerBottomActivity.this, "当前第" + position + "页", Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onPageScrollStateChanged(int state) {

            }
        });
        onPagerSelected(0);
    }

    private void initView() {

        mViewPager = findViewById(R.id.vp);
        mRlHome = findViewById(R.id.rl_home);
        mRlSetting = findViewById(R.id.rl_find);
        mRlMine = findViewById(R.id.rl_mine);

        mTvMine = findViewById(R.id.tv_mine);
        mTvFind = findViewById(R.id.tv_find);
        mTvHome = findViewById(R.id.tv_home);

        mIvMine = findViewById(R.id.iv_mine);
        mIvFind = findViewById(R.id.iv_find);
        mIvHome = findViewById(R.id.iv_home);
    }

    @Override
    public void onClick(View v) {
        setBottomTabSelected(v.getId());
    }

    private void resetBottomTab() {
        mTvHome.setTextColor(getResources().getColor(R.color.black));
        mTvFind.setTextColor(getResources().getColor(R.color.black));
        mTvMine.setTextColor(getResources().getColor(R.color.black));

        mIvHome.setSelected(false);
        mIvFind.setSelected(false);
        mIvMine.setSelected(false);
    }

    private void onPagerSelected(int position) {
        resetBottomTab();
        switch (position) {
            case 0:
                mIvHome.setSelected(true);
                mTvHome.setTextColor(getResources().getColor(R.color.green_500));
                break;
            case 1:
                mIvFind.setSelected(true);
                mTvFind.setTextColor(getResources().getColor(R.color.green_500));
                break;
            case 2:
                mIvMine.setSelected(true);
                mTvMine.setTextColor(getResources().getColor(R.color.green_500));
                break;
            default:
                break;
        }
    }

    private void setBottomTabSelected(int tabId) {
        switch (tabId) {
            case R.id.rl_home:
                mViewPager.setCurrentItem(0);
                break;
            case R.id.rl_find:
                mViewPager.setCurrentItem(1);
                break;
            case R.id.rl_mine:
                mViewPager.setCurrentItem(2);
                break;
            default:
                break;
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

子林Android

感谢老板,老板大气!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值