TabHost+ViewPager实现底部导航效果

一:摘要

这篇文章主要为大家介绍了使用ViewPager+TabHost实现底部导航效果,具有一定的参考价值,感兴趣的小伙伴们可以参考一下。

下面我们先来看一下要实现的效果吧!

二:具体实现过程

1.新建一个project,把主布局改为TabHost,id为tab_host

2.主布局添加一个相对布局,相对布局里加一个id为view_pager的 ViewPager布局。

加一个id为@android:id/tabs的TabWidget(指明高度)并将其放在最底部。

由于TabWidget要求必须要有一个id为@android:id/tabcontent的FrameLayout,所以得再加一个FrameLayout。

为了看出界限,我们还要添加一条id为tab_divider线,具体可用View实现。

从上往下,对组件进行排布。

ViewPager和FrameLayout的layout_above="@+id/tab_divider",

View的layout_above="@android:id/tabs",

TabWidget的layout_alignParentBottom="true"。

最后,让FrameLayout的visibility="gone"。

3.创建一个TestFragment类继承Fragment并复写onCreate,onCreateView方法。

4.创建fragment_test.xml文件。里面添加一个id为text_view的TextView。

5.在TestFragment的onCreateView方法中,删除原有代码,通过布局反向生成view对象并加以返回。

部分代码:

public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {

   //删除原有代码,通过布局反向生成view对象并加以返回。
   View view=inflater.inflate(R.layout.fragment_test,null);
   TextView textView=view.findViewById(R.id.text_view);
   return view;
}

6.因为要给TestFragment传参数(标题),所以得写一个静态方法来实现newInstance。

部分代码:

public static TestFragment newInstance(String title){
   TestFragment testFragment=new TestFragment();
   //建立一个bundle
   Bundle bundle=new Bundle();
   bundle.putString(TITLE,title);
   testFragment.setArguments(bundle);
   return testFragment;

}
//该方法里设置一个bundle用来testFragment.setArguments(bundle);可以把title提取为常量TITLE。

7.在TestFragment的onCreate的方法里,我们要判断是否接受了数据,如果接受了数据,则把数据给提取出来。这时,我们可以把接受到的字符提取为一个叫mTitle的全局变量。

部分代码:

public void onCreate(@Nullable Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   if (getArguments()!=null){
       mTitle = getArguments().getString(TITLE);
   }
}

8.因为有了传参,所以在TestFragment的onCreateView的方法里,我们要把对应布局的TextView找到,设置TextView的文本为传入的参数mTitle。

部分代码:

public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {

   //删除原有代码,通过布局反向生成view对象并加以返回。
   View view=inflater.inflate(R.layout.fragment_test,null);
   TextView textView=view.findViewById(R.id.text_view);
   textView.setText(mTitle);
   return view;
}

9.在MainActivity把ViewPager引用出来,记为mViewPager。

10.我们要用三个fragment组成的viewpager。因此,先创建final Fragment [] fragments数组。注意分号

部分代码:

final Fragment[] fragments=new Fragment[]{
       TestFragment.newInstance("home"),
       TestFragment.newInstance("message"),
       TestFragment.newInstance("me")
};//注意分号

11.给mViewPager设置适配器。

部分代码:

mViewPager.setAdapter(new FragmentPagerAdapter(getSupportFragmentManager()) {……};

/*
要重写两个方法,getItem和getCount。

getItem中,return fragments[position];

getCount中,return fragments.length
*/

至此,上面部分已经完成。

我们来看看效果。

12.我们建立一个main_tab_layout.xml文件(相对布局),id为tab_bg。

里面加一个id为main_tab_icon的ImageView,id为main_tab_text的TextView。设置好控件的外边距。

如果以后有需要在右上角做一个小红点的话,可以把这两个放在一个id为main_contain的LinearLayout里,小红点在LinearLayout就行。

13.main_tab_layout.xml里的文本,点击和没点击会显示不同的颜色,所以我们可以用textColor="@color/color_main_tab_text"来实现。

新建color_main_tab_text布局,在其中的选择器selector里,添加item标签

(注意顺序,显示特殊情况下的选择,最后才是默认情况下的选择,否则会失效)

    <item android:state_selected="true" android:color="#4dd048"/>

    <item android:state_pressed="true" android:color="#4dd048"/>

    <item android:color="#cccccc"/>

14.同理,我们可以在drawable里也建立3个选择器。可以直接把color_main_tab_text布局文件复制到drawable后改名改参数即可。

15.复制准备好的图片到drawable文件夹。对布局文件的参数进行修改。(注意顺序)

    <item android:state_selected="true" android:drawable="@drawable/tabbar_home_pressed"/>

    <item android:state_pressed="true" android:drawable="@drawable/tabbar_home_pressed"/>

    <item android:drawable="@drawable/tabbar_home"/>

16.初始化总布局。把TabHost找出来并提取为全局变量mTabHost。调用mTabHost.setup();

17.三个Tab做处理。三个Tab做处理,用数组来准备。

int [] titlesIDs={R.string.home,R.string.message,R.string.me};//利用alt+enter为其设值。

        int [] drawableIDs={

                R.drawable.color_main_tab_icon_home,

                R.drawable.color_main_tab_icon_message,

                R.drawable.color_main_tab_icon_me

        };

18.利用for循环把数据填充到布局里。

先把视图提取出来,给mIcon设置图片,给title设置文本,给tab设置背景颜色。

调用mTabHost.addTab方法。因为该方法的setContent()要设置视图内容,我们可以通过让该类实现一个TabContentFactory接口来解决。

部分代码:

//数据填到视图里
for (int index = 0; index < titlesIDs.length; index++) {
   //先把视图提取出来
   View view=getLayoutInflater().inflate(R.layout.main_tab_layout,null,false);

   ImageView mIcon=view.findViewById(R.id.main_tab_icon);
   TextView mTitle=view.findViewById(R.id.main_tab_text);
   View tab=view.findViewById(R.id.tab_bg);

   mIcon.setImageResource(drawableIDs[index]);
   mTitle.setText(titlesIDs[index]);

   //tab背景颜色,可以创建一个value
   tab.setBackgroundColor(getResources().getColor(R.color.myColor));

   mTabHost.addTab(
           mTabHost.newTabSpec(getString(titlesIDs[index]))
           .setIndicator(view)//设置分割
            //因为实现了TabContentFactory接口,所以我们就可以用this
            .setContent(this)    );
}

19.MainActivity实现TabContentFactory接口,重写方法createTabContent。

在createTabContent中,直接创建一个view并返回就行。

因为下面的要对应上上面的内容,而我们的内容是用ViewPager做的,虽然我们不要这个内容,但要求我们必须设置,所以直接返回一个view。我们可以把它隐藏掉。

部分代码:

public View createTabContent(String tag) {
   /*因为下面的要对应上上面的内容,而我们的内容是用ViewPager做的,
   虽然我们不要这个内容,但要求我们必须设置,所以直接返回一个view。我们可以把它隐藏掉。
    */
   View view=new View(this);
   view.setMinimumWidth(0);
   view.setMinimumHeight(0);
   return view;
}

至此,界面基本完成。

20.我们看到底下有分割线,

在activity_main的TabWidget里,可以通过调用

android:showDividers="none"去掉分割线

21.最后,添加互动。

给mViewPager添加监听器。

部分代码:

mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {……}

//在方法onPageSelected中先判断mTabHost是否为空,
//不为空则调用mTabHost.setCurrentTab(position);方法。具体如下:

public void onPageSelected(int position) {
   if(mTabHost!=null){
       mTabHost.setCurrentTab(position);
   }
}

22.给mTabHost添加监听器。

部分代码:

/*
mTabHost.setOnTabChangedListener(new TabHost.OnTabChangeListener() {……}

onTabChanged方法中,先判断mTabHost && mViewPager是否为空。

如果两个都不为空的话,则调用mViewPager.setCurrentItem(mTabHost.getCurrentTab());具体如下:
*/
mTabHost.setOnTabChangedListener(new TabHost.OnTabChangeListener() {
   @Override
   public void onTabChanged(String tabId) {
       if(mViewPager!=null&&mTabHost!=null){
           //写法1
           //mViewPager.setCurrentItem(mTabHost.getCurrentTab());
           //写法2
           int position = mTabHost.getCurrentTab();
           mViewPager.setCurrentItem(position);
       }
   }
});

至此,全部功能已经完成。

下面提供全部代码,欢迎小伙伴们交流学习。

MainActivity:

package com.wxdgut.navigation;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentPagerAdapter;
import androidx.viewpager.widget.ViewPager;

import android.os.Bundle;
import android.view.View;
import android.widget.ImageView;
import android.widget.TabHost;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity implements TabHost.TabContentFactory {

   private TabHost mTabHost;

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

       mTabHost = findViewById(R.id.tab_host);
       mTabHost.setup();
       //三个Tab做处理,用数组来准备
       int [] titlesIDs= new int[]{R.string.home, R.string.message, R.string.me};
       int [] drawableIDs={
               R.drawable.main_tab_icon_home,
               R.drawable.main_tab_icon_message,
               R.drawable.main_tab_icon_me
       };

       //数据塞到视图里
       for (int index = 0; index < titlesIDs.length; index++) {
           //先把视图提取出来

            View view=

            getLayoutInflater().inflate(R.layout.main_tab_layout,null,false);

           ImageView mIcon=view.findViewById(R.id.main_tab_icon);
           TextView mTitle=view.findViewById(R.id.main_tab_text);
           View tab=view.findViewById(R.id.tab_bg);

           mIcon.setImageResource(drawableIDs[index]);
           mTitle.setText(titlesIDs[index]);

           //tab背景颜色,可以创建一个value
           tab.setBackgroundColor(getResources().getColor(R.color.myColor));

           mTabHost.addTab(
                   mTabHost.newTabSpec(getString(titlesIDs[index]))
                   .setIndicator(view)//设置分割
                   //因为实现了该接口,所以我们就可以用this

                    .setContent(this)
           );

       }

       final Fragment[] fragments=new Fragment[]{
               TestFragment.newInstance("home"),
               TestFragment.newInstance("message"),
               TestFragment.newInstance("me")
       };//注意分号

       final ViewPager mViewPager= findViewById(R.id.view_pager);
       mViewPager.setAdapter(new FragmentPagerAdapter(getSupportFragmentManager()) {
           @NonNull
           @Override
           public Fragment getItem(int position) {
               return fragments[position];
           }

           @Override
           public int getCount() {
               return fragments.length;
           }
       });

       //做互动,将tab与viewpager关联
       mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
           @Override
           public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

           }

           @Override
           public void onPageSelected(int position) {
               if(mTabHost!=null){
                   mTabHost.setCurrentTab(position);
               }
           }

           @Override
           public void onPageScrollStateChanged(int state) {

           }
       });
       mTabHost.setOnTabChangedListener(new TabHost.OnTabChangeListener() {
           @Override
           public void onTabChanged(String tabId) {
               if(mViewPager!=null&&mTabHost!=null){
                   //写法1
                   //mViewPager.setCurrentItem(mTabHost.getCurrentTab());
                   //写法2
                   int position = mTabHost.getCurrentTab();
                   mViewPager.setCurrentItem(position);
               }
           }
       });
   }

   @Override
   public View createTabContent(String tag) {
       /*因为下面的要对应上上面的内容,而我们的内容是用ViewPager做的,
       虽然我们不要这个内容,但要求我们必须设置,所以直接返回一个view。我们可以把它隐藏掉。
        */
       View view=new View(this);
       view.setMinimumWidth(0);
       view.setMinimumHeight(0);
       return view;
   }
}

TestFragment:

package com.wxdgut.navigation;

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;

public class TestFragment extends Fragment {

   public static final String TITLE = "title";
   private String mTitle;

   public static TestFragment newInstance(String title){
       TestFragment testFragment=new TestFragment();
       //建立一个bundle
       Bundle bundle=new Bundle();
       bundle.putString(TITLE,title);
       testFragment.setArguments(bundle);
       return testFragment;

   }
   @Override
   public void onCreate(@Nullable Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       if (getArguments()!=null){
           mTitle = getArguments().getString(TITLE);
       }
   }

   @Nullable
   @Override
   public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
       //删除原有代码,通过布局反向生成view对象并加以返回。非activity中就用inflater
       View view=inflater.inflate(R.layout.fragment_test,null);
       TextView textView=view.findViewById(R.id.text_view);
       textView.setText(mTitle);
       return view;
   }
}

activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<TabHost 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:id="@+id/tab_host"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   tools:context=".MainActivity"
   android:orientation="vertical">

   <RelativeLayout
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       android:orientation="vertical">
       <androidx.viewpager.widget.ViewPager
           android:id="@+id/view_pager"
           android:layout_width="match_parent"
           android:layout_height="match_parent"
           android:layout_above="@+id/tab_divider">

       </androidx.viewpager.widget.ViewPager>

       <FrameLayout
           android:id="@android:id/tabcontent"
           android:layout_width="match_parent"
           android:layout_height="match_parent"
           android:layout_above="@+id/tab_divider"
           android:visibility="gone"/>

       <View
           android:id="@+id/tab_divider"
           android:layout_width="match_parent"
           android:layout_height="5dp"
           android:background="#dfdfdf"
           android:layout_above="@android:id/tabs"/>

       <TabWidget
           android:id="@android:id/tabs"
           android:layout_width="match_parent"
           android:layout_height="80dp"
           android:showDividers="none"
           android:layout_alignParentBottom="true"/>

   </RelativeLayout>

</TabHost>

fragment_test.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"
   android:orientation="vertical"
   android:gravity="center">

   <TextView
       android:id="@+id/text_view"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="wxdgut.com"
       android:textSize="25sp" />

</LinearLayout>

main_tab_layout.xml:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:orientation="vertical"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:id="@+id/tab_bg">

   <LinearLayout
       android:id="@+id/main_content"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:orientation="vertical"
       android:gravity="center"
       android:layout_centerInParent="true">
       <ImageView
           android:id="@+id/main_tab_icon"
           android:layout_width="30dp"
           android:layout_height="30dp"
           android:src="@mipmap/ic_launcher"
           android:layout_marginTop="4dp"/>
       <TextView
           android:id="@+id/main_tab_text"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:text="@string/app_name"
           android:textColor="@color/color_main_tab_text"
           android:layout_marginTop="5dp"/>
   </LinearLayout>
   //以后可以做个小红点
   <ImageView
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:visibility="gone"/>

</RelativeLayout>

color_main_tab_text.xml:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
   //注意顺序,显示特殊情况下的选择,最后才是默认情况下的选择,否则会失效
   <item android:state_selected="true" android:color="#4dd048"/>
   <item android:state_pressed="true" android:color="#4dd048"/>
   <item android:color="#cccccc"/>
</selector>

main_tab_icon_home.xml:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
   //注意顺序,显示特殊情况下的选择,最后才是默认情况下的选择,否则会失效
   <item android:drawable="@drawable/tabbar_home_pressed" android:state_selected="true" />
   <item android:drawable="@drawable/tabbar_home_pressed" android:state_pressed="true" />
   <item android:drawable="@drawable/tabbar_home" />
</selector>

main_tab_icon_me.xml和main_tab_icon_message.xml:同上面的类似。

接下来是图片素材,需要的话就保存一下吧。

1

tabbar_home_pressed

2

tabbar_home

3

tabbar_msg_pressed

4

tabbar_msg

5

tabbar_my_pressed

6

tabbar_my

欢迎小伙伴们批评指正!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。