Android使用Fragment实现TabHost效果
现在Fragment的应用真的是越来越广泛了,之前Android在3.0版本加入Fragment的时候,主要是为了解决Android Pad屏幕比较大,空间不能充分利用的问题,但现在即使只是在手机上,也有很多的场景可以运用到Fragment了,今天我们就来学习其中一个特别棒的应用技巧。很多手机应用都会有一个非常类似的功能,即屏幕的下方显示一行Tab标签选项,点击不同的标签就可以切换到不同的界面,如以下几个应用所示:

直接上实例:
新建一个项目,起名就叫FragmentDemo,这里我使用的是4.0的API。
下面开始编程工作,这里我们首先需要去编写一个类似于QQ的主界面,当然只会去编写界面最下方的TabHost部分,而不会编写上面的内容界面部分,因为内容界面是应该写在Fragment的布局里的。
打开或新建activity_main.xml作为程序的主布局文件,在里面加入如下代码:
1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
2 android:layout_width="match_parent"
3 android:layout_height="match_parent"
4 android:orientation="vertical" >
5
6 <FrameLayout
7 android:id="@+id/content"
8 android:layout_width="match_parent"
9 android:layout_height="0dp"
10 android:layout_weight="1" >
11 </FrameLayout>
12
13 <LinearLayout
14 android:layout_width="match_parent"
15 android:layout_height="60dp"
16 android:background="@drawable/tab_bg" >
17
18 <RelativeLayout
19 android:id="@+id/message_layout"
20 android:layout_width="0dp"
21 android:layout_height="match_parent"
22 android:layout_weight="1" >
23
24 <LinearLayout
25 android:layout_width="match_parent"
26 android:layout_height="wrap_content"
27 android:layout_centerVertical="true"
28 android:orientation="vertical" >
29
30 <ImageView
31 android:id="@+id/message_image"
32 android:layout_width="wrap_content"
33 android:layout_height="wrap_content"
34 android:layout_gravity="center_horizontal"
35 android:src="@drawable/message_unselected" />
36
37 <TextView
38 android:id="@+id/message_text"
39 android:layout_width="wrap_content"
40 android:layout_height="wrap_content"
41 android:layout_gravity="center_horizontal"
42 android:text="消息"
43 android:textColor="#82858b" />
44 </LinearLayout>
45 </RelativeLayout>
46
47 <RelativeLayout
48 android:id="@+id/contacts_layout"
49 android:layout_width="0dp"
50 android:layout_height="match_parent"
51 android:layout_weight="1" >
52
53 <LinearLayout
54 android:layout_width="match_parent"
55 android:layout_height="wrap_content"
56 android:layout_centerVertical="true"
57 android:orientation="vertical" >
58
59 <ImageView
60 android:id="@+id/contacts_image"
61 android:layout_width="wrap_content"
62 android:layout_height="wrap_content"
63 android:layout_gravity="center_horizontal"
64 android:src="@drawable/contacts_unselected" />
65
66 <TextView
67 android:id="@+id/contacts_text"
68 android:layout_width="wrap_content"
69 android:layout_height="wrap_content"
70 android:layout_gravity="center_horizontal"
71 android:text="联系人"
72 android:textColor="#82858b" />
73 </LinearLayout>
74 </RelativeLayout>
75
76 <RelativeLayout
77 android:id="@+id/news_layout"
78 android:layout_width="0dp"
79 android:layout_height="match_parent"
80 android:layout_weight="1" >
81
82 <LinearLayout
83 android:layout_width="match_parent"
84 android:layout_height="wrap_content"
85 android:layout_centerVertical="true"
86 android:orientation="vertical" >
87
88 <ImageView
89 android:id="@+id/news_image"
90 android:layout_width="wrap_content"
91 android:layout_height="wrap_content"
92 android:layout_gravity="center_horizontal"
93 android:src="@drawable/news_unselected" />
94
95 <TextView
96 android:id="@+id/news_text"
97 android:layout_width="wrap_content"
98 android:layout_height="wrap_content"
99 android:layout_gravity="center_horizontal"
100 android:text="动态"
101 android:textColor="#82858b" />
102 </LinearLayout>
103 </RelativeLayout>
104
105 <RelativeLayout
106 android:id="@+id/setting_layout"
107 android:layout_width="0dp"
108 android:layout_height="match_parent"
109 android:layout_weight="1" >
110
111 <LinearLayout
112 android:layout_width="match_parent"
113 android:layout_height="wrap_content"
114 android:layout_centerVertical="true"
115 android:orientation="vertical" >
116
117 <ImageView
118 android:id="@+id/setting_image"
119 android:layout_width="wrap_content"
120 android:layout_height="wrap_content"
121 android:layout_gravity="center_horizontal"
122 android:src="@drawable/setting_unselected" />
123
124 <TextView
125 android:id="@+id/setting_text"
126 android:layout_width="wrap_content"
127 android:layout_height="wrap_content"
128 android:layout_gravity="center_horizontal"
129 android:text="设置"
130 android:textColor="#82858b" />
131 </LinearLayout>
132 </RelativeLayout>
133 </LinearLayout>
134
135 </LinearLayout>
这段布局代码虽然有点长,但其实主要就分为两部分。第一个部分就是FrameLayout,这里只是给FrameLayout的id设置成content,并没有在里面添加任何具体的内容,因为具体的内容是要在后面动态进行添加的。第二个部分就是FrameLayout下面的LinearLayout,这个LinearLayout中包含的就是整个类似于TabHost的布局。可以看到,我们将这个LinearLayout又等分成了四份,每一份中都会显示一个ImageView和一个TextView。ImageView用于显示当前Tab的图标,TextView用于显示当前Tab的标题,这个效果就会和QQ非常得类似。
既然是等分成了四分,那接下来我们自然要去分别实现四个Fragment和它们的布局了。
新建一个message_layout.xml作为消息界面的布局,代码如下所示:
1 <?xml version="1.0" encoding="utf-8"?>
2 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
3 android:layout_width="match_parent"
4 android:layout_height="match_parent" >
5
6 <LinearLayout
7 android:layout_width="wrap_content"
8 android:layout_height="wrap_content"
9 android:layout_centerInParent="true"
10 android:orientation="vertical" >
11
12 <ImageView
13 android:layout_width="wrap_content"
14 android:layout_height="wrap_content"
15 android:layout_gravity="center_horizontal"
16 android:src="@drawable/message_selected" />
17
18 <TextView
19 android:layout_width="wrap_content"
20 android:layout_height="wrap_content"
21 android:layout_gravity="center_horizontal"
22 android:padding="10dp"
23 android:text="这是消息界面"
24 android:textSize="20sp" />
25 </LinearLayout>
26
27 </RelativeLayout>
然后要去创建对应这个布局的Fragment。新建MessageFragment继承自Fragment,代码如下所示:
1 public class MessageFragment extends Fragment {
2 public View onCreateView(LayoutInflater inflater, ViewGroup container,
3 Bundle savedInstanceState) {
4 View messageLayout = inflater.inflate(R.layout.message_layout,
5 container, false);
6 return messageLayout;
7 }
8 }
后面就是依葫芦画瓢,把其它几个Fragment以及对应的布局创建出来。
新建contacts_layout.xml作为联系人界面的布局,代码如下所示:
1 <?xml version="1.0" encoding="utf-8"?>
2 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
3 android:layout_width="match_parent"
4 android:layout_height="match_parent" >
5
6 <LinearLayout
7 android:layout_width="wrap_content"
8 android:layout_height="wrap_content"
9 android:layout_centerInParent="true"
10 android:orientation="vertical" >
11
12 <ImageView
13 android:layout_width="wrap_content"
14 android:layout_height="wrap_content"
15 android:layout_gravity="center_horizontal"
16 android:src="@drawable/contacts_selected" />
17
18 <TextView
19 android:layout_width="wrap_content"
20 android:layout_height="wrap_content"
21 android:layout_gravity="center_horizontal"
22 android:padding="10dp"
23 android:text="这是联系人界面"
24 android:textSize="20sp" />
25 </LinearLayout>
26
27 </RelativeLayout>
再新建ContactsFragment继承自Fragment,代码如下所示:
1 public class ContactsFragment extends Fragment {
2 @Override
3 public View onCreateView(LayoutInflater inflater, ViewGroup container,
4 Bundle savedInstanceState) {
5 View contactsLayout = inflater.inflate(R.layout.contacts_layout,
6 container, false);
7 return contactsLayout;
8 }
9 }
然后新建news_layout.xml作为动态界面的布局,代码如下所示:
1 <?xml version="1.0" encoding="utf-8"?>
2 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
3 android:layout_width="match_parent"
4 android:layout_height="match_parent" >
5
6 <LinearLayout
7 android:layout_width="wrap_content"
8 android:layout_height="wrap_content"
9 android:layout_centerInParent="true"
10 android:orientation="vertical" >
11
12 <ImageView
13 android:layout_width="wrap_content"
14 android:layout_height="wrap_content"
15 android:layout_gravity="center_horizontal"
16 android:src="@drawable/news_selected" />
17
18 <TextView
19 android:layout_width="wrap_content"
20 android:layout_height="wrap_content"
21 android:layout_gravity="center_horizontal"
22 android:padding="10dp"
23 android:text="这是动态界面"
24 android:textSize="20sp" />
25 </LinearLayout>
26
27 </RelativeLayout>
再新建NewsFragment继承自Fragment,代码如下所示:
1 public class NewsFragment extends Fragment {
2 @Override
3 public View onCreateView(LayoutInflater inflater, ViewGroup container,
4 Bundle savedInstanceState) {
5 View newsLayout = inflater.inflate(R.layout.news_layout, container,
6 false);
7 return newsLayout;
8 }
9 }
最后新建setting_layout.xml作为设置界面的布局,代码如下所示:
1 <?xml version="1.0" encoding="utf-8"?>
2 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
3 android:layout_width="match_parent"
4 android:layout_height="match_parent" >
5
6 <LinearLayout
7 android:layout_width="wrap_content"
8 android:layout_height="wrap_content"
9 android:layout_centerInParent="true"
10 android:orientation="vertical" >
11
12 <ImageView
13 android:layout_width="wrap_content"
14 android:layout_height="wrap_content"
15 android:layout_gravity="center_horizontal"
16 android:src="@drawable/setting_selected" />
17
18 <TextView
19 android:layout_width="wrap_content"
20 android:layout_height="wrap_content"
21 android:layout_gravity="center_horizontal"
22 android:padding="10dp"
23 android:text="这是设置界面"
24 android:textSize="20sp" />
25 </LinearLayout>
26
27 </RelativeLayout>
再新建SettingFragment继承自Fragment,代码如下所示:
1 public class SettingFragment extends Fragment {
2 @Override
3 public View onCreateView(LayoutInflater inflater, ViewGroup container,
4 Bundle savedInstanceState) {
5 View settingLayout = inflater.inflate(R.layout.setting_layout,
6 container, false);
7 return settingLayout;
8 }
9 }
这样我们就把每一个Fragment,以及它们所对应的布局文件都创建好了。接下来也就是最关键的步骤了,打开或新建MainActivity作为主Activity,代码如下所示:
/** * 项目的主Activity,所有的Fragment都嵌入在这里。 * * @author guolin */
public class MainActivity extends Activity implements OnClickListener {
/** * 用于展示消息的Fragment */
private MessageFragment messageFragment;
/** * 用于展示联系人的Fragment */
private ContactsFragment contactsFragment;
/** * 用于展示动态的Fragment */
private NewsFragment newsFragment;
/** * 用于展示设置的Fragment */
private SettingFragment settingFragment;
/** * 消息界面布局 */
private View messageLayout;
/** * 联系人界面布局 */
private View contactsLayout;
/** * 动态界面布局 */
private View newsLayout;
/** * 设置界面布局 */
private View settingLayout;
/** * 在Tab布局上显示消息图标的控件 */
private ImageView messageImage;
/** * 在Tab布局上显示联系人图标的控件 */
private ImageView contactsImage;
/** * 在Tab布局上显示动态图标的控件 */
private ImageView newsImage;
/**
* 在Tab布局上显示设置图标的控件
*/
private ImageView settingImage;
/** * 在Tab布局上显示消息标题的控件 */
private TextView messageText;
/** * 在Tab布局上显示联系人标题的控件 */
private TextView contactsText;
/** * 在Tab布局上显示动态标题的控件 */
private TextView newsText;
/** * 在Tab布局上显示设置标题的控件 */
private TextView settingText;
/** * 用于对Fragment进行管理 */
private FragmentManager fragmentManager; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_main);
// 初始化布局元素
initViews();
fragmentManager = getFragmentManager();
// 第一次启动时选中第0个tab
setTabSelection(0);
} /** * 在这里获取到每个需要用到的控件的实例,并给它们设置好必要的点击事件。 */
private void initViews() {
messageLayout = findViewById(R.id.message_layout);
contactsLayout = findViewById(R.id.contacts_layout);
newsLayout = findViewById(R.id.news_layout);
settingLayout = findViewById(R.id.setting_layout);
messageImage = (ImageView) findViewById(R.id.message_image);
contactsImage = (ImageView) findViewById(R.id.contacts_image);
newsImage = (ImageView) findViewById(R.id.news_image);
settingImage = (ImageView) findViewById(R.id.setting_image);
messageText = (TextView) findViewById(R.id.message_text);
contactsText = (TextView) findViewById(R.id.contacts_text);
newsText = (TextView) findViewById(R.id.news_text);
settingText = (TextView) findViewById(R.id.setting_text);
messageLayout.setOnClickListener(this);
contactsLayout.setOnClickListener(this);
newsLayout.setOnClickListener(this);
settingLayout.setOnClickListener(this);
} @Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.message_layout:
setTabSelection(0);
break;
case R.id.contacts_layout:
setTabSelection(1);
break;
case R.id.news_layout:
setTabSelection(2);
break;
case R.id.setting_layout:
setTabSelection(3);
break;
default: break;
}
}
/** * 根据传入的index参数来设置选中的tab页。 * * @param index *
每个tab页对应的下标。0表示消息,1表示联系人,2表示动态,3表示设置。 */
private void setTabSelection(int index) {
// 每次选中之前先清楚掉上次的选中状态
clearSelection();
// 开启一个Fragment事务 FragmentTransaction
transaction = fragmentManager.beginTransaction();
//先隐藏掉所有的Fragment,以防止有多个Fragment显示在界面上的情况
hideFragments(transaction);
switch (index) {
case 0: //当点击了消息tab时,改变控件的图片和文字颜色
messageImage.setImageResource(R.drawable.message_selected);
messageText.setTextColor(Color.WHITE);
if (messageFragment ==null) {
// 如果MessageFragment为空,则创建一个并添加到界面上
messageFragment = new MessageFragment();
transaction.add(R.id.content, messageFragment);
} else {
// 如果MessageFragment不为空,则直接将它显示出来
transaction.show(messageFragment);
}
break;
case 1: // 当点击了联系人tab时,改变控件的图片和文字颜色
contactsImage.setImageResource(R.drawable.contacts_selected);
contactsText.setTextColor(Color.WHITE);
if (contactsFragment ==null) {
// 如果ContactsFragment为空,则创建一个并添加到界面上
contactsFragment = new ContactsFragment();
transaction.add(R.id.content, contactsFragment);
} else {
// 如果ContactsFragment不为空,则直接将它显示出来
transaction.show(contactsFragment); }
break;
case 2: //当点击了动态tab时,改变控件的图片和文字颜色
newsImage.setImageResource(R.drawable.news_selected);
newsText.setTextColor(Color.WHITE);
if (newsFragment == null) {
// 如果NewsFragment为空,则创建一个并添加到界面上
newsFragment = new NewsFragment(); transaction.add(R.id.content, newsFragment); }
else {
// 如果NewsFragment不为空,则直接将它显示出来
transaction.show(newsFragment);
}
break;
case 3:
default:
//当点击了设置tab时,改变控件的图片和文字颜色
settingImage.setImageResource(R.drawable.setting_selected);
settingText.setTextColor(Color.WHITE);
if (settingFragment ==null) {
// 如果SettingFragment为空,则创建一个并添加到界面上
settingFragment = new SettingFragment();
transaction.add(R.id.content,settingFragment); }
else { // 如果SettingFragment不为空,则直接将它显示出来
transaction.show(settingFragment);
}
break;
}
transaction.commit();
}
/** * 清除掉所有的选中状态。 */
private void clearSelection() {
messageImage.setImageResource(R.drawable.message_unselected);
messageText.setTextColor(Color.parseColor("#82858b"));
contactsImage.setImageResource(R.drawable.contacts_unselected);
contactsText.setTextColor(Color.parseColor("#82858b"));
newsImage.setImageResource(R.drawable.news_unselected);
newsText.setTextColor(Color.parseColor("#82858b"));
settingImage.setImageResource(R.drawable.setting_unselected);
settingText.setTextColor(Color.parseColor("#82858b")); } /** *
将所有的Fragment都置为隐藏状态。 * * @param transaction * 用于对Fragment执行操作的事务
*/ private void hideFragments(FragmentTransaction transaction) {
if (messageFragment != null) {
transaction.hide(messageFragment);
} if (contactsFragment != null) {
transaction.hide(contactsFragment);
}
if (newsFragment != null) {
transaction.hide(newsFragment);
}
if (settingFragment != null) {
transaction.hide(settingFragment);
}
}
}
这个类中的注释已经写得非常详细了,下面我再带大家简单梳理一遍。在onCreate()方法中先是调用了initViews()来获取每个控件的实例,并给相应的控件设置好点击事件,然后调用setTabSelection()方法设置默认的选中项,这里传入的0说明默认选中第1个Tab项。那么setTabSelection()方法中又是如何处理的呢?可以看到,首先第一步是调用clearSelection()方法来清理掉之前的选中状态,然后开启一个Fragment事务,并隐藏掉所有的Fragment,以防止有多个Fragment显示在界面上。接下来根据传入的index参数判断出选中的是哪一个Tab项,并改变该Tab项的图标和文字颜色,然后将相应的Fragment添加到界面上。这里注意一个细节,我们添加Fragment的时候并没有使用replace()方法,而是会先判断一下该Fragment是否为空,如果是空的则调用add()方法添加一个进来,如果不是空的则直接调用show()方法显示出来即可。那么为什么没有使用replace()方法呢?这是因为replace()方法会将被替换掉的那个Fragment彻底地移除掉,该Fragment的生命周期就结束了。当再次点击刚才那个Tab项的时候,就会让该Fragment的生命周期重新开始,onCreate()、onCreateView()等方法都会重新执行一遍。这显然不是我们想要的,也和ActivityGroup的工作原理不符,因此最好的解决方案就是使用hide()和show()方法来隐藏和显示Fragment,这就不会让Fragment的生命周期重走一遍了。
设置完默认选中项后,我们当然还可以通过点击Tab项来自由地切换界面,这就会进入到onClick()方法中。onClick()方法中的逻辑判断非常简单,当点击了消息标签时就会选中第1个tab项,点击联系人标签时就会选中第2个tab项,点击动态标签时就会选中第3个tab项,点击设置标签时就会选中第4个tab项。都是通过调用setTabSelection()方法来完成的,只是传入了不同的参数。
好了,这样我们就将全部的代码都编写完成了,下面就来运行一下吧
原文来自:雨枫技术教程网 http://www.fengfly.com
原文网址:http://www.fengfly.com/plus/view-215109-3.html
Android使用Fragment实现TabHost效果的更多相关文章
- ViewPager+Fragment替代TabHost效果的简单示例
本示例旨在展示fragment替代tabhost的效果,具体的业务逻辑还要根据这个示例进行扩展. 效果图如下: 主Activity代码: package com.llb.view; import ja ...
- Android使用Fragment来实现ViewPager的功能(解决切换Fragment状态不保存)以及各个Fragment之间的通信
以下内容为原创,转载请注明:http://www.cnblogs.com/tiantianbyconan/p/3364728.html 我前两天写过一篇博客<Android使用Fragment来 ...
- Android 自学之选项卡TabHost
选项卡(TabHost)是一种非常实用的组件,TabHost可以很方便地在窗口上放置多个标签页,每个标签页相当于获得了一个与外部容器相同大小的组建摆放区域.通过这种方式,就可以在一个容器中放置更多组件 ...
- Android实现网易新闻客户端效果
下面来简单实现一下网易新闻客户端左右切换的效果,当然实际项目上肯定不能这样写,还有很多需要优化的地方. activity_main.xml [html] view plaincopyprint? &l ...
- Android中Fragment和ViewPager那点事儿(仿微信APP)
在之前的博文<Android中使用ViewPager实现屏幕页面切换和引导页效果实现>和<Android中Fragment的两种创建方式>以及<Android中Fragm ...
- Android中Fragment与Activity之间的交互(两种实现方式)
(未给Fragment的布局设置BackGound) 之前关于Android中Fragment的概念以及创建方式,我专门写了一篇博文<Android中Fragment的两种创建方式>,就如 ...
- Android - ViewPager+Fragment初始化问题
Android应用开发中,经常会用到ViewPager + Fragment,虽然效果不错,但随之而来的还有一些问题,下面就说说其中的初始化问题. ViewPager初始化时会预加载前后的2个页面,即 ...
- 33.Android之Fragment学习
Fragment Android是在Android 3.0 (API level 11)开始引入Fragment的. 可以把Fragment想成Activity中的模块,这个模块有自己的布局,有自己的 ...
- Android之Fragment(二)
本文主要内容 如何管理Fragment回退栈 Fragment如何与Activity交互 Fragment与Activity交互的最佳实践 没有视图的Fragment的用处 使用Fragment创建对 ...
随机推荐
- 最近无意中看到一个讲解spring mvc的系列,从源码的角度讲解,特记录下来,供以后反复学习
SpringMVC深度探险(一) —— SpringMVC前传 SpringMVC深度探险(二) —— SpringMVC概览 SpringMVC深度探险(三) —— DispatcherServle ...
- The system is running in low-graphics mode UB16
1.Ctrl+ALT+F1 进入控制台 2.输入用户名和密码进入系统 3.输入以下命令: cd /etc/X11 sudo cp xorg.conf.failsafe xorg.conf sudo r ...
- 关于产品UE的胡思乱想
1.产品的目标是 取悦 用户 不能只盯着功能实现,而不考虑用户使用. 我们的目标不应该不过让用户使用我们的产品.而是让用户在使用我们产品过程中感到 "愉悦". 2.用户是SB 3 ...
- vs2010编译错误(报错:LINK : fatal error LNK1123: 转换到 COFF 期间失败: 文件无效或损坏)
报错:LINK : fatal error LNK1123: 转换到 COFF 期间失败: 文件无效或损坏 1> 这段时间忙于看文献,没用过VS了. 今天用着用着就报错了: LINK : fat ...
- Android批量图片加载经典系列——使用LruCache、AsyncTask缓存并异步加载图片
一.问题描述 使用LruCache.AsyncTask实现批量图片的加载并达到下列技术要求 1.从缓存中读取图片,若不在缓存中,则开启异步线程(AsyncTask)加载图片,并放入缓存中 2.及时移除 ...
- (转)GPU图形绘制管线
摘抄“GPU Programming And Cg Language Primer 1rd Edition” 中文名“GPU编程与CG语言之阳春白雪下里巴人”第二章. 图形绘制管线描述GPU渲染流程, ...
- CentOS下防御或减轻DDoS攻击方法(转)
查看攻击IP 首先使用以下代码,找出攻击者IP netstat -ntu | awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -n 将会得 ...
- SoapUI Pro Project Solution Collection-XML assert
in soapui the XML object used here is from org.w3c.dom package so you need to read this article car ...
- Java动态代理实现方法
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflec ...
- spring boot+ Intellj idea devtools 设置热部署
POM文件 <!--添加依赖--> <dependency> <groupId>org.springframework.boot</groupId> & ...