今天,我将总结一下Android应用中大家经常见到的底部导航栏的几种实现!
一。TabHost + RadioGroup实现方式
在我们平时开发过程中使用的TabHost是在上方,这里我们简单修改了一下<TabHost>的布局,让叶片放到了底部。
main.xml
01.
<?
xml
version
=
"1.0"
encoding
=
"utf-8"
?>
02.
<
TabHost
xmlns:android
=
"http://schemas.android.com/apk/res/android"
03.
android:id
=
"@android:id/tabhost"
04.
android:layout_width
=
"fill_parent"
05.
android:layout_height
=
"fill_parent"
06.
android:orientation
=
"horizontal"
>
07.
<
LinearLayout
08.
android:layout_width
=
"fill_parent"
09.
android:layout_height
=
"fill_parent"
10.
android:orientation
=
"vertical"
>
11.
<
TabWidget
12.
android:id
=
"@android:id/tabs"
13.
android:layout_width
=
"fill_parent"
14.
android:layout_height
=
"wrap_content"
15.
android:visibility
=
"gone"
/>
16.
<
FrameLayout
17.
android:id
=
"@android:id/tabcontent"
18.
android:layout_width
=
"fill_parent"
19.
android:layout_height
=
"fill_parent"
20.
android:layout_weight
=
"1.0"
/>
21.
<
RadioGroup
22.
android:id
=
"@+id/radioGroup"
23.
android:layout_width
=
"fill_parent"
24.
android:layout_height
=
"wrap_content"
25.
android:layout_gravity
=
"bottom"
26.
android:background
=
"@drawable/bar"
27.
android:gravity
=
"center_vertical"
28.
android:orientation
=
"horizontal"
>
29.
<
RadioButton
30.
android:id
=
"@+id/rd_home"
31.
style
=
"@style/main_btn_style"
32.
android:layout_width
=
"wrap_content"
33.
android:layout_height
=
"wrap_content"
34.
android:drawableTop
=
"@drawable/home_icon"
35.
android:text
=
"主页"
/>
36.
<
RadioButton
37.
android:id
=
"@+id/rd_wt"
38.
style
=
"@style/main_btn_style"
39.
android:layout_width
=
"wrap_content"
40.
android:layout_height
=
"wrap_content"
41.
android:drawableTop
=
"@drawable/wb_icon_write_n"
42.
android:text
=
"发表"
/>
43.
<
RadioButton
44.
android:id
=
"@+id/rd_msg"
45.
style
=
"@style/main_btn_style"
46.
android:layout_width
=
"wrap_content"
47.
android:layout_height
=
"wrap_content"
48.
android:drawableTop
=
"@drawable/msg_icon"
49.
android:text
=
"信息"
/>
50.
<
RadioButton
51.
android:id
=
"@+id/rd_more"
52.
style
=
"@style/main_btn_style"
53.
android:layout_width
=
"wrap_content"
54.
android:layout_height
=
"wrap_content"
55.
android:drawableTop
=
"@drawable/more_icon"
56.
android:text
=
"更多"
/>
57.
</
RadioGroup
>
58.
</
LinearLayout
>
59.
</
TabHost
>
styles.xml
01.
<
style
name
=
"main_btn_style"
>
02.
<
item
name
=
"android:button"
>@null</
item
>
03.
<
item
name
=
"android:textSize"
>10dp</
item
>
04.
<
item
name
=
"android:textColor"
>#ffffff</
item
>
05.
<
item
name
=
"android:gravity"
>center_horizontal</
item
>
06.
<
item
name
=
"android:drawablePadding"
>4dp</
item
>
07.
<
item
name
=
"android:layout_weight"
>1.0</
item
>
08.
<
item
name
=
"android:background"
>@drawable/main_btn_bg_d</
item
>
09.
</
style
>
main_btn_bg_d.xml
1.
<?
xml
version
=
"1.0"
encoding
=
"utf-8"
?>
2.
<
selector
xmlns:android
=
"http://schemas.android.com/apk/res/android"
>
3.
<
item
android:drawable
=
"@drawable/main_btn_bg"
android:state_enabled
=
"true"
android:state_pressed
=
"true"
></
item
>
4.
<
item
android:drawable
=
"@drawable/main_btn_bg"
android:state_checked
=
"true"
android:state_enabled
=
"true"
></
item
>
5.
</
selector
>
这里需要注意的是:
1.main.xml中:TabWidget的id必须是@android:id/tabs,FrameLayout的id必须是 @android:id/tabcontent。
2.把TabWidget的Visibility设置成了gone!也就是默认难看的风格不见了:,取而代之的是5个带风格的单选按钮.
MainActivity类
01.
package
com.zhf.android_tabhost;
02.
import
android.app.TabActivity;
03.
import
android.content.Intent;
04.
import
android.os.Bundle;
05.
import
android.widget.RadioGroup;
06.
import
android.widget.RadioGroup.OnCheckedChangeListener;
07.
import
android.widget.TabHost;
08.
import
android.widget.TabHost.TabSpec;
09.
public
class
MainActivity
extends
TabActivity {
10.
private
TabHost tabHost;
11.
12.
@Override
13.
public
void
onCreate(Bundle savedInstanceState) {
14.
super
.onCreate(savedInstanceState);
15.
setContentView(R.layout.main);
16.
17.
tabHost =
this
.getTabHost();
18.
19.
TabSpec homeSpec = tabHost.newTabSpec(
"home"
).setIndicator(
"home"
).setContent(
new
Intent(
this
,HomeActivity.
class
));
20.
TabSpec writeSpec = tabHost.newTabSpec(
"write"
).setIndicator(
"write"
).setContent(
new
Intent(
this
,WriteActivity.
class
));
21.
TabSpec msgSpec = tabHost.newTabSpec(
"msg"
).setIndicator(
"msg"
).setContent(
new
Intent(
this
,MsgActivity.
class
));
22.
TabSpec moreSpec = tabHost.newTabSpec(
"more"
).setIndicator(
"more"
).setContent(
new
Intent(
this
,MoreActivity.
class
));
23.
24.
tabHost.addTab(homeSpec);
25.
tabHost.addTab(writeSpec);
26.
tabHost.addTab(msgSpec);
27.
tabHost.addTab(moreSpec);
28.
29.
RadioGroup rg = (RadioGroup)
this
.findViewById(R.id.radioGroup);
30.
31.
rg.setOnCheckedChangeListener(
new
OnCheckedChangeListener() {
32.
33.
@Override
34.
public
void
onCheckedChanged(RadioGroup group,
int
checkedId) {
35.
switch
(checkedId) {
36.
case
R.id.rd_home:
37.
tabHost.setCurrentTabByTag(
"home"
);
38.
break
;
39.
case
R.id.rd_wt:
40.
tabHost.setCurrentTabByTag(
"write"
);
41.
break
;
42.
case
R.id.rd_msg:
43.
tabHost.setCurrentTabByTag(
"msg"
);
44.
break
;
45.
case
R.id.rd_more:
46.
tabHost.setCurrentTabByTag(
"more"
);
47.
break
;
48.
default
:
49.
break
;
50.
}
51.
}
52.
});
53.
}
54.
}
注:
1.由于TabWidget被隐藏,所以相关的事件也会无效,这里取巧用RadioGroup与RadioButton的特性来处理切换,然后监听事件调用setCurrentTabByTag来切换Activity。
2.注意即使TabWidget被隐藏,也要为其设置indicator,否则会保持。
效果图:
(点击底部就可以实现切换不同的Activity的操作了,这里需要注意的一点是,切换底部菜单时不会重新调用onCreate()方法的!!)
二.底部回调接口实现(使用Fragment)--- 重要!
这种方式使用了最新的Fragment,采用了底部导航栏回调接口的方法,来切换底部菜单,并且每次切换回重新调用onCreate()方法!!
main.xml:
01.
<?
xml
version
=
"1.0"
encoding
=
"utf-8"
?>
02.
<
LinearLayout
xmlns:android
=
"http://schemas.android.com/apk/res/android"
03.
xmlns:tools
=
"http://schemas.android.com/tools"
04.
android:layout_width
=
"fill_parent"
05.
android:layout_height
=
"fill_parent"
06.
android:orientation
=
"vertical"
07.
tools:context
=
".MainActivity"
>
08.
<
LinearLayout
09.
android:layout_width
=
"fill_parent"
10.
android:layout_height
=
"0dp"
11.
android:layout_weight
=
"10"
12.
android:orientation
=
"vertical"
>
13.
<
RelativeLayout
14.
android:id
=
"@+id/main_title_RelativeLayout"
15.
android:layout_width
=
"fill_parent"
16.
android:layout_height
=
"50dp"
17.
android:background
=
"@drawable/fragment_bottom_normal"
18.
android:orientation
=
"horizontal"
>
19.
<
TextView
20.
android:id
=
"@+id/main_title_TextView"
21.
android:layout_width
=
"wrap_content"
22.
android:layout_height
=
"wrap_content"
23.
android:layout_centerInParent
=
"true"
24.
android:text
=
"主页"
25.
android:textColor
=
"@android:color/white"
26.
android:textSize
=
"24sp"
/>
27.
</
RelativeLayout
>
28.
<
FrameLayout
29.
android:id
=
"@+id/main_detail_FrameLayout"
30.
android:layout_width
=
"fill_parent"
31.
android:layout_height
=
"fill_parent"
32.
android:background
=
"#ffffff"
>
33.
</
FrameLayout
>
34.
</
LinearLayout
>
35.
<
fragment
36.
android:id
=
"@+id/bottom_fragment"
37.
android:name
=
"com.zhf.frameworkdemo02.fragments.BottomFragment"
38.
android:layout_width
=
"fill_parent"
39.
android:layout_height
=
"0dp"
40.
android:layout_weight
=
"1"
/>
41.
</
LinearLayout
>
这里由于我们底部菜单我们采用了fragment,所以布局里面要留出位置!
BottomFragment类:
01.
package
com.zhf.frameworkdemo02.fragments;
02.
import
com.zhf.frameworkdemo02.R;
03.
import
android.app.Activity;
04.
import
android.os.Bundle;
05.
import
android.support.v4.app.Fragment;
06.
import
android.view.LayoutInflater;
07.
import
android.view.View;
08.
import
android.view.ViewGroup;
09.
import
android.widget.RadioGroup;
10.
import
android.widget.RadioGroup.OnCheckedChangeListener;
11.
/**
12.
* 页面底部导航栏
13.
*
14.
* @author ZHF
15.
*
16.
*/
17.
public
class
BottomFragment
extends
Fragment {
18.
//默认回调接口实现类的对象
19.
private
Callbacks callbacks = defaultCallbacks;
20.
/** Fragment和Activity建立关联的时候调用 **/
21.
@Override
22.
public
void
onAttach(Activity activity) {
23.
super
.onAttach(activity);
24.
//当前类是否实现了底部导航栏点击事件回调接口
25.
if
(!(activity
instanceof
Callbacks)) {
26.
throw
new
IllegalStateException(
"Activity must implements fragment's callbacks !"
);
27.
}
28.
callbacks = (Callbacks) activity;
29.
}
30.
/** 为Fragment加载布局时调用 **/
31.
@Override
32.
public
View onCreateView(LayoutInflater inflater, ViewGroup container,
33.
Bundle savedInstanceState) {
34.
RadioGroup radioGroup = (RadioGroup) inflater.inflate(R.layout.fragment_bottom, container,
false
);
35.
//绑定监听器
36.
radioGroup.setOnCheckedChangeListener(changeListener);
37.
return
radioGroup;
38.
}
39.
40.
/**RadioGroup监听器**/
41.
private
OnCheckedChangeListener changeListener =
new
OnCheckedChangeListener() {
42.
@Override
43.
public
void
onCheckedChanged(RadioGroup group,
int
checkedId) {
44.
System.out.println(checkedId);
45.
callbacks.onItemSelected(checkedId);
//调用接口中方法
46.
}
47.
};
48.
public
interface
Callbacks{
49.
/**导航栏回调接口**/
50.
public
void
onItemSelected(
int
item);
51.
}
52.
/**默认回调实现类的对象**/
53.
private
static
Callbacks defaultCallbacks =
new
Callbacks() {
54.
@Override
55.
public
void
onItemSelected(
int
item) {
56.
//什么都不干
57.
}
58.
};
59.
60.
/**Fragment和Activity解除关联的时候调用**/
61.
@Override
62.
public
void
onDetach() {
63.
super
.onDetach();
64.
callbacks = defaultCallbacks;
65.
}
66.
}
底部菜单布局fragment_bottom.xml
01.
<?
xml
version
=
"1.0"
encoding
=
"utf-8"
?>
02.
<
RadioGroup
xmlns:android
=
"http://schemas.android.com/apk/res/android"
03.
android:layout_width
=
"fill_parent"
04.
android:layout_height
=
"fill_parent"
05.
android:orientation
=
"horizontal"
>
06.
<
RadioButton
07.
android:id
=
"@+id/fragment_bottom_home"
08.
android:layout_width
=
"fill_parent"
09.
android:layout_height
=
"fill_parent"
10.
android:layout_weight
=
"1"
11.
android:background
=
"@drawable/fragment_bottom_selector"
12.
android:button
=
"@null"
13.
android:drawableTop
=
"@drawable/main_readiobutton_home"
14.
android:gravity
=
"center"
15.
android:text
=
"@string/home"
16.
android:textColor
=
"@color/white"
17.
android:textSize
=
"12sp"
/>
18.
<
View
19.
android:layout_width
=
"1dp"
20.
android:layout_height
=
"fill_parent"
21.
android:background
=
"@color/white"
/>
22.
<
RadioButton
23.
android:id
=
"@+id/fragment_bottom_order"
24.
android:layout_width
=
"fill_parent"
25.
android:layout_height
=
"fill_parent"
26.
android:layout_weight
=
"1"
27.
android:background
=
"@drawable/fragment_bottom_selector"
28.
android:button
=
"@null"
29.
android:drawableTop
=
"@drawable/main_readiobutton_order"
30.
android:gravity
=
"center"
31.
android:text
=
"@string/order"
32.
android:textColor
=
"@color/white"
33.
android:textSize
=
"12sp"
/>
34.
<
View
35.
android:layout_width
=
"1dp"
36.
android:layout_height
=
"fill_parent"
37.
android:background
=
"@color/white"
38.
/>
39.
<
RadioButton
40.
android:id
=
"@+id/fragment_bottom_notice"
41.
android:layout_width
=
"fill_parent"
42.
android:layout_height
=
"fill_parent"
43.
android:layout_weight
=
"1"
44.
android:background
=
"@drawable/fragment_bottom_selector"
45.
android:button
=
"@null"
46.
android:drawableTop
=
"@drawable/main_readiobutton_notice"
47.
android:gravity
=
"center"
48.
android:text
=
"@string/notice"
49.
android:textColor
=
"@color/white"
50.
android:textSize
=
"12sp"
/>
51.
<
View
52.
android:layout_width
=
"1dp"
53.
android:layout_height
=
"fill_parent"
54.
android:background
=
"@color/white"
/>
55.
<
RadioButton
56.
android:id
=
"@+id/fragment_bottom_more"
57.
android:layout_width
=
"fill_parent"
58.
android:layout_height
=
"fill_parent"
59.
android:layout_weight
=
"1"
60.
android:background
=
"@drawable/fragment_bottom_selector"
61.
android:button
=
"@null"
62.
android:drawablePadding
=
"3dip"
63.
android:drawableTop
=
"@drawable/main_readiobutton_more"
64.
android:gravity
=
"center"
65.
android:text
=
"@string/more"
66.
android:textColor
=
"@color/white"
67.
android:textSize
=
"12sp"
/>
68.
</
RadioGroup
>
这里我们定义了一个框架类:GeneralFragment(所有的fragment界面都需继承它)
001.
package
com.zhf.frameworkdemo02.fragments;
002.
import
java.io.Serializable;
003.
import
com.zhf.frameworkdemo02.R;
004.
import
com.zhf.frameworkdemo02.view.OrderView;
005.
import
com.zhf.frameworkdemo02.view.HomeView;
006.
import
com.zhf.frameworkdemo02.view.MoreView;
007.
import
com.zhf.frameworkdemo02.view.NoticeView;
008.
import
android.os.Bundle;
009.
import
android.support.v4.app.Fragment;
010.
import
android.view.LayoutInflater;
011.
import
android.view.View;
012.
import
android.view.ViewGroup;
013.
import
android.widget.TextView;
014.
/**
015.
* 框架类,抽象公共方法
016.
* @author ZHF
017.
*
018.
*/
019.
public
class
GeneralFragment
extends
Fragment
implements
Serializable{
020.
/**
021.
*
022.
*/
023.
private
static
final
long
serialVersionUID = 1L;
024.
025.
private
int
item;
//用于区分底部菜单项
026.
protected
static
View main_title_RelativeLayout;
//标题栏
027.
protected
final
static
String key =
"Bundle"
;
//跳转值传递key的名称
028.
029.
@Override
030.
public
void
onCreate(Bundle savedInstanceState) {
031.
// TODO Auto-generated method stub
032.
super
.onCreate(savedInstanceState);
033.
if
(getArguments() !=
null
) {
//根据接收子类传来的arguments判断item的哪一项
034.
if
(getArguments().containsKey(MainFragment.Item)) {
035.
item = getArguments().getInt(MainFragment.Item);
036.
}
037.
}
038.
}
039.
040.
/**为Fragment加载布局时调用 **/
041.
@Override
042.
public
View onCreateView(LayoutInflater inflater, ViewGroup container,
043.
Bundle savedInstanceState) {
044.
View view = inflater.inflate(R.layout.fragment_general, container,
false
);
045.
GeneralFragment fragment =
null
;
046.
switch
(item) {
047.
case
R.id.fragment_bottom_home:
//初始化主页
048.
fragment =
new
HomeView();
049.
break
;
050.
case
R.id.fragment_bottom_order:
051.
fragment =
new
OrderView();
//初始化订单
052.
break
;
053.
case
R.id.fragment_bottom_notice:
054.
fragment =
new
NoticeView();
//初始化公告
055.
break
;
056.
case
R.id.fragment_bottom_more:
057.
fragment =
new
MoreView();
//初始化更多
058.
break
;
059.
default
:
060.
break
;
061.
}
062.
if
(fragment !=
null
) {
063.
//更换mainView中间的内容(home,msg,at,more)
064.
getActivity().getSupportFragmentManager().beginTransaction().replace(R.id.general_fragment, fragment).commit();
065.
}
066.
main_title_RelativeLayout = ((View) container.getParent()).findViewById(R.id.main_title_RelativeLayout);
067.
//将生成的view返回
068.
return
view;
069.
}
070.
071.
/**设置标题**/
072.
protected
void
setTitle(Object title) {
073.
if
(main_title_RelativeLayout !=
null
) {
074.
//标题栏中的文字
075.
TextView mTvTitle = (TextView) main_title_RelativeLayout.findViewById(R.id.main_title_TextView);
076.
if
(mTvTitle !=
null
) {
077.
if
(title
instanceof
Integer) {
//整型
078.
mTvTitle.setText((Integer)title);
079.
}
else
{
//字符类型
080.
mTvTitle.setText((CharSequence)title);
081.
}
082.
}
083.
}
084.
}
085.
086.
/**页面跳转值传递**/
087.
protected
void
setBundle(Object... objects) {
088.
Bundle arguments =
new
Bundle();
089.
arguments.putSerializable(key, objects);
090.
GeneralFragment generalFragment =
new
GeneralFragment();
091.
generalFragment.setArguments(arguments);
092.
}
093.
094.
/**获取所传递的值**/
095.
protected
Object[] getBundle() {
096.
if
(getArguments() !=
null
) {
097.
System.out.println(
"getBundle"
);
098.
if
(getArguments().containsKey(key)) {
099.
Object[] object = (Object[]) getArguments().getSerializable(key);
100.
return
object;
101.
}
102.
}
103.
return
null
;
104.
}
105.
106.
/**无参页面跳转**/
107.
protected
void
toIntent(GeneralFragment generalFragment) {
108.
if
(generalFragment !=
null
) {
109.
getActivity().getSupportFragmentManager().beginTransaction().replace(R.id.general_fragment, generalFragment).commit();
110.
}
111.
}
112.
}
程序入口MainFragment:
01.
package
com.zhf.frameworkdemo02.fragments;
02.
import
com.zhf.frameworkdemo02.R;
03.
import
android.os.Bundle;
04.
import
android.support.v4.app.FragmentActivity;
05.
import
android.support.v4.app.FragmentManager;
06.
/**
07.
*MainView
08.
* @author ZHF
09.
*
10.
*/
11.
public
class
MainFragment
extends
FragmentActivity
implements
BottomFragment.Callbacks {
12.
13.
public
final
static
String Item =
"item"
;
14.
15.
@Override
16.
protected
void
onCreate(Bundle savedInstanceState) {
17.
// TODO Auto-generated method stub
18.
super
.onCreate(savedInstanceState);
19.
setContentView(R.layout.main);
20.
//初始化默认调用接口中item选中方法
21.
onItemSelected(R.id.fragment_bottom_home);
22.
}
23.
@Override
24.
public
void
onItemSelected(
int
item) {
25.
Bundle arguments =
new
Bundle();
26.
arguments.putInt(Item, item);
//将选中的底部radio的Id放进去
27.
GeneralFragment generalFragment =
new
GeneralFragment();
28.
generalFragment.setArguments(arguments);
//设置参数值
29.
//这里根据item的ID进行界面跳转
30.
FragmentManager fm = getSupportFragmentManager();
31.
fm.beginTransaction().replace(R.id.main_detail_FrameLayout, generalFragment).commit();
32.
}
33.
}
说明:这里我们的每个界面都将采用Fragment,故每个界面需重写onCreateView()
01.
package
com.zhf.frameworkdemo02.view;
02.
import
android.os.Bundle;
03.
import
android.view.LayoutInflater;
04.
import
android.view.View;
05.
import
android.view.ViewGroup;
06.
import
com.zhf.frameworkdemo02.R;
07.
import
com.zhf.frameworkdemo02.fragments.GeneralFragment;
08.
/**
09.
* 主页面
10.
* @author ZHF
11.
*
12.
*/
13.
public
class
HomeView
extends
GeneralFragment{
14.
@Override
15.
public
void
onCreate(Bundle savedInstanceState) {
16.
// TODO Auto-generated method stub
17.
super
.onCreate(savedInstanceState);
18.
super
.setTitle(
"主页"
);
19.
}
20.
21.
@Override
22.
public
View onCreateView(LayoutInflater inflater, ViewGroup container,
23.
Bundle savedInstanceState) {
24.
return
inflater.inflate(R.layout.home, container,
false
);
25.
}
26.
}
(其他三个略)
最终效果图:
ok!大功告成!相当实用的!有兴趣的可以学习一下!