转载请注明出处:http://blog.csdn.net/htwhtw123/article/details/78441431
比较简单的用BottomNavigationView 、 ViewPager 、 Fragment 实现下方导航栏与上方可翻页页面,页面布局和逻辑在自定义的Fragment类中自行定义。点击这里跳转源码
(注意,所有项目中所有Fragment都是导入:import android.support.v4.app.Fragment;)
(注意:BottomNavigationView如果是新建项目想要引入的话,可以在创建项目的时候自动生成(新建项目时,选下图的一个activity),无需再看下面的1~4步骤)
(还有,新建活动也可以自动生成带有BottomNavigationView 的Activity,如下方式去新建活动):
(可以通过如上两种方式非常快速的完成对BottomNavigationView相关代码编写,但是请不要有BottomNavigationView所需的同名文件,否则因报错而无法生成。)
下面讲解一下纯手写非自动生成的方法:
1.BottomNavigationView所需库导入:
ctrl+alt+shift+s 打开project struct->选择左边的Module(默认是app)->点击右上 Dependencies->点击最右侧“+”符号->点击Library dependency->搜索栏输入design->选择以com.android开头的一项,点击OK。现在的导入的是下面的版本:
(采用这种方法而不是去build.gradle粘代码后同步,导入的会一直是最新版本的库。还有,一般android的库都是以com.android开头的)
2.创建导航栏图标
(也可以在网上自寻找png图片,因为只会显示点中和没有点中两种颜色,挑选时只需要注意形状即可,颜色会失效)
右击drawable文件夹->New->Vector Asset->点击下面圈出来的地方就可以选择图片。->选好后OK,next,finish就可以生成图片
3.设置导航栏子项
在res文件夹下新建menu文件夹,在menu中新建文navigation.xml:右击menu->new->Menu Resource,添加导航栏子项,从前到后依次会是导航栏从左到右的子项。
下面是完整代码
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/navigation_home"
android:icon="@drawable/ic_home_black_24dp"
android:title="@string/title_home"/>
<item
android:id="@+id/navigation_dashboard"
android:icon="@drawable/ic_dashboard_black_24dp"
android:title="@string/title_dashboard"/>
<item
android:id="@+id/navigation_notifications"
android:icon="@drawable/ic_notifications_black_24dp"
android:title="@string/title_notifications"/>
<item
android:id="@+id/navigation_notifications2"
android:icon="@drawable/ic_settings_black_24dp"
android:title="@string/title_notifications"/>
</menu>
item属性讲解:
id属性是必要的,需要在点击监听时时使用;android:icon是图标,同一时间只会显示一种颜色,只有选中和未选中时的两种颜色;android:title是会在图标下显示的字,默认情况只有选中项的字才会显示。
4.在布局中添加BottomNavigationView和 ViewPager
这种情况,我喜欢用LinearLayout,上方的ViewPager占满剩余屏幕(android:layout_weight=”1”),下方BottomNavigationView 高度为wrap_content。下面是Activity的布局文件。app:menu=”@menu/navigation”将刚刚写的子项定义文件加载进来,android:background=”?android:attr/windowBackground”的效果是:当BottomNavigationView与上方内容同色时,会显示的一条分割线,如下图中部。
<?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.support.v4.view.ViewPager
android:id="@+id/view_pager"
android:layout_width="match_parent"
android:layout_weight="1"
android:layout_height="0dp"/>
<android.support.design.widget.BottomNavigationView
android:id="@+id/navigation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/windowBackground"
app:menu="@menu/navigation"/>
</LinearLayout>
现在BottomNavigationView已经可以成功显示在Activity中了。下面介绍ViewPager初始化。
5.为ViewPager创建适配器
因为要添加的页面是Fragment所以让MyFragment继承FragmentPagerAdapter。listFragment用来存储显示页面对象。
(注意,项目中所有Fragment都是如下导入:import android.support.v4.app.Fragment,不要导入另一种),
class MyFragAdapter extends FragmentStatePagerAdapter {
Context context;
List<Fragment> listFragment;
public MyFragAdapter(FragmentManager fm, Context context, List<Fragment> listFragment) {
super(fm);
this.context = context;
this.listFragment = listFragment;
}
@Override
public Fragment getItem(int position) {
return listFragment.get(position);
}
@Override
public int getCount() {
return listFragment.size();
}
6.为ViewPager准备显示的Fragment页面
这里写一个例子。
首先是准备Fragment的布局,这里没有限制,根据需要自己写就行,为了举个Fragment中控件使用的简单例子,我加了一个按钮。下面是frag1.xml的代码:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="frag1"
android:textSize="30sp"/>
<Button
android:id="@+id/bt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Fragment中的按钮"
android:textAllCaps="false"/>
</LinearLayout>
然后下面是加载这个布局的Fragment1.java中的代码,获取context使用getActivity()方法。通过获取的view,使用view.findViewById()可以获取控件的对象。
public class Fragment1 extends Fragment {
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.frag1, container, false);
Button bt = (Button) view.findViewById(R.id.bt);
bt.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(getActivity(), "Fragment1上的按钮被点击了", Toast.LENGTH_SHORT).show();
}
});
return view;
}
}
关于使用碎片踩过一些坑,有兴趣的朋友可以看一下:Android 碎片使用的一些坑
之后我与之上相似的又写了3个Fragment。
7.活动中的代码逻辑
终于到最重要的地方了但是依旧简单,这里将ViewPager和BottomNavigationView 关联到一起的思想就是:监听他们的点击或滑动事件,然后将对方设置成滑动到相应界面或相应子项被点下的状态。下面是代码:
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
ViewPager viewPager;
BottomNavigationView navigation;//底部导航栏对象
List<Fragment> listFragment;//存储页面对象
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();//初始化
}
private void initView() {
viewPager = (ViewPager) findViewById(R.id.view_pager);
navigation = (BottomNavigationView) findViewById(R.id.navigation);
//向ViewPager添加各页面
listFragment = new ArrayList<>();
listFragment.add(new Fragment1());
listFragment.add(new Fragment2());
listFragment.add(new Fragment3());
listFragment.add(new Fragment4());
MyFragAdapter myAdapter = new MyFragAdapter(getSupportFragmentManager(), this, listFragment);
viewPager.setAdapter(myAdapter);
//导航栏点击事件和ViewPager滑动事件,让两个控件相互关联
navigation.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
//这里设置为:当点击到某子项,ViewPager就滑动到对应位置
switch (item.getItemId()) {
case R.id.navigation_home:
viewPager.setCurrentItem(0);
return true;
case R.id.navigation_dashboard:
viewPager.setCurrentItem(1);
return true;
case R.id.navigation_notifications:
viewPager.setCurrentItem(2);
return true;
case R.id.navigation_setting:
viewPager.setCurrentItem(3);
return true;
default:
break;
}
return false;
}
});
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
//注意这个方法滑动时会调用多次,下面是参数解释:
//position当前所处页面索引,滑动调用的最后一次绝对是滑动停止所在页面
//positionOffset:表示从位置的页面偏移的[0,1]的值。
//positionOffsetPixels:以像素为单位的值,表示与位置的偏移
}
@Override
public void onPageSelected(int position) {
//该方法只在滑动停止时调用,position滑动停止所在页面位置
// 当滑动到某一位置,导航栏对应位置被按下
navigation.getMenu().getItem(position).setChecked(true);
//这里使用navigation.setSelectedItemId(position);无效,
//setSelectedItemId(position)的官网原句:Set the selected
// menu item ID. This behaves the same as tapping on an item
//未找到原因
}
@Override
public void onPageScrollStateChanged(int state) {
//这个方法在滑动是调用三次,分别对应下面三种状态
// 这个方法对于发现用户何时开始拖动,
// 何时寻呼机自动调整到当前页面,或何时完全停止/空闲非常有用。
// state表示新的滑动状态,有三个值:
// SCROLL_STATE_IDLE:开始滑动(空闲状态->滑动),实际值为0
// SCROLL_STATE_DRAGGING:正在被拖动,实际值为1
// SCROLL_STATE_SETTLING:拖动结束,实际值为2
}
});
}
}