当前位置:   article > 正文

Android studio中使用ViewPager和BottomNavigationView实现底部导航栏和碎片的同步切换_android studio底部导航栏

android studio底部导航栏

前言

通过几次的踩雷和摸索,完成了以上的操作,本教程写的详细全面,包教包会,对新手有好,看了不会的联系我,我倒立洗头给你看。

1.需要了解的一些知识

所需控件:

fragment

作为Android中最常用的控件,它有自己的声明周期,可以粗略地等比为能够分屏的activity,但是和activity有区别,fragment有自己的生命周期和接收、处理用户的事件。Fragment必须是依存与Activity而存在的。所以他们可以有自己的xml文件(布局文件)和class文件(处理逻辑的java类文件)。

Viewpager

ViewPager是可以实现多个界面的左右滑动的控件。ViewPager最典型的应用场景主要包 括引导页导航,轮转广告和页面菜单。其中有两个ViewPager适配器需要使用:

  • FragmentStatePagerAdapter:

    它是 PagerAdapter 的子类,用于处理多个碎片,并支持动态加载和销毁碎片来节省内存。适用于需要处理大量页面、支持动态刷新以及动态添加或删除页面的场景。例如图片浏览器、新闻阅读器等。

  • FragmentPagerAdapter:

    它也 是 PagerAdapter 的子类,与 FragmentStatePagerAdapter 类似,但有一些不同之处。与 FragmentStatePagerAdapter 相比,FragmentPagerAdapter 更适用于静态、数量有限的页面集合,并提供更好的滑动性能和用户体验,因为它不会销毁已经创建的碎片实例。

我们接下来要使用的是FragmentPagerAdapter。

java语法结构

List<>

List<> 是 Java 中的泛型类,一种动态数组,用于表示一个列表,可以存储任意类型的元素。在使用 List<> 时,需要在尖括号中指定具体的类型参数。可以实现增,删,替换等操作。举个例子

  1.  List<String> stringList = new ArrayList<>();
  2.  stringList.add("Hello");
  3.  stringList.add("World");
  4.  ​
  5.  List<Integer> integerList = new ArrayList<>();
  6.  integerList.add(1);
  7.  integerList.add(2);
  8.  ​
  9.  for (String str : stringList) {
  10.      System.out.println(str);
  11.  }
  12.  ​
  13.  for (int num : integerList) {
  14.      System.out.println(num);
  15.  }

  ​首先,创建了一个 List<String> 类型的列表 stringList,并向其中添加两个字符串元素 "Hello" 和 "World"。

接下来,创建了一个 List<Integer> 类型的列表 integerList,并向其中添加两个整数元素 1 和 2。

然后,通过使用增强型 for 循环,分别对 stringListintegerList 进行迭代遍历,将列表中的元素逐个取出,并分别打印输出。

在第一个循环中,每次迭代将当前的字符串元素赋值给变量 str,然后将其输出。因此,在循环的第一次迭代中,会输出 "Hello",在第二次迭代中,会输出 "World"。

在第二个循环中,每次迭代将当前的整数元素赋值给变量 num,然后将其输出。因此,在循环的第一次迭代中,会输出 1,第二次迭代中,会输出 2。

所以最终的输出结果是:

  1.  Hello
  2.  World
  3.  1
  4.  2

2.准备工作

1.导入所需包

在build.gradle文件中引入implementation 'com.google.android.material:material:1.9.0'

当导入进去后,可能会有版本不一样的情况,如果报黄,点击alt+enter键,会出现修改版本的选项。当引入完以后,就可以点击sync进行数据操作同步。如果已经有了这个包就可以不用管他了。

2.创建三个fragment

根据我上述截图来操作,创建三个名字分别为homeFragment,moreFragment,userFragment。其对应布局文件名字为fragment_home,fragment_more,fragment_user。

创建好后三个fragment的class里面的暂时先不用管

3.创建menu

BottomNavigationView 可以来创建底部导航栏,而菜单项组合menu就在其中。

先在res文件夹中创建一个menu文件夹

然后创建一个名为menu的xml文件,将下列代码写进文件中:

  1.  <?xml version="1.0" encoding="utf-8"?>
  2.  <menu xmlns:android="http://schemas.android.com/apk/res/android">
  3.  ​
  4.      <item
  5.          android:id="@+id/item_home"
  6.          android:icon="@drawable/home"
  7.          android:title="@string/home"
  8.          />
  9.  ​
  10.      <item
  11.          android:id="@+id/item_more"
  12.          android:icon="@drawable/more"
  13.          android:title="@string/more"
  14.          />
  15.  ​
  16.      <item
  17.          android:id="@+id/item_user"
  18.          android:icon="@drawable/user"
  19.          android:title="@string/user"
  20.          />
  21.  ​
  22.  </menu>
 

其中@drawable/user是drawable下的user.png,一张图片,由 android:icon引用。同理"@string/user"是 res/values/strings.xml文件里的<string name="user">用户</string>代码,这样,底部导航栏菜单项的标题就会显示为 "用户"。这里推荐使用阿里矢量图标库。drawable文件是用于存放图片的,把图复制粘贴进去就行。其中名字不能为中文等字符,不然会报错。

 

4.编写ViewPagerAdapter继承FragmentPagerAdapter

完成底部导航栏的切换我们需要FragmentPagerAdapter的帮助,所以创建并编写ViewPagerAdapter类(这是我取的名字,想取什么名字都行)继承FragmentPagerAdapter。

代码如下:

  1. package com.example.bnvone.Adapter;
  2.  ​
  3.  import androidx.annotation.NonNull;
  4.  import androidx.fragment.app.Fragment;
  5.  import androidx.fragment.app.FragmentManager;
  6.  import androidx.fragment.app.FragmentPagerAdapter;
  7.  ​
  8.  import java.util.List;
  9.  ​
  10.  public class ViewPagerAdapter extends FragmentPagerAdapter {
  11.      private List<Fragment> mfragmentList;
  12.      public ViewPagerAdapter(@NonNull FragmentManager fm,List<Fragment> fragmentList) {
  13.          super(fm);//调用父类构造函数,传递FragmentManager 参数
  14.                    //用于确保适配器类内部具有有效的 FragmentManager 实例,从而顺利完成片段管理和展示的任务
  15.          this.mfragmentList = fragmentList;
  16.  ​
  17.     }//设置 ViewPager 的适配器。
  18.  ​
  19.  ​
  20.      @NonNull
  21.      @Override
  22.      public Fragment getItem(int position) {
  23.          return  mfragmentList == null ? null: mfragmentList.get(position);
  24.     }// mfragmentList == null ? null 判断mfragmentList是否为空,如果不为null,执行:后面的代码。
  25.       //mfragmentList.get(position)获得第position个的fragment对象返回
  26.  ​
  27.      @Override
  28.      public int getCount() {
  29.          return  mfragmentList == null ? null: mfragmentList.size();
  30.          // 返回片段对象的数量,即mfragmentList列表中的元素个数
  31.     }
  32.  }
 
 ​

这个适配器的作用是将 Fragment 列表与 ViewPager 关联起来,以便在 ViewPager 中展示相应的内容。通过重写 getItemgetCount 方法,适配器可以根据位置返回对应的 Fragment 对象,并在 ViewPager 中正确显示。其中重写的方法我们会在MainActivity中调用。

前期准备的差不多了,可以开始实现了。

3.实现底部导航栏同步切换操作

1.创建MainActivity

  1.  <?xml version="1.0" encoding="utf-8"?>
  2.  <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3.      xmlns:app="http://schemas.android.com/apk/res-auto"
  4.      xmlns:tools="http://schemas.android.com/tools"
  5.      android:layout_width="match_parent"
  6.      android:layout_height="match_parent"
  7.      android:orientation="vertical"
  8.      >
  9.  ​
  10.      <androidx.viewpager.widget.ViewPager
  11.          android:id="@+id/vp"
  12.          android:layout_width="match_parent"
  13.          android:layout_height="0dp"
  14.          android:layout_weight="1"
  15.       />
  16.  ​
  17.  ​
  18.      <com.google.android.material.bottomnavigation.BottomNavigationView
  19.          android:id="@+id/nav_bottom"
  20.          android:layout_width="match_parent"
  21.          android:layout_height="60dp"
  22.          android:background="#ffffff"
  23.          app:menu="@menu/menu_nav"
  24.          />
  25.  ​
  26.  ​
  27.  ​
  28.  </LinearLayout>
 ​

使用的线性布局,也可以使用其他的。出现vp <androidx.viewpager.widget.ViewPager>: No speakable text present问题不大,可以运行。得到的样子是这样的

 

2.编写MainActivity

  1.  package com.example.bnvone;
  2.  ​
  3.  import androidx.annotation.NonNull;
  4.  import androidx.appcompat.app.AppCompatActivity;
  5.  import androidx.fragment.app.Fragment;
  6.  import androidx.viewpager.widget.ViewPager;
  7.  ​
  8.  import android.annotation.SuppressLint;
  9.  import android.os.Bundle;
  10.  import android.view.MenuItem;
  11.  ​
  12.  import com.example.bnvone.Adapter.ViewPagerAdapter;
  13.  import com.example.bnvone.Fragment.homeFragment;
  14.  import com.example.bnvone.Fragment.moreFragment;
  15.  import com.example.bnvone.Fragment.userFragment;
  16.  ​
  17.  import com.google.android.material.bottomnavigation.BottomNavigationView;
  18.  ​
  19.  import java.util.ArrayList;
  20.  import java.util.List;
  21.  ​
  22.  public class MainActivity extends AppCompatActivity {
  23.      private BottomNavigationView navigationView;
  24.      private ViewPager viewPager;
  25.  ​
  26.  ​
  27.      @Override
  28.      protected void onCreate(Bundle savedInstanceState) {
  29.  ​
  30.          super.onCreate(savedInstanceState);
  31.          setContentView(R.layout.activity_main);
  32.  ​
  33.          navigationView = findViewById(R.id.nav_bottom);
  34.          viewPager = findViewById(R.id.vp);
  35.  ​
  36.          homeFragment homeFragment= new homeFragment();
  37.  ​
  38.          List<Fragment> fragments = new ArrayList<>();
  39.          fragments.add(new homeFragment()); //新建一个homeFragment对象将这个对象加入到数组fragments中
  40.          fragments.add(new moreFragment());
  41.          fragments.add(new userFragment());
  42.  ​
  43.          ViewPagerAdapter viewPagerAdapter = new ViewPagerAdapter(getSupportFragmentManager(),fragments);
  44.          //创建对象并通过构造函数初始化,该适配器可以知道要显示哪些片段。
  45.          viewPager.setAdapter(viewPagerAdapter);
  46.          //将前面创建的 viewPagerAdapter 适配器设置给 viewPager 视图组件,以便在 ViewPager 中显示相应的页面。
  47.          //底部导航栏监听事件
  48.          navigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
  49.              @SuppressLint("NonConstantResourceId")
  50.              @Override
  51.              public boolean onNavigationItemSelected(@NonNull MenuItem item) {
  52.              //根据菜单ID显示页面
  53.                  switch (item.getItemId()) {//监听事件中,点击菜单立马执行item.getItemId()方法
  54.                      //item.getItemId() 方法用于获取选中的 MenuItem 的唯一标识符(ID)
  55.                      case R.id.item_home://R.id.xxx是整数类型。
  56.                          viewPager.setCurrentItem(0);
  57.                          // 将 ViewPager 的当前页面显示成索引为 0 的页面
  58.                          return true;
  59.                      case R.id.item_more:
  60.                          viewPager.setCurrentItem(1);
  61.                          return true;
  62.                      case R.id.item_user:
  63.                          viewPager.setCurrentItem(2);
  64.                          return true;
  65.                 }
  66.                  return false;
  67.             }
  68.  ​
  69.         });
  70.  ​
  71.          // 添加页面切换的监听器,根据页面切换实现菜单切换
  72.      viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
  73.          @Override
  74.          public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
  75.  ​
  76.         }
  77.  ​
  78.          @Override
  79.          public void onPageSelected(int position) {
  80.          switch (position){  // 根据页面位置更新导航栏的选中状态
  81.              case 0:
  82.                  navigationView.setSelectedItemId(R.id.item_home);
  83.                  //将导航栏中的选中项设置为 R.id.item_home
  84.                  break;
  85.              case 1:
  86.                  navigationView.setSelectedItemId(R.id.item_more);
  87.                  break;
  88.              case 2:
  89.                  navigationView.setSelectedItemId(R.id.item_user);
  90.                  break;
  91.         }
  92.       }
  93.  ​
  94.          @Override
  95.          public void onPageScrollStateChanged(int state) {}
  96.     });//通过使用页面切换监听器,
  97.          // 我们可以根据页面切换的情况来更改导航栏的选中状态,
  98.          // 从而实现页面切换时导航栏菜单的同步切换效果。
  99.  ​
  100.     }
  101.  }
  102.  ​
 

实现了底部导航栏与 ViewPager 的联动效果。底部导航栏通过监听选中项的改变,然后根据选中项对应的页面索引,调用 ViewPager 的 setCurrentItem 方法来切换到相应的页面。同时,ViewPager 添加了页面切换的监听器,通过监听页面的切换事件,更新底部导航栏的选中状态,以实现同步切换效果。

完成以下操作就可以实现同步切换了。

补充

我的fragment代码:

  1. <?xml version="1.0" encoding="utf-8"?>
  2.  <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3.      xmlns:app="http://schemas.android.com/apk/res-auto"
  4.      xmlns:tools="http://schemas.android.com/tools"
  5.      android:layout_width="match_parent"
  6.      android:layout_height="match_parent"
  7.      tools:context=".Fragment.moreFragment">
  8.  ​
  9.      <LinearLayout
  10.          android:layout_width="match_parent"
  11.          android:layout_height="match_parent"
  12.          app:layout_constraintBottom_toBottomOf="parent"
  13.          app:layout_constraintEnd_toEndOf="parent"
  14.          app:layout_constraintStart_toStartOf="parent"
  15.          app:layout_constraintTop_toTopOf="parent">
  16.  ​
  17.          <TextView
  18.              android:layout_width="wrap_content"
  19.              android:layout_height="wrap_content"
  20.              android:text="2"
  21.              android:textSize="50sp" />
  22.  ​
  23.      </LinearLayout>
  24.  </androidx.constraintlayout.widget.ConstraintLayout>
 

本次操作所需要所有文件:

 

效果如下:

是不是很简单,有手就行

会了的话就给个推荐之类的吧,各位兄弟们

----------------2023年11月23日修改----------------

拓展使用

随着软件的不断成熟,导航栏的样式也变得越来越多,各大APP顶部导航栏流行使用字体当做顶部导航栏,并在选中后实现放大等样式改变的效果。

我们来简单实现一下这种效果:

创建viewpagerActivity

创建一个新的activity,命名viewpagerActivity;

XML文件

他的布局代码如下:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. xmlns:app="http://schemas.android.com/apk/res-auto"
  4. xmlns:tools="http://schemas.android.com/tools"
  5. android:layout_width="match_parent"
  6. android:layout_height="match_parent"
  7. tools:context=".viewpagerActivity"
  8. android:orientation="vertical">
  9. <LinearLayout
  10. android:layout_width="match_parent"
  11. android:layout_height="40dp"
  12. android:orientation="horizontal">
  13. <TextView
  14. android:id="@+id/textView1"
  15. android:text="动态"
  16. android:textSize="20sp"
  17. android:layout_marginLeft="10dp"
  18. android:layout_width="wrap_content"
  19. android:layout_height="wrap_content"/>
  20. <TextView
  21. android:id="@+id/textView2"
  22. android:text="排行榜"
  23. android:textSize="20sp"
  24. android:layout_marginLeft="20dp"
  25. android:layout_width="wrap_content"
  26. android:layout_height="wrap_content"/>
  27. </LinearLayout>
  28. <androidx.viewpager.widget.ViewPager
  29. android:id="@+id/viewPager"
  30. android:layout_width="match_parent"
  31. android:layout_height="0dp"
  32. android:layout_weight="1"
  33. tools:ignore="SpeakableTextPresentCheck">
  34. </androidx.viewpager.widget.ViewPager>
  35. </LinearLayout>

是这种效果:

再按上文创建两个fragment,一个oneFragment,一个twoFragment;

java文件
  1. import androidx.appcompat.app.AppCompatActivity;
  2. import androidx.fragment.app.Fragment;
  3. import androidx.viewpager.widget.ViewPager;
  4. import android.graphics.Color;
  5. import android.os.Bundle;
  6. import android.util.TypedValue;
  7. import android.view.View;
  8. import android.widget.TextView;
  9. import android.widget.Toast;
  10. import java.util.ArrayList;
  11. import java.util.List;
  12. public class viewpagerActivity extends AppCompatActivity {
  13. TextView textView1;
  14. TextView textView2;
  15. ViewPager viewPager;
  16. @Override
  17. protected void onCreate(Bundle savedInstanceState) {
  18. super.onCreate(savedInstanceState);
  19. setContentView(R.layout.activity_viewpager);
  20. textView1 = findViewById(R.id.textView1);
  21. textView2 = findViewById(R.id.textView2);
  22. viewPager = findViewById(R.id.viewPager);
  23. List<Fragment> fragments = new ArrayList<>();
  24. fragments.add(new oneFragment());
  25. fragments.add(new twoFragment());
  26. ViewPagerAdapter viewPagerAdapter = new ViewPagerAdapter(getSupportFragmentManager(),fragments);
  27. viewPager.setAdapter(viewPagerAdapter);
  28. textView1.animate().scaleX(1.5f).scaleY(1.5f).setDuration(0).withEndAction(new Runnable() {
  29. @Override
  30. public void run() {
  31. }
  32. }).start();
  33. textView1.setTextColor(Color.BLACK);
  34. textView1.setOnClickListener(new View.OnClickListener() {
  35. @Override
  36. public void onClick(View v) {
  37. textView1.setTextColor(Color.BLACK);
  38. viewPager.setCurrentItem(0);
  39. }
  40. });
  41. textView2.setOnClickListener(new View.OnClickListener() {
  42. @Override
  43. public void onClick(View v) {
  44. viewPager.setCurrentItem(1);
  45. }
  46. });
  47. viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
  48. @Override
  49. public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
  50. }
  51. @Override
  52. public void onPageSelected(int position) {
  53. switch (position){
  54. case 0:
  55. textView1.animate().scaleX(1.5f).scaleY(1.5f).setDuration(100).withEndAction(new Runnable() {
  56. @Override
  57. public void run() {
  58. }
  59. }).start();
  60. textView2.animate().scaleX(1f).scaleY(1f).setDuration(100).withEndAction(new Runnable() {
  61. @Override
  62. public void run() {
  63. }
  64. }).start();
  65. textView1.setTextColor(Color.BLACK);
  66. textView2.setTextColor(Color.GRAY);
  67. break;
  68. case 1:
  69. textView2.animate().scaleX(1.5f).scaleY(1.5f).setDuration(100).withEndAction(new Runnable() {
  70. @Override
  71. public void run() {
  72. }
  73. }).start();
  74. textView1.animate().scaleX(1f).scaleY(1f).setDuration(100).withEndAction(new Runnable() {
  75. @Override
  76. public void run() {
  77. }
  78. }).start();
  79. textView2.setTextColor(Color.BLACK);
  80. textView1.setTextColor(Color.GRAY);
  81. break;
  82. }
  83. }
  84. @Override
  85. public void onPageScrollStateChanged(int state) {
  86. }
  87. });
  88. }
  89. }

完成后的效果如下:

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/714636
推荐阅读
相关标签
  

闽ICP备14008679号