您的位置:首页 > 移动开发 > Android开发

Android底部导航栏—FragmentTabHost+Fragment

2017-02-07 11:01 447 查看

介绍



Android开发中使用底部菜单栏的频次非常高,主要的实现手段有以下:

- TabWidget

- 隐藏TabWidget,使用RadioGroup和RadioButton

- FragmentTabHost

- 5.0以后的TabLayout

- 最近推出的 Bottom navigation

案例1:简单使用

简单使用 FragmentTabHost

1 在布局文件使用 FragmentTabHost,并提供 Fragment 的容器

2 在 Activity 里查找 FragmentTabHost,并使用 setUp 方法关联到 Fragment 的容器

3 向 FragmentTabHost 里添加 Tab,注意 Tab 需要设置 indicate 文本

4 使用newInstance创建fragment

布局文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="cn.itcast.demo.MainActivity">

<android.support.v7.widget.Toolbar

android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#f0f">

<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:layout_marginRight="10dp"
android:src="@drawable/actionbar_search_icon" />
</android.support.v7.widget.Toolbar>

<android.support.v4.widget.DrawerLayout
android:id="@+id/drawerlayout"
android:layout_width="match_parent"
android:layout_height="match_parent">

<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#f00"
android:orientation="vertical">

<FrameLayout
android:id="@+id/container"
android:layout_width="match_parent"
//特殊之处,高设置0dp  weight设置1
android:layout_height="0dp"
android:layout_weight="1"></FrameLayout>

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

</LinearLayout>

<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="start"
android:background="#00f"></LinearLayout>
</android.support.v4.widget.DrawerLayout>

</LinearLayout>


MainActivity

public class MainActivity extends AppCompatActivity {

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

Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setTitle("heihehie");
getSupportActionBar().setDisplayHomeAsUpEnabled(true);

DrawerLayout drawerLayout = (DrawerLayout) findViewById(R.id.drawerlayout);
ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(this,drawerLayout,toolbar,R.string.app_name,R.string.app_name);
drawerLayout.addDrawerListener(toggle);// 面板展开的监听设置给 Toogle
toggle.syncState();// 初始化 toogle 的绘制内容

// 设置底部栏
FragmentTabHost tabHost = (FragmentTabHost) findViewById(R.id.tabhost);
tabHost.setup(this,getSupportFragmentManager(),R.id.container);// 初始化 tabhost

// 添加一个 tab
TabHost.TabSpec tab1 = tabHost.newTabSpec("news");// 创建tab对象
tab1.setIndicator("资讯");// 设置tab 内容

Bundle arg = new Bundle();
arg.putString("content","资讯界面");

tabHost.addTab(tab1,TestFragment.class,arg);// 将tab添加到底部栏

// 再添加一个 Tab
TabHost.TabSpec tab2 = tabHost.newTabSpec("tweet");
tab2.setIndicator("动弹");
Bundle arg2 = new Bundle();
arg2.putString("content","动弹界面");
tabHost.addTab(tab2,TestFragment.class,arg2);
}

@Override
// 为 Activity 生成菜单,ToolBar已经被设置为标题栏,这个菜单会自动显示到 ToolBar 上
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_main,menu);

return super.onCreateOptionsMenu(menu);
}

@Override
// 目录菜单的点击响应
public boolean onOptionsItemSelected(MenuItem item) {

switch (item.getItemId()){
case R.id.menu_search:
Toast.makeText(this, "跳转到搜索界面", Toast.LENGTH_SHORT).show();
break;
}

return super.onOptionsItemSelected(item);
}
}


TestFragment

public class TestFragment extends Fragment {

public static TestFragment newInstance(String content){
Bundle arg = getBundle(content);

TestFragment fragment = new TestFragment();
fragment.setArguments(arg);
return fragment;
}

@NonNull
public static Bundle getBundle(String content) {
Bundle arg = new Bundle();
arg.putString("content",content);
return arg;
}

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
Bundle arg = getArguments();
String content = arg.getString("content");

TextView textView = new TextView(getActivity());
textView.setText(content);
return textView;
}
}


案例 2

1.调用FragmentTabHost的setup方法;

2.创建TabHost.TabSpec对象,每一个对象对应一个item;

3.调用FragmentTabHost的addTab方法,把TabHost.TabSpec对象添加进来。



布局文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >

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

<android.support.v4.app.FragmentTabHost
android:id="@android:id/tabhost"
android:layout_width="fill_parent"
android:layout_height="wrap_content" >

<FrameLayout
android:id="@android:id/tabcontent"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="0" />
</android.support.v4.app.FragmentTabHost>

</LinearLayout>


这个文件中需要注意的是,FragmentTabHost跟它里面的FrameLayout中的id都是引入的id,而真正的id写在FragmentTabHost外面的FrameLayout中

tab_item代码如下:

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

<ImageView
android:id="@+id/imageview"
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
</ImageView>

<TextView
android:id="@+id/textview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="2dp"
android:textColor="@drawable/selector_tab_text" >
</TextView>

</LinearLayout>


我们定义了五个子item,每个item对应一张图片跟一个标题,相信上面的布局大家都能看懂。每个item对应一个fragment,那么接下来我们把fragment的代码写出来,这里因为f五个ragment的代码差不多,我只给出HomeFragment的代码。fragment_home.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" >

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="首页Fragment"
android:textSize="20sp" />

</LinearLayout></span>


HomeFragment.Java代码如下所示:

public class HomeFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_home, container, false);
}
}


HomeFragment.java,这个里面不用讲吧,就是把fragment_home.xml文件中的内容填充到fragment中。接下来,我们还要继续写状态选择器selector,Eclipse在res下新建一个文件夹drawable,里面创建5个图片状态选择器跟一个文字状态选择器,这里图片状态选择器我只贴出一个,selector_icon_home.xml代码如下所示:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">

<!-- Non focused states -->
<item android:drawable="@drawable/icon_home" android:state_focused="false" android:state_pressed="false" android:state_selected="false"/>
<item android:drawable="@drawable/icon_home_press" android:state_focused="false" android:state_pressed="false" android:state_selected="true"/>
<!-- Focused states -->
<item android:drawable="@drawable/icon_home_press" android:state_focused="true" android:state_pressed="false" android:state_selected="false"/>
<item android:drawable="@drawable/icon_home_press" android:state_focused="true" android:state_pressed="false" android:state_selected="true"/>
<!-- Pressed -->
<item android:drawable="@drawable/icon_home_press" android:state_pressed="true" android:state_selected="true"/>
<item android:drawable="@drawable/icon_home_press" android:state_pressed="true"/>

</selector>


这里简单介绍一下,android:state_focused=”false”是没有聚焦状态,相反若为true,则为获得焦点状态。

android:state_pressed=”false”是没有按下状态,相反若为true,则为按下状态。

android;state_selected=”false”是没有被选择状态,相反则被选择。

最后,到了最重要的MainActivity,代码如下:

public class MainActivity extends FragmentActivity {

/**
* 定义FragmentTabHost对象
*/
private FragmentTabHost mTabHost;

/**
* 定义一个布局填充器对象
*/
private LayoutInflater mInflater;

/**
* 定义一个ArrayList来存放Tab
*/
private List<Tab> mTabs = new ArrayList<Tab>(5);

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

initTab();
}

/**
* 初始化Tab
*/
private void initTab() {
Tab tab_home = new Tab(HomeFragment.class, R.string.home, R.drawable.selector_icon_home);
Tab tab_hot = new Tab(HotFragment.class, R.string.hot, R.drawable.selector_icon_hot);
Tab tab_category = new Tab(CategoryFragment.class, R.string.catagory, R.drawable.selector_icon_category);
Tab tab_cart = new Tab(CartFragment.class, R.string.cart, R.drawable.selector_icon_cart);
Tab tab_mine = new Tab(MineFragment.class, R.string.mine, R.drawable.selector_icon_mine);

mTabs.add(tab_home);
mTabs.add(tab_hot);
mTabs.add(tab_category);
mTabs.add(tab_cart);
mTabs.add(tab_mine);

mInflater = LayoutInflater.from(this);
mTabHost = (FragmentTabHost) this.findViewById(android.R.id.tabhost);
mTabHost.setup(this, getSupportFragmentManager(), R.id.realtabcontent);

for (Tab tab : mTabs) {

TabHost.TabSpec tabSpec = mTabHost.newTabSpec(getString(tab.getTitle()));

tabSpec.setIndicator(buildIndicator(tab));

mTabHost.addTab(tabSpec, tab.getFragment(), null);

}
}

/**
* 给Tab设置图标跟文字
*
* @param tab
* @return
*/
private View buildIndicator(Tab tab) {

View view = mInflater.inflate(R.layout.tab_item, null);
ImageView img = (ImageView) view.findViewById(R.id.imageview);
TextView text = (TextView) view.findViewById(R.id.textview);

img.setBackgroundResource(tab.getIcon());
text.setText(tab.getTitle());

return view;
}

}


看到MainActivity中的Tab了吗?我们创建一个package:com.example.fragmenttabhostdemo.bean,然后在该目录下新建一个java文件,Tab代码如下所示:

public class Tab {

private int title;

private int icon;

private Class fragment;

public Tab(Class fragment, int title, int icon) {
super();
this.title = title;
this.icon = icon;
this.fragment = fragment;
}

public int getTitle() {
return title;
}

public void setTitle(int title) {
this.title = title;
}

public int getIcon() {
return icon;
}

public void setIcon(int icon) {
this.icon = icon;
}

public Class getFragment() {
return fragment;
}

public void setFragment(Class fragment) {
this.fragment = fragment;
}

}


案例3:彷新浪微博底部栏



1、主tab布局界面,main_tab_layout:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >

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

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

<FrameLayout
android:id="@android:id/tabcontent"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="0" />
</android.support.v4.app.FragmentTabHost>

</LinearLayout>


2、Tab按钮选项布局,tab_item_view.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:id="@+id/imageview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:focusable="false"
android:padding="3dp"
android:src="@drawable/tab_home_btn">
</ImageView>

<TextView
android:id="@+id/textview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="首页"
android:textSize="10sp"
android:textColor="#ffffff">
</TextView>

</LinearLayout>


3、fragment布局界面,这里只列出一个,fragment_1.xml:

<span style="font-size:12px;"><?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >

<ImageView
android:id="@+id/imageview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:scaleType="fitCenter"
android:src="@drawable/xianjian01" >
</ImageView>

</LinearLayout></span>


4、Tab选项的自定义按钮资源文件,列出其中一个按钮,tab_home_btn:

<span style="font-size:12px;"><?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">

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

</selector></span>


5、Tab选项按钮背景资源文件,selector_tab_background.xml:

<span style="font-size:12px;"><?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">

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

</selector></span>


6、主Activity类,MainTabActivity.Java:

public class MainTabActivity extends FragmentActivity{
//定义FragmentTabHost对象
private FragmentTabHost mTabHost;

//定义一个布局
private LayoutInflater layoutInflater;

//定义数组来存放Fragment界面
private Class fragmentArray[] = {FragmentPage1.class,FragmentPage2.class,FragmentPage3.class,FragmentPage4.class,FragmentPage5.class};

//定义数组来存放按钮图片
private int mImageViewArray[] = {R.drawable.tab_home_btn,R.drawable.tab_message_btn,R.drawable.tab_selfinfo_btn,
R.drawable.tab_square_btn,R.drawable.tab_more_btn};

//Tab选项卡的文字
private String mTextviewArray[] = {"首页", "消息", "好友", "广场", "更多"};

public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_tab_layout);

initView();
}

/**
* 初始化组件
*/
private void initView(){
//实例化布局对象
layoutInflater = LayoutInflater.from(this);

//实例化TabHost对象,得到TabHost
mTabHost = (FragmentTabHost)findViewById(android.R.id.tabhost);
mTabHost.setup(this, getSupportFragmentManager(), R.id.realtabcontent);

//得到fragment的个数
int count = fragmentArray.length;

for(int i = 0; i < count; i++){
//为每一个Tab按钮设置图标、文字和内容
TabSpec tabSpec = mTabHost.newTabSpec(mTextviewArray[i]).setIndicator(getTabItemView(i));
//将Tab按钮添加进Tab选项卡中
mTabHost.addTab(tabSpec, fragmentArray[i], null);
//设置Tab按钮的背景
mTabHost.getTabWidget().getChildAt(i).setBackgroundResource(R.drawable.selector_tab_background);
}
}

/**
* 给Tab按钮设置图标和文字
*/
private View getTabItemView(int index){
View view = layoutInflater.inflate(R.layout.tab_item_view, null);

ImageView imageView = (ImageView) view.findViewById(R.id.imageview);
imageView.setImageResource(mImageViewArray[index]);

TextView textView = (TextView) view.findViewById(R.id.textview);
textView.setText(mTextviewArray[index]);

return view;
}
}


7、Fragment页面,FragmentPage1.java:

public class FragmentPage1 extends Fragment{

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_1, null);
}
}


案例4:顶部栏导航



主要包括:

(1) 自定义Tab的图片资源和去掉分割线.

(2) 缓存Fragment的布局, 减少填充.

在切换页面时, 控件会调用Fragment的onCreateView, 重新创建页面.

通过缓存页面, 可以增强性能.

1. 布局

FragmentTabHost是原生控件, 并不需要添加其他的maven库.

包括标签组Tabs和页面TabContainer, 标签组固定大小, 页面填充.

<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.app.FragmentTabHost
android:id="@android:id/tabhost"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">

<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">

<TabWidget
android:id="@android:id/tabs"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:layout_gravity="bottom"/>

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

</LinearLayout>

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


注意控件的id必须是Android提供的标准id, 即”@android:id”.

Fragment布局, 包含一行文字提示.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">

<TextView
android:id="@+id/tab_tv_text"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:textSize="40sp"
tools:text="Test"/>

</LinearLayout>


Tab布局, 包含一个图片控件.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">

<ImageView
android:id="@+id/tab_iv_image"
android:padding="12dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@null"
tools:src="@drawable/tab_assistant"/>

</LinearLayout>


2. 主页

setup设置页面组合, 却掉分割线etDividerDrawable(null), 设置Tab.

使用自定义的图片资源, newTabSpec设置Fragment的Tag标签.

/**
* 主页, 切换Tab标签显示不同页面.
*
* @author C.L.Wang
*/
public class MainActivity extends AppCompatActivity {

@Bind(android.R.id.tabhost) FragmentTabHost mTabHost;

// 图片
@DrawableRes
private int mImages[] = {
R.drawable.tab_counter,
R.drawable.tab_assistant,
R.drawable.tab_contest,
R.drawable.tab_center
};

// 标题
private String mFragmentTags[] = {
"counter",
"assistant",
"contest",
"center"
};

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

ButterKnife.bind(this);

mTabHost.setup(this, getSupportFragmentManager(), android.R.id.tabcontent);
mTabHost.getTabWidget().setDividerDrawable(null); // 去掉分割线

for (int i = 0; i < mImages.length; i++) {
// Tab按钮添加文字和图片
TabHost.TabSpec tabSpec = mTabHost.newTabSpec(mFragmentTags[i]).setIndicator(getImageView(i));
// 添加Fragment
mTabHost.addTab(tabSpec, FragmentTab.class, null);
// 设置Tab按钮的背景
mTabHost.getTabWidget().getChildAt(i).setBackgroundResource(R.color.pedo_actionbar_bkg);
}
}

// 获得图片资源
private View getImageView(int index) {
@SuppressLint("InflateParams")
View view = getLayoutInflater().inflate(R.layout.view_tab_indicator, null);
ImageView imageView = (ImageView) view.findViewById(R.id.tab_iv_image);
imageView.setImageResource(mImages[index]);
return view;
}
}


3. 切换页

显示不同Tag标签. 缓存页面, 注意关联前, 删除父控件关联. 页面显示Tag信息.

public class FragmentTab extends Fragment {

@Bind(R.id.tab_tv_text) TextView mTvText;

private View mViewContent; // 缓存视图内容

@Nullable @Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
if (mViewContent == null) {
mViewContent = inflater.inflate(R.layout.fragment_tab, container, false);
}

// 缓存View判断是否含有parent, 如果有需要从parent删除, 否则发生已有parent的错误.
ViewGroup parent = (ViewGroup) mViewContent.getParent();
if (parent != null) {
parent.removeView(mViewContent);
}

ButterKnife.bind(this, mViewContent);
return mViewContent;
}

@Override public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
// 显示Fragment的Tag信息
mTvText.setText(String.valueOf("Page: " + getTag()));
}

@Override public void onDestroyView() {
super.onDestroyView();
ButterKnife.unbind(this);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐