Android 程序中实现Tab类型界面很常见,本人在做项目的时候也经常用到,所以想在这里总结一下,实现tab类型界面的几种方式,供大家参考。如有不对之处,欢迎大家指正!
实现TAB类型界面,首先想到的就是这种方式。但是在API level 13之后官方就不建议使用它了。不过还是在这里简单说一下它的使用吧。
使用它的关键就是布局文件了。需要在布局中添加<TabHost>、<TabWidget>、<FrameLayout>这三个控件,id分别是系统提供的:@android:id/tabhost 、@android:id/tabs 、@android:id/tabcontent 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
|
<?
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"
>
<
TabHost
android:id
=
"@android:id/tabhost"
android:layout_width
=
"match_parent"
android:layout_height
=
"match_parent"
>
<
RelativeLayout
android:layout_width
=
"match_parent"
android:layout_height
=
"match_parent"
android:orientation
=
"vertical"
>
<!-- 可以指定tabwidget的位置 android:layout_alignParentBottom="true" -->
<
TabWidget
android:id
=
"@android:id/tabs"
android:layout_width
=
"match_parent"
android:layout_height
=
"wrap_content"
android:layout_alignParentBottom
=
"false"
>
</
TabWidget
>
<
FrameLayout
android:id
=
"@android:id/tabcontent"
android:layout_width
=
"match_parent"
android:layout_height
=
"match_parent"
android:layout_below
=
"@android:id/tabs"
>
<
LinearLayout
android:id
=
"@+id/tab1"
android:layout_width
=
"match_parent"
android:layout_height
=
"match_parent"
android:background
=
"#DEB887"
android:orientation
=
"vertical"
>
</
LinearLayout
>
<
LinearLayout
android:id
=
"@+id/tab2"
android:layout_width
=
"match_parent"
android:layout_height
=
"match_parent"
android:background
=
"#BCEE68"
android:orientation
=
"vertical"
>
</
LinearLayout
>
<
LinearLayout
android:id
=
"@+id/tab3"
android:layout_width
=
"match_parent"
android:layout_height
=
"match_parent"
android:background
=
"#7D9EC0"
android:orientation
=
"vertical"
>
</
LinearLayout
>
</
FrameLayout
>
</
RelativeLayout
>
</
TabHost
>
</
LinearLayout
>
|
一个linearlayout对应一个tab页面的布局。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
|
目前最常见的tab界面就是使用viewpager来实现了。
先来说一下viewpager的一般使用步骤:
在布局文件中添加viewpager控件
2. 在代码中设置viewpager适配器,该类继承与pagerAdapter或它的子类。必须实现以下四个方法:
(1)getCount()
(2)instantiateItem()
(3)destoryItem()
(4)isViewFromObject()
3. 初始化viewpager控件,设置监听器
4. 设置监听事件(setOnPageChangeListener)
下面看一下这种方式的效果图:
主要的功能代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
private
void
init() {
viewPager = (ViewPager) findViewById(R.id.first_vp);
LayoutInflater inflater = LayoutInflater.from(
this
);
View view1 = inflater.inflate(R.layout.first_layout1,
null
);
View view2 = inflater.inflate(R.layout.first_layout2,
null
);
View view3 = inflater.inflate(R.layout.first_layout3,
null
);
list.add(view1);
list.add(view2);
list.add(view3);
viewPager.setAdapter(pagerAdapter);
viewPager.setOnPageChangeListener(
new
OnPageChangeListener() {
@Override
public
void
onPageSelected(
int
arg0) {
setDots(arg0);
}
@Override
public
void
onPageScrolled(
int
arg0,
float
arg1,
int
arg2) {
}
@Override
public
void
onPageScrollStateChanged(
int
arg0) {
}
});
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
private
PagerAdapter pagerAdapter =
new
PagerAdapter() {
//官方建议这么写
@Override
public
boolean
isViewFromObject(View arg0, Object arg1) {
return
arg0 == arg1;
}
//返回一共有多少个界面
@Override
public
int
getCount() {
return
list.size();
}
//实例化一个item
@Override
public
Object instantiateItem(ViewGroup container,
int
position) {
container.addView(list.get(position));
return
list.get(position);
}
//销毁一个item
@Override
public
void
destroyItem(ViewGroup container,
int
position, Object object) {
container.removeView(list.get(position));
}
|
适配器中必须要实现以上的四个方法。
如果只有这几个页面,交互性肯定是不好的,所以需要添加“指示器”,用来标识当前的页面是哪一个!我在这里用点来实现。就像效果图显示的那样。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
/**
* 初始化底部的点
*/
private
void
initDots() {
pointLayout = (LinearLayout) findViewById(R.id.point_layout);
dots =
new
ImageView[list.size()];
for
(
int
i =
0
; i < list.size(); i++) {
dots[i] = (ImageView) pointLayout.getChildAt(i);
}
currentIndex =
0
;
dots[currentIndex].setBackgroundResource(R.drawable.dian_down);
}
/**
* 当滚动的时候更换点的背景图
*/
private
void
setDots(
int
position) {
if
(position <
0
|| position > list.size() -
1
|| currentIndex == position) {
return
;
}
dots[position].setBackgroundResource(R.drawable.dian_down);
dots[currentIndex].setBackgroundResource(R.drawable.dian);
currentIndex = position;
|
重点就是页面切换之后,点也要切换。这时候就用到了OnPageChangeListener中的onPageSelected(int arg0)这个方法了。
1
2
3
4
|
@Override
public
void
onPageSelected(
int
arg0) {
setDots(arg0);
|
fragment相信大家在项目中肯定都用过。这个方法主要就是利用fragmentManager对fragment的事务管理功能。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
|
// 三个选项卡
private
LinearLayout tab1Layout, tab2Layout, tab3Layout;
// 默认选中第一个tab
private
int
index =
1
;
// fragment管理类
private
FragmentManager fragmentManager;
// 三个fragment
private
Fragment tab1Fragment, tab2Fragment, tab3Fragment;
@Override
protected
void
onCreate(Bundle savedInstanceState) {
super
.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
fragmentManager = getSupportFragmentManager();
init();
}
/**
* 初始化控件
*/
private
void
init() {
tab1Layout = (LinearLayout) findViewById(R.id.tab1_layout);
tab2Layout = (LinearLayout) findViewById(R.id.tab2_layout);
tab3Layout = (LinearLayout) findViewById(R.id.tab3_layout);
tab1Layout.setOnClickListener(
this
);
tab2Layout.setOnClickListener(
this
);
tab3Layout.setOnClickListener(
this
);
//
setDefaultFragment();
}
/**
* 设置默认显示的fragment
*/
private
void
setDefaultFragment() {
FragmentTransaction transaction = fragmentManager.beginTransaction();
tab1Fragment =
new
Tab1Fragment();
transaction.replace(R.id.content_layout, tab1Fragment);
transaction.commit();
}
/**
*切换fragment
* @param newFragment
*/
private
void
replaceFragment(Fragment newFragment) {
FragmentTransaction transaction = fragmentManager.beginTransaction();
if
(!newFragment.isAdded()) {
transaction.replace(R.id.content_layout, newFragment);
transaction.commit();
}
else
{
transaction.show(newFragment);
}
}
/**
* 改变现象卡的选中状态
*/
private
void
clearStatus() {
if
(index ==
1
) {
tab1Layout.setBackgroundColor(getResources().getColor(R.color.tab));
}
else
if
(index ==
2
) {
tab2Layout.setBackgroundColor(getResources().getColor(R.color.tab));
}
else
if
(index ==
3
) {
tab3Layout.setBackgroundColor(getResources().getColor(R.color.tab));
}
}
@Override
public
void
onClick(View v) {
clearStatus();
switch
(v.getId()) {
case
R.id.tab1_layout:
if
(tab1Fragment ==
null
) {
tab1Fragment =
new
Tab1Fragment();
}
replaceFragment(tab1Fragment);
tab1Layout.setBackgroundColor(getResources().getColor(R.color.tab_down));
index =
1
;
break
;
case
R.id.tab2_layout:
if
(tab2Fragment ==
null
) {
tab2Fragment =
new
Tab2Fragment();
}
replaceFragment(tab2Fragment);
tab2Layout.setBackgroundColor(getResources().getColor(R.color.tab_down));
index =
2
;
break
;
case
R.id.tab3_layout:
if
(tab3Fragment ==
null
) {
tab3Fragment =
new
Tab3Fragment();
}
replaceFragment(tab3Fragment);
tab3Layout.setBackgroundColor(getResources().getColor(R.color.tab_down));
index =
3
;
break
;
}
|
每一个fragment对应一个布局,点击不同的按钮来切换页面。效果如下图:
如果想使用fragment的时候又想可以左右滑动,就可以使用这种方式。主要的部分就在viewpager的适配器。它的适配器继承FragmentPagerAdapter.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
import
java.util.ArrayList;
import
android.support.v4.app.Fragment;
import
android.support.v4.app.FragmentManager;
import
android.support.v4.app.FragmentPagerAdapter;
public
class
FragmentAdapter
extends
FragmentPagerAdapter {
private
ArrayList<Fragment> list;
public
FragmentAdapter(FragmentManager fm, ArrayList<Fragment> list) {
super
(fm);
this
.list = list;
}
@Override
public
Fragment getItem(
int
arg0) {
return
list.get(arg0);
}
@Override
public
int
getCount() {
return
list.size();
}
|
需要传入FragmentManager对象和一个存放fragment的list对象。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
/**
* 初始化viewpager
*/
private
void
initViewPager() {
viewPager = (ViewPager) findViewById(R.id.third_vp);
fragmentsList =
new
ArrayList<>();
Fragment fragment =
new
Tab1Fragment();
fragmentsList.add(fragment);
fragment =
new
Tab2Fragment();
fragmentsList.add(fragment);
fragment =
new
Tab3Fragment();
fragmentsList.add(fragment);
viewPager.setAdapter(
new
FragmentAdapter(getSupportFragmentManager(), fragmentsList));
viewPager.setCurrentItem(
0
);
viewPager.setOnPageChangeListener(
this
);
|
对button添加点击事件。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
@Override
public
void
onClick(View v) {
switch
(v.getId()) {
case
R.id.tab1_tv:
viewPager.setCurrentItem(
0
);
break
;
case
R.id.tab2_tv:
viewPager.setCurrentItem(
1
);
break
;
case
R.id.tab3_tv:
viewPager.setCurrentItem(
2
);
break
;
}
|
我在布局文件中添加了一个imageview作为指示器。如果想第一种tab类型界面的实现方式那样在onPageSelected()方法中进行设置,效果是只能当页面完全切换过来之后才能把指示器移动过去。要想实现滑动页面的时候同时移动指示器,就需要在onPageScrolled()方法中进行设置。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
@Override
public
void
onPageScrolled(
int
position,
float
positionOffset,
int
positionOffsetPixels) {
offset = (screen1_3 - cursorImg.getLayoutParams().width) /
2
;
Log.d(
"111"
, position +
"--"
+ positionOffset +
"--"
+ positionOffsetPixels);
final
float
scale = getResources().getDisplayMetrics().density;
if
(position ==
0
) {
// 0<->1
lp.leftMargin = (
int
) (positionOffsetPixels /
3
) + offset;
}
else
if
(position ==
1
) {
// 1<->2
lp.leftMargin = (
int
) (positionOffsetPixels /
3
) + screen1_3 +offset;
}
cursorImg.setLayoutParams(lp);
currentIndex = position;
|
onPageScrolled中的三个参数比较重要。第一个参数是position。它的含义是表示当前显示的界面中的第一个界面。意思就是的当滑动的时候,有可能出现两个界面,position指的是左边的界面。第二个参数是positionOffset指的是偏移量的比例,取值范围是[0, 1)。第三个参数是positionOffsetPixels是指偏移的像素值。后两个参数都相对页面(一个page)来说的。
我之前有看到过设置指示器的时候用的前两个参数的,我也试了一下,OK的。不过感觉比较复杂,看了一下官方api,用第三个参数更简单。关键就是理解第一个参数position。用这种方法我只在代码里有两个判断就可以完成了。
效果图如下:
这种方式没有上一种效果好看,而且标题变动。看一下效果图:
布局文件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
<?
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.support.v4.view.ViewPager
android:id
=
"@+id/fifth_vp"
android:layout_width
=
"wrap_content"
android:layout_height
=
"wrap_content"
android:layout_gravity
=
"center"
>
<
android.support.v4.view.PagerTabStrip
android:id
=
"@+id/fifth_strip"
android:layout_width
=
"wrap_content"
android:layout_height
=
"wrap_content"
android:layout_gravity
=
"top"
android:background
=
"#7EC0EE"
android:padding
=
"10dip"
/>
</
android.support.v4.view.ViewPager
>
</
LinearLayout
>
|
先来说一下PagerTitleStrip和PagerTabStrip的区别:PagerTitleStrip没有指示器,只有标题,且标题没有响应事件;而PagerTabStrip是带有指示器的,当然也有标题,具有相应事件。二者的实现只在布局文件中有区别,只需要把android.support.v4.view.PagerTabStrip改成android.support.v4.viewPagerTitleStrip即可。
代码中需要注意的就是,在适配器中重写getPageTitle(int)方法来显示标题。
1
2
|
PagerAdapter pagerAdapter =
new
PagerAdapter() {
//此处省略其他的方法
|
1
2
3
4
5
6
|
// 重写此方法即可显示标题
@Override
public
CharSequence getPageTitle(
int
position) {
return
titleList.get(position);
}
|
我目前所遇到的tab类型界面的实现方式就是这么多了,若果大家有其他的实现方式,请留言。如果我写的有误,请留言指正,共同进步!谢谢!
Demo下载地址:http://download.csdn.net/detail/crazy1235/8358671