说明:因为我习惯实用DataBinding,所以本文使用了DataBinding 的知识,如果对 DataBinding 还没有一点了解的强烈建议大家一定先去了解一下 DataBinding 相关的知识Android DataBinding 详解)。
下面我们开始一步步来实现:
1.首先我们先来创建所需要的 Fragement 以及相对应的布局文件,这里我们拿首页 HomeFragment 来做示例:
首先创建 fragment_home.xml 布局文件如下:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="首页"
android:textSize="40sp" />
</LinearLayout>
</layout>
这里非常简单,一个线性布局里面放一个 TextView,想必大家一看就明白,只是要注意最外层布局是 <layout> 标签,这就是刚才提到的 DataBinding 需要的,和我们平时的布局不太一样的一点
接下来创建 HomeFragment 类:
package com.example.qiudengjiao.tablayout.Module.home;
import android.databinding.DataBindingUtil;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.example.qiudengjiao.tablayout.Base.BaseFragment;
import com.example.qiudengjiao.tablayout.R;
import com.example.qiudengjiao.tablayout.databinding.FragmentHomeBinding;
/**
* 主界面 - 首页Fragment
* Created by qiudengjiao on 2017/5/6.
*/
public class HomeFragment extends BaseFragment {
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
FragmentHomeBinding binding = DataBindingUtil.inflate(inflater, R.layout.fragment_home, container, false);
return binding.getRoot();
}
}
我们来简单看一下上面的这段代码,和我们平时写的其实差不太多,使用 DataBindingUtil 调用 inflate() 方法来加载我们刚写好的布局,返回一个 FramentHomeBinding 对象,这个对象是自动生成的,这个对象的生成和布局文件的名字是有关系的,生成规则就是布局文件的名字加 Binding,更详细的大家可以去看我关于 DataBinding 的博文,这里不再展开详细描述,再就是 return binding.getRoot(),这句代码就是返回上面绑定的布局文件,到这里我们的首页 HomeFragment 就创建完成了,剩下了热卖、购物车、分类、我的依次类推,我们一一创建完成,这里不再描述,如下:
这样我们所需要的所有 Fragment 就全部创建完成,需要多少个 Tab 就创建多少个相对应的 Fragment,这个大家根据自己的项目来定
接下来我们来创建主布局文件 activity_main.xml 代码如下:
这里需要注意的是需要先在 app/build.gradle 中添加如下依赖,如果你的项目中还没有添加的话:
compile 'com.android.support:design:25.1.0'
<?xml version="1.0" encoding="utf-8"?>
<layout 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">
<LinearLayout
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.example.qiudengjiao.tablayout.MainActivity">
<android.support.v4.view.ViewPager
android:id="@+id/viewPager"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
</android.support.v4.view.ViewPager>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#d6d2d2" />
<android.support.design.widget.TabLayout
android:id="@+id/tabLayout"
android:layout_width="match_parent"
android:layout_height="60dp"
app:tabIndicatorHeight="0dp"
app:tabIndicatorColor="#33e0ff">
</android.support.design.widget.TabLayout>
</LinearLayout>
</layout>
和上面一样,最外层是 layout 布局,接着里面嵌套了一个线性布局,线性布局里面主要放了一个 ViewPager,一个 TabLayout,也比较简单,接下来就是定义适配器 Adapter 类,这里我们暂且命名为 MainTabAdapter,代码如下:
/**
* 适配器
* Created by qiudengjiao on 2017/5/6.
*/
public class MainTabAdapter extends FragmentPagerAdapter {
private MainActivity mContext;
private HomeFragment homeFragment;
private HotFragment hotFragment;
private CategoryFragment categoryFragment;
private CartFragment cartFragment;
private MineFragment mineFragment;
public MainTabAdapter(MainActivity mainActivity) {
super(mainActivity.getSupportFragmentManager());
this.mContext = mainActivity;
//初始化Fragment
homeFragment = new HomeFragment();
hotFragment = new HotFragment();
categoryFragment = new CategoryFragment();
cartFragment = new CartFragment();
mineFragment = new MineFragment();
}
@Override
public Fragment getItem(int position) {
if (position == 0) {
return homeFragment;
} else if (position == 1) {
return hotFragment;
} else if (position == 2) {
return categoryFragment;
} else if (position == 3) {
return cartFragment;
} else if (position == 4) {
return mineFragment;
}
return null;
}
@Override
public int getCount() {
return 5;
}
}
这里我们继承 FragmentPagerAdapter,并且在构造函数里初始化了我们刚才准备好的 Fragment,还实现了它的 getItem() 和 getCount() 方法,在 getItem() 方法中返回相应的 Fragment,getCount() 方法中返回相对应的个数
接下来我们开始来定义 MainAcitivity,代码如下:
package com.example.qiudengjiao.tablayout;
import android.databinding.DataBindingUtil;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import com.example.qiudengjiao.tablayout.Adapter.MainTabAdapter;
import com.example.qiudengjiao.tablayout.databinding.ActivityMainBinding;
public class MainActivity extends AppCompatActivity {
private ActivityMainBinding binding;
private FragmentPagerAdapter mAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
//初始化适配器
mAdapter = new MainTabAdapter(this);
binding.viewPager.setAdapter(mAdapter);
//将TabLayout和ViewPager关联起来
binding.tabLayout.setupWithViewPager(binding.viewPager);
binding.viewPager.setOffscreenPageLimit(5);
initTab();
}
/**
* 设置添加Tab
*/
private void initTab() {
binding.tabLayout.getTabAt(0).setCustomView(R.layout.tab_home);
binding.tabLayout.getTabAt(1).setCustomView(R.layout.tab_hot);
binding.tabLayout.getTabAt(2).setCustomView(R.layout.tab_category);
binding.tabLayout.getTabAt(3).setCustomView(R.layout.tab_cart);
binding.tabLayout.getTabAt(4).setCustomView(R.layout.tab_mine);
//默认选中的Tab
binding.tabLayout.getTabAt(0).getCustomView().setSelected(true);
}
}
这里的代码其实也挺干净简单,首先初始化适配器,并通过 setupWithViewPager() 方法将 TabLayout 和 ViewPager 关联起来,然后初始化 Tab,并通过 getTabAt() 方法返回指定的 Tab 选项,然后通过 setCustomView() 把自定义的布局设置到此 Tab 选项卡上,注意这里的布局是我们自己定义的,下面会讲解,然后通过 setSelected() 方法来设置默认选中的 Tab 选项卡,接下来我们来看一下我们自定义的布局,还是拿一个 tab_home.xml 来做演示,代码如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="vertical">
<ImageView
android:layout_width="30dp"
android:layout_height="30dp"
android:src="@drawable/selector_tab_home" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="首页"
android:textColor="@color/selector_tab_text"
android:textSize="14sp" />
</LinearLayout>
可以看到在线性布局里面包含了一个 ImageView 和一个 TextView 这个 ImageView 就是我们地步导航栏 Tab 选项卡中的图标,TextView 就是相对应的文字,这里需要注意的就是需要用选择器 selector 的形式来设置图片和文字,代码如下:
图片选择器:selector_tab_home.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@mipmap/icon_home" android:state_selected="false"/>
<item android:drawable="@mipmap/icon_home_press" android:state_selected="true"/>
</selector>
文字选择器:selector_tab_text.xml,需要注意的是这里文字选择器只需要一个就可以了,因为不管那个 Tab 选项卡的文字颜色变化都是一样的
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="#999999" android:state_selected="false"/>
<item android:color="#e22b2b" android:state_selected="true"/>
</selector>
下的图片选择器请大家自行编写,写完如下:
到这里我们的底部导航栏就完成了,实现效果就是我们文章刚开始贴出的效果,到这里还有一个问题,那就是我们的底部导航栏是可以滑动的,但往往在实际项目中是不能滑动的,这个也好解决,就是我们需要去自定义一个 ViewPager,把滑动功能禁止掉就可以了,下面贴上自定义的 ViewPager,代码如下:
/**
* 不可滑动的ViewPager
* Created by qiudengjiao on 2017/1/20.
*/
public class NoScrollViewPager extends ViewPager {
public boolean canScroll = false;
public NoScrollViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
public NoScrollViewPager(Context context) {
super(context);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
return canScroll && super.onTouchEvent(ev);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return canScroll && super.onInterceptTouchEvent(ev);
}
@Override
public void setCurrentItem(int item) {
setCurrentItem(item, false);
}
}
这里我们自定义了自己的 NoScrollViewPager,继承 ViewPager 并从写了 onTouchEvent() 和 onInterceptTouchEvent() 方法,这两个方法的返回值都是 boolean 类型的,只需要改为 false,ViewPager 就不会消耗掉手指的滑动事件了,也就是说就不会再传递给上层View 去处理该滑动事件了,如果大家想让 ViewPager 从新滑动,只需要把 pulic boolean canSoroll = false 改为 ture 即可,如果大家想彻底的搞明白,那么就需要去搞清楚 View 的事件分发,这里就不详细介绍了
接下来就是在 activity_main.xml 布局文件中去引用我们自己定义的 ViewPager,代码如下:
<?xml version="1.0" encoding="utf-8"?>
<layout 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">
<LinearLayout
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.example.qiudengjiao.tablayout.MainActivity">
<com.example.qiudengjiao.tablayout.view.NoScrollViewPager
android:id="@+id/viewPager"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
</com.example.qiudengjiao.tablayout.view.NoScrollViewPager>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#d6d2d2" />
<android.support.design.widget.TabLayout
android:id="@+id/tabLayout"
android:layout_width="match_parent"
android:layout_height="60dp"
app:tabIndicatorHeight="0dp"
app:tabIndicatorColor="#33e0ff">
</android.support.design.widget.TabLayout>
</LinearLayout>
</layout>
到这里我们就把 ViewPager 左右滑动禁止了,底部导航栏也就算是彻底完成了!