XDRush

仿斗鱼Android客户端结构实现

1. 实现说明

本文旨在通过TabLayout和ViewPager打造最常见的底部导航栏+多Tab结构的Android App。

1.1 斗鱼Android客户端结构

先来简要看看斗鱼客户端基本结构:

首页结构

直播页结构

1.2 模仿结构示意图

模仿首页

模仿直播页

其中,首页和直播页最大的区别是首页只有5个tab,儿直播页则有10个tab,其中几个tab又有若干个子Tab。

2. 代码实现

2.1 主体代码实现

采用Android官方提供的TabLayout+ViewPager结构来实现。

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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
/**
* 点击每个底部导航栏中的子项所对应的基类Fragment.
*
* Created by xdrush on 2017/1/26.
*/
public abstract class BaseItemFragment extends Fragment {
TabLayout mTabLayout;
ViewPager mViewPager;
ItemFragmentAdapter mFragmentAdapter;
List<Fragment> mFragmentList;
List<String> mTitleList;
@Override
public View onCreateView(LayoutInflater layoutInflater, ViewGroup container,
Bundle savedInstanceState) {
View view = layoutInflater.inflate(R.layout.main_content_fragment_layout, container, false);
setupViews(view);
initFragment();
setupFragment();
return view;
}
private final void setupViews(View view) {
mTabLayout = (TabLayout) view.findViewById(R.id.tablayout);
mViewPager = (ViewPager) view.findViewById(R.id.viewpager);
mFragmentList = new ArrayList<>();
mTitleList = new ArrayList<>();
}
private final void setupFragment() {
int size = mTitleList.size();
if (getActivity() == null || 0 == size) {
return;
}
for (int i = 0; i < size; i ++) {
mTabLayout.addTab(mTabLayout.newTab().setText(mTitleList.get(i)));
}
// 方法一:有bug, 没有回弹.
// mFragmentAdapter = new ItemFragmentAdapter(getActivity().getSupportFragmentManager(),
// mFragmentList, mTitleList);
// 方法二:bug fixed, 有回弹.
mFragmentAdapter = new ItemFragmentAdapter(getChildFragmentManager(),
mFragmentList, mTitleList);
mViewPager.setAdapter(mFragmentAdapter);
mTabLayout.setupWithViewPager(mViewPager);
setTabLayoutMode();
}
public abstract void setTabLayoutMode();
public abstract void initFragment();
}
/**
* 首页Fragment.
*
* Created by xdrush on 2017/1/26.
*/
public class MainPageFragment extends BaseItemFragment {
private static final int TAB_COUNTS = 5;
private static final String[] TITLE = {"推荐", "手游", "娱乐", "游戏", "趣玩", ""};
@Override
public void initFragment() {
for (int i = 0; i < TAB_COUNTS; i ++) {
String title = TITLE[i];
mTitleList.add(title);
BaseContentFragment fragment = new BaseContentFragment();
fragment.setContent(title);
mFragmentList.add(fragment);
}
mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
BaseContentFragment baseContentFragment = (BaseContentFragment) mFragmentList.get(position);
baseContentFragment.setContent(mTitleList.get(position));
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
}
@Override
public void setTabLayoutMode() {
mTabLayout.setTabMode(TabLayout.MODE_FIXED);
}
}
/**
* 直播页面对应的Fragment.
*
* Created by xdrush on 2017/1/26.
*/
public class OnAirPageFragment extends BaseItemFragment {
private static final int TAB_COUNTS = 10;
private static final String[] TITLE = {"常用", "全部", "热门游戏", "移动游戏", "鱼乐星天地",
"颜值", "科技", "文娱天堂", "正能量", "体育直播"};
@Override
public void initFragment() {
for (int i = 0; i < TAB_COUNTS; i ++) {
String title = TITLE[i];
mTitleList.add(title);
BaseContentFragment fragment = new BaseContentFragment();
fragment.setContent(title);
mFragmentList.add(fragment);
}
}
@Override
public void setTabLayoutMode() {
mTabLayout.setTabMode(TabLayout.MODE_SCROLLABLE);
}
}

上面涉及到的MainPageFragment和OnAirPageFragment分别是点击底部“首页”和“直播”所对应的页面,其中BaseContentFragment类对应的每个子Tab的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
/**
* 每个Tab对应的Fragment.
*
* Created by xdrush on 2017/1/26.
*/
public class BaseContentFragment extends Fragment {
private TextView mContentTv;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.base_content_fragment_layout, container, false);
setupViews(view);
return view;
}
private void setupViews(View view) {
mContentTv = (TextView) view.findViewById(R.id.content);
}
public void setContent(String content) {
if (null == mContentTv) {
return;
}
mContentTv.setText(content);
}
}

对应的xml文件如下:

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
// main_content_fragment_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.TabLayout
android:id="@+id/tablayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#389d00"
app:tabIndicatorColor="#ffff00"
app:tabSelectedTextColor="#CD0000"
app:tabTextColor="#ffffff"/>
<android.support.v4.view.ViewPager
android:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>
// base_content_fragment_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="hello world"
android:layout_gravity="center"/>
</LinearLayout>

2.2 注意问题

可能你会注意到上面有个注释bug/bug fixed,对的,如果采用方法一来实现的话,将会出现下图所示现象:

如上图所示:将会导致indicator和title对不上,整个ViewPager没有回弹过程,导致以上bug。这个问题用方法二即可解决。