CSDN博客不再更新,新内容请移步掘金:https://juejin.im/user/5886d699128fe1006c455fb6,有问题也请直接发邮件至guoxiaoxingse@163.com...

【Android应用开发技术:用户界面】底部Tab标签设计方案一:FragmentTabHost+Fragment

作者:郭孝星
微博:郭孝星的新浪微博
邮箱:allenwells@163.com
博客:http://blog.csdn.net/allenwells
Github:https://github.com/AllenWells

底部Tab标签设计有很多种方案,后续的文章会一一介绍,这里讨论的是
android.support.v4.app.FragmentTabHost + import android.support.v4.app.Fragment来实现的。

一 实现原理

这里我们先来讨论android.support.v4.app.FragmentTabHost这个类,关于Fragment的相关内容请参见:

【Android应用开发技术:应用组件】Fragment基本原理
【Android应用开发技术:应用组件】Fragment使用方法

android.support.v4.app.FragmentTabHost继承于TabHost,与TabHost相比,它具有更丰富的功能和可定制性,主要特点如下所示:

  • 支持将Object作为标签内容
  • 既可以在Activity中使用也可以在Fragment中使用
  • FragmentTabHost在绘制界面时使用的是Detach和Attach机制,所以每次界面切换都会进行界面重绘。

android.support.v4.app.FragmentTabHost的继承结构如下所示:

java.lang.Object
   ↳    android.view.View
       ↳    android.view.ViewGroup
           ↳    android.widget.FrameLayout
               ↳    android.widget.TabHost
                   ↳    android.support.v4.app.FragmentTabHost

1.1 FragmentTabHost的创建流程

现在我们来分析一下FragmentTabHost的创建流程:

1 在布局文件中定义android.support.v4.app.FragmentTabHost

2 在Java代码中设置FragmentTabHost

mFragmentTabHost = (FragmentTabHost) findViewById(R.id.tabhost);
mFragmentTabHost.setup(this, getSupportFragmentManager(), R.id.realtabcontent);

FragmentTabHost有三种setup()方法,如下所示:

  • void setup():这个方法以及过时,它会调用原始的TabHost设置,推荐使用后面两种方法。
  • void setup(Context context, FragmentManager manager)
  • void setup(Context context, FragmentManager manager, int containerId)

setup(Context context, FragmentManager manager, int containerId)有三个参数:

  • context:上下文
  • manager:是FragmentManager,注意在Activity中和在Fragment的获取方式是不同的,Activity中的获取方式为:getSupportFragmentManager(),Fragment中的获取方式为:getChildFragmentManager()。
  • containerId:真正内容的资源ID。

3 初始化FragmentTabHost标签内容

TabHost.TabSpec tabSpecHomePage = mFragmentTabHost.newTabSpec("主页").setIndicator(getTabItemView(R.drawable.main_tab_homepage_btn));
mFragmentTabHost.addTab(tabSpecHomePage,HomePageFragment.class, null);
mFragmentTabHost.getTabWidget().getChildAt(0).setBackgroundResource(R.drawable.selector_tab_background);

添加标签的方法是

addTab(TabHost.TabSpec tabSpec, Class<?> clss, Bundle args)

该方法有三个参数:

  • tabSpec:标签的Tag
  • clss:标签对应的类,点击可跳转。
  • args:其他参数。

    tabSpec的生成方法是

TabHost.TabSpec tabSpecHomePage = mFragmentTabHost.newTabSpec("主页")      .setIndicator(getTabItemView(R.drawable.main_tab_homepage_btn));

该方法会设置Tag和Indicator,Indicator有三种生成方式,这也是FragmentTabHost比TabHost灵活的原因之一,如下所示:

  • setIndicator(CharSequence label):系统提供布局
  • setIndicator(CharSequence label, Drawable icon):系统提供布局
  • setIndicator(View view):自定义View

1.2 FragmentTabHost的执行流程

我们先来看一个Fragment在FragmentTabHost中的切换流程:

在Fragment退出的时候会自动执行onSaveInstanceState()方法,把当前打开的Fragment的TabTag通过Parcelable的方式记录下来,然后当再次进入到该fragment的时候会自动执行OnRestoreInstanceState(Parcelable state)方法,把之前保存的状态恢复,如下所示:

这里写图片描述

我们再来看一个问题,如果当前没有Tab的时候,FragmentTabHost会怎么处理,我们来看一下setup()的源码,如下所示:

这里写图片描述

因为我们必须有一个Tag ID来save/restore Fragment的状态,如果这个ID没有被设置,则会设置ID为android.R.id.tabhost。

二 实现方案

2.1 FragmentTabHost for Activity

首先我们先来看一下效果图:

这里写图片描述

Java代码

import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentTabHost;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ImageView;
import android.widget.TabHost;

import com.allenwells.onlineclient.fragments.FindMoreFragment;
import com.allenwells.onlineclient.fragments.GoodsCategoryFragment;
import com.allenwells.onlineclient.fragments.HomePageFragment;
import com.allenwells.onlineclient.fragments.ShopingCartFragment;
import com.allenwells.onlineclient.fragments.UserCenterFragment;

public class MainActivity extends FragmentActivity
{

    private FragmentTabHost mFragmentTabHost = null;

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

        mFragmentTabHost = (FragmentTabHost) findViewById(R.id.tabhost);

        mFragmentTabHost.setup(this, getSupportFragmentManager(), R.id.realtabcontent);


        //设置底部FragmentTabHost菜单

        TabHost.TabSpec tabSpecHomePage = mFragmentTabHost.newTabSpec("主页")
                .setIndicator(getTabItemView(R.drawable.main_tab_homepage_btn));
        mFragmentTabHost.addTab(tabSpecHomePage,
                                HomePageFragment.class, null);
        mFragmentTabHost.getTabWidget().getChildAt(0).setBackgroundResource(
                R.drawable.selector_tab_background);


        TabHost.TabSpec tabSpecGoodsCategory = mFragmentTabHost.newTabSpec("商品")
                .setIndicator(getTabItemView(R.drawable.main_tab_goodscategory_btn));
        mFragmentTabHost.addTab(tabSpecGoodsCategory,
                                GoodsCategoryFragment.class, null);
        mFragmentTabHost.getTabWidget().getChildAt(1).setBackgroundResource(
                R.drawable.selector_tab_background);


        TabHost.TabSpec tabSpecFindMore = mFragmentTabHost.newTabSpec("发现")
                .setIndicator(getTabItemView(R.drawable.main_tab_findmore_btn));
        mFragmentTabHost.addTab(tabSpecFindMore,
                                FindMoreFragment.class, null);
        mFragmentTabHost.getTabWidget().getChildAt(2).setBackgroundResource(
                R.drawable.selector_tab_background);

        TabHost.TabSpec tabSpecShopingCart = mFragmentTabHost.newTabSpec("购物车")
                .setIndicator(getTabItemView(R.drawable.main_tab_shoppingcart_btn));
        mFragmentTabHost.addTab(tabSpecShopingCart,
                                ShopingCartFragment.class, null);
        mFragmentTabHost.getTabWidget().getChildAt(3).setBackgroundResource(
                R.drawable.selector_tab_background);

        TabHost.TabSpec tabSpecUserCenter = mFragmentTabHost.newTabSpec("我的")
                .setIndicator(getTabItemView(R.drawable.main_tab_usercenter_btn));
        mFragmentTabHost.addTab(tabSpecUserCenter,
                                UserCenterFragment.class, null);
        mFragmentTabHost.getTabWidget().getChildAt(4).setBackgroundResource(
                R.drawable.selector_tab_background);

    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu)
    {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item)
    {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings)
        {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }


    private View getTabItemView(int tabItemIconRes)
    {
        //获取布局实例
        View tabItemView = View.inflate(MainActivity.this, R.layout.tab_item_view, null);

        ImageView tabItemIcon = (ImageView) tabItemView.findViewById(R.id.tab_item_icon);
        tabItemIcon.setImageResource(tabItemIconRes);

        return tabItemView;
    }
}

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">

    <!--主页面内容 -->

    <FrameLayout
        android:id="@+id/realtabcontent"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"/>

    <!--底部菜单 -->

    <android.support.v4.app.FragmentTabHost
        android:id="@+id/tabhost"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@mipmap/maintab_toolbar_bg">


        <FrameLayout
            android:id="@+id/tabcontent"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:layout_weight="1"/>

    </android.support.v4.app.FragmentTabHost>


</LinearLayout>

2.2 FragmentTabHost for Fragment

Java代码

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTabHost;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class FragmentTabsFragmentSupport extends Fragment {
    private FragmentTabHost mTabHost;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        mTabHost = new FragmentTabHost(getActivity());
        mTabHost.setup(getActivity(), getChildFragmentManager(), R.id.fragment1);

        mTabHost.addTab(mTabHost.newTabSpec("simple").setIndicator("Simple"),
                FragmentStackSupport.CountingFr  agment.class, null);
        mTabHost.addTab(mTabHost.newTabSpec("contacts").setIndicator("Contacts"),
                LoaderCursorSupport.CursorLoaderListFragment.class, null);
        mTabHost.addTab(mTabHost.newTabSpec("custom").setIndicator("Custom"),
                LoaderCustomSupport.AppListFragment.class, null);
        mTabHost.addTab(mTabHost.newTabSpec("throttle").setIndicator("Throttle"),
                LoaderThrottleSupport.ThrottledLoaderListFragment.class, null);

        return mTabHost;
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        mTabHost = null;
    }
}

三 开发问题

3.1 java.lang.IndexOutOfBoundsException: Invalid index 0, size is 0

这里写图片描述

阅读更多
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

不良信息举报

【Android应用开发技术:用户界面】底部Tab标签设计方案一:FragmentTabHost+Fragment

最多只允许输入30个字

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭