Android 用 TabLayout + ViewPager + Fragment 实现顶部、底部导航栏

来源:互联网 发布:算法导论和数据结构 编辑:程序博客网 时间:2024/05/13 05:45

       这是我们 Material Design 系列的第四篇分享了,对 Material Design 还不是太清楚的可以先看前几篇博文大概的了解一下,今天我们主要通过实现顶部和底部导航栏的功能来了解又一个 Material Design 的控件 TabLayout 的使用,在移动应用开发中,由于屏膜大小的限制,使得顶部、底部 Tab 导航栏的使用非常的广泛,相信大家在实际开发过程中也是经常遇到,以前可以通过 RadioGroup,FragmentTabHost 以及后来的 Bottom Navigation 等方法来实现,现在我们可以使用 Design Support library 库的 TabLayout 来实现了


一、底部导航栏


我们先上效果图吧




      上面就是我们的效果图,接下来我们来通过代码一步步实现上面的效果,这里我使用了一点 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 左右滑动禁止了,底部导航栏也就算是彻底完成了


二、顶部导航栏


到这里大家想必顶部导航栏心里也就有数了,这里我们还是简单的来看一下,效果图如下:




接下来我们就上代码,这里我们首先创建顶部 Tab 对应的 Fragment,代码就不写了,和上面的是一样的,接下来我们来看 activity_top_layout.xml 布局文件:


<?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">    <LinearLayout        android:layout_width="match_parent"        android:layout_height="match_parent"        android:orientation="vertical">        <android.support.design.widget.TabLayout            android:id="@+id/top_tabLayout"            android:layout_width="match_parent"            android:layout_height="48dp"            app:tabSelectedTextColor="#a61849da"            app:tabTextColor="#767474"            app:tabIndicatorColor="#6cefe2"            app:tabIndicatorHeight="2dp">        </android.support.design.widget.TabLayout>        <View            android:layout_width="match_parent"            android:layout_height="1dp"            android:background="#d6d2d2" />        <android.support.v4.view.ViewPager            android:id="@+id/top_viewPager"            android:layout_width="match_parent"            android:layout_height="0dp"            android:layout_weight="1">        </android.support.v4.view.ViewPager>    </LinearLayout></layout>

是不是和我们上面的基本一样,这是上下调换了一下顺序,一样的内容就不重复来讲解了,不过这里简单说下 TabLayout 中的几个属性用法:

  • 导航栏背景颜色:android:background
  • 指示器颜色:app:tabIndicatorColor
  • 普通状态下文字颜色:app:tabTextColor
  • 选中时文字的颜色:app:tabSelectedTextColor
  • 是否可滑动( fixed:固定  scrollable:可滑动 ):app:tabMode

等等,属性就简单介绍这些,更多的项目中用到的大家可以自己尝试,这里就不一一全部列举了


接下来我们就来写适配器了,这里我们命名为 TopAdapter,相关代码如下:

/** * 顶部导航栏 Adapter * Created by qiudengjiao on 2017/5/8. */public class TopAdapter extends FragmentPagerAdapter {    //Fragment列表    private List<Fragment> mFragment;    //Tab名列表    private List<String> mTitle;    public TopAdapter(FragmentManager fm, List<Fragment> fragments, List<String> titles) {        super(fm);        this.mFragment = fragments;        this.mTitle = titles;    }    @Override    public Fragment getItem(int position) {        return mFragment.get(position);    }    @Override    public int getCount() {        return mTitle.size();    }    //此方法用来显示Tab上的名字    @Override    public CharSequence getPageTitle(int position) {        return mTitle.get(position);    }}

布局文件和 Adapter 都准备好了,我们该写 Activity了,来看 Activity 中的代码:

package com.example.qiudengjiao.tablayout.top;import android.databinding.DataBindingUtil;import android.os.Bundle;import android.support.v4.app.Fragment;import android.support.v4.app.FragmentPagerAdapter;import com.example.qiudengjiao.tablayout.Adapter.TopAdapter;import com.example.qiudengjiao.tablayout.Base.BaseActivity;import com.example.qiudengjiao.tablayout.R;import com.example.qiudengjiao.tablayout.databinding.ActivityTopLayoutBinding;import java.util.ArrayList;import java.util.List;/** * 顶部导航栏 * Created by qiudengjiao on 2017/5/8. */public class TopLayoutActivity extends BaseActivity {    private FragmentPagerAdapter mAdapter;    private List<Fragment> mFragment;    private List<String> mTitle;    private ActivityTopLayoutBinding binding;    @Override    protected void onCreate(Bundle savedInstanceState) {        //隐藏掉整个ActionBar        getSupportActionBar().hide();        super.onCreate(savedInstanceState);        binding = DataBindingUtil.setContentView(this, R.layout.activity_top_layout);        initTabFragment();    }    private void initTabFragment() {        //初始化Fragment        Fragment1 fragment1 = new Fragment1();        Fragment2 fragment2 = new Fragment2();        Fragment3 fragment3 = new Fragment3();        //将Fragment装进列表中        mFragment = new ArrayList<>();        mFragment.add(fragment1);        mFragment.add(fragment2);        mFragment.add(fragment3);        //将名称添加daoTab列表        mTitle = new ArrayList<>();        mTitle.add("Tab1");        mTitle.add("Tab2");        mTitle.add("Tab3");        //为TabLayout添加Tab名称        binding.topTabLayout.addTab(binding.topTabLayout.newTab().setText(mTitle.get(0)));        binding.topTabLayout.addTab(binding.topTabLayout.newTab().setText(mTitle.get(1)));        binding.topTabLayout.addTab(binding.topTabLayout.newTab().setText(mTitle.get(2)));        mAdapter = new TopAdapter(this.getSupportFragmentManager(), mFragment, mTitle);        //ViewPager加载Adapter        binding.topViewPager.setAdapter(mAdapter);        //TabLayout加载ViewPager        binding.topTabLayout.setupWithViewPager(binding.topViewPager);    }}

       由于这里我们在代码里都加了相关的注释,还是非常清楚的,就不在详细的去介绍代码,这样顶部导航栏也就实现了,具体到项目中有用到不同的样式也是完变不离其宗,大家自行处理就好了

到这里我们项目中常用的的底部和顶部导航栏就都实现了,是不是觉得比以前的实现方法简单了许多

源码Demo:点击打开链接

谢谢阅读,如有问题请指出


5 0
原创粉丝点击