使用ViewPager和Fragment实现底部导航滑动重构版

自定义view

设置每个页面的子item

layout_tab_item.xml,每个item由一个ImageView和一个TextView组成

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
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">


<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true">


<ImageView
android:id="@+id/tabImg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true" />


<TextView
android:id="@+id/tabText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/tabImg"
android:layout_centerHorizontal="true"
android:text="@string/app_name"
android:textColor="@android:color/white"
android:textSize="16sp" />


</RelativeLayout>

</RelativeLayout>

设置整个底部导航栏

layout_bottom.xml,底部导航栏由5个item组成

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
<?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="70dp"
android:orientation="horizontal">


<include
android:id="@+id/homeLayout"
layout="@layout/layout_tab_item"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1" />


<include
android:id="@+id/chosenLayout"
layout="@layout/layout_tab_item"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1" />


<include
android:id="@+id/searchLayout"
layout="@layout/layout_tab_item"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1" />


<include
android:id="@+id/localLayout"
layout="@layout/layout_tab_item"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1" />


<include
android:id="@+id/settingLayout"
layout="@layout/layout_tab_item"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1" />


</LinearLayout>

创建自定义View并配置

这里用的是bufferKnife注解功能,不懂的请移步谷歌。主要需要注意的就是需要设置很多id的BackgroundResource和TextColor,这里我把需要重复设置的地方进行了重构,知道这点就很容易看懂了。下面多了一处回调接口,主要用于实现点击item时viewpager滑动,即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
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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
/**
* 底部自定义view
* Created by yyg on 2016/4/22.
*/

public class MyBottomLayout extends LinearLayout implements View.OnClickListener{
private Context context;
private RelativeLayout homeLayout;
private RelativeLayout chosenLayout;
private RelativeLayout searchLayout;
private RelativeLayout localLayout;
private RelativeLayout settingLayout;
private ICallbackListener iCallbackListener = null;

public MyBottomLayout(Context context) {
super(context);
this.context = context;
initView();
}

public MyBottomLayout(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
initView();
}

public MyBottomLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.context = context;
initView();
}

/**
* 初始化
*/

private void initView(){
View view = LayoutInflater.from(context).inflate(R.layout.layout_bottom, this);
findView(view);
initData();
initListener();
}

/**
* 初始化数据
*/

private void initData() {
setResidAndColor(0);
}

/**
* 把所有的数据整合一起进行抽取
*/

private void changeDataItem(int[] resid, int[] color) {
initDataItem(homeLayout, resid[0], "首页", color[0]);
initDataItem(chosenLayout, resid[1], "精选", color[1]);
initDataItem(searchLayout, resid[2], "搜索", color[2]);
initDataItem(localLayout, resid[3], "本地", color[3]);
initDataItem(settingLayout, resid[4], "设置", color[4]);
}

/**
* 初始化数据的抽取方法
* @param resid
* @param name
* @param color
*/

private void initDataItem(View view, int resid, String name, int color) {
view.findViewById(R.id.tabImg).setBackgroundResource(resid);
TextView tv = (TextView) view.findViewById(R.id.tabText);
tv.setText(name);
tv.setTextColor( (color == 1) ? Color.BLUE : Color.WHITE);
}

/**
* 找到控件的方法
*
* @param view
*/

private void findView(View view) {
homeLayout = (RelativeLayout) view.findViewById(R.id.homeLayout);
chosenLayout = (RelativeLayout) view.findViewById(R.id.chosenLayout);
searchLayout = (RelativeLayout) view.findViewById(R.id.searchLayout);
localLayout = (RelativeLayout) view.findViewById(R.id.localLayout);
settingLayout = (RelativeLayout) view.findViewById(R.id.settingLayout);
}

/**
* 控件的监听事件
*/

private void initListener() {
homeLayout.setOnClickListener(this);
chosenLayout.setOnClickListener(this);
searchLayout.setOnClickListener(this);
localLayout.setOnClickListener(this);
settingLayout.setOnClickListener(this);
}

/**
* 控件的点击事件
* 点击后改变显示的图标和文字的颜色
* @param v
*/

@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.homeLayout:
setResidAndColor(0);
break;
case R.id.chosenLayout:
setResidAndColor(1);
break;
case R.id.searchLayout:
setResidAndColor(2);
break;
case R.id.localLayout:
setResidAndColor(3);
break;
case R.id.settingLayout:
setResidAndColor(4);
break;
}

//这里加入了一个接口方法,留给ViewPager去实现
//功能是点击item后viewPager也会跟着变
iCallbackListener.click(v.getId());
}

/**
* 设置Res和Color
* @param i
*/

public void setResidAndColor(int i) {
switch (i) {
case 0:
changeDataItem(setResid(new int[] {1, 0, 0, 0, 0}),
new int[] {1, 0, 0, 0, 0});
break;
case 1:
changeDataItem(setResid(new int[] {0, 1, 0, 0, 0}),
new int[] {0, 1, 0, 0, 0});
break;
case 2:
changeDataItem(setResid(new int[] {0, 0, 1, 0, 0}),
new int[] {0, 0, 1, 0, 0});
break;
case 3:
changeDataItem(setResid(new int[] {0, 0, 0, 1, 0}),
new int[] {0, 0, 0, 1, 0});
break;
case 4:
changeDataItem(setResid(new int[] {0, 0, 0, 0, 1}) ,
new int[] {0, 0, 0, 0, 1});
break;
}
}

/**
* 统一设置Res的地方,留有参数和返回值
* @param resid 数组,1表示选中,0表示未选中,导航页5个item都要进行判断
* @return 返回当前设置的Res,作为changeDataItem的参数
*/

public int[] setResid(int[] resid) {
int resHome = (resid[0] == 1) ? R.mipmap.image_tabbar_button_home_down : R.mipmap.image_tabbar_button_home;
int resChosen = (resid[1] == 1) ? R.mipmap.image_tabbar_button_chosen_down : R.mipmap.image_tabbar_button_chosen;
int resSearch = (resid[2] == 1) ? R.mipmap.image_tabbar_button_search_down : R.mipmap.image_tabbar_button_search;
int resLocal = (resid[3] == 1) ? R.mipmap.image_tabbar_button_local_down : R.mipmap.image_tabbar_button_local;
int resSetting = (resid[4] == 1) ? R.mipmap.image_tabbar_button_setting_down : R.mipmap.image_tabbar_button_setting;
return new int[] {resHome, resChosen, resSearch, resLocal, resSetting};
}

//初始化接口,由需要实现activity(MainActivity)调用
//通过findviewbyid获取MyBottomLayout,进行调用
public void setOnCallbackListener(ICallbackListener iCallbackListener) {
this.iCallbackListener = iCallbackListener;
}
//自定义接口文件,click方法由调用处实现,功能是完成viewpager的滑动
public interface ICallbackListener {
public void click(int id);
}
}

实现ViewPager和Fragment的滑动

创建fragment布局和fragment类

此处以home_fragment.xml和HomeFragment为例

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
home_fragment.xml
----------------------------------
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">

<TextView
android:layout_centerInParent="true"
android:text="首页"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</RelativeLayout>


HomeFragment
----------------------------------
/**
* 主页
* Created by yyg on 2016/4/22.
*/

public class HomeFragment extends Fragment {

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

@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
}
}

创建activity_main.xml

记得ViewPager和刚刚自定义的MyBottomLayout都要导入类全名

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"?>
<RelativeLayout
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"
tools:context=".activity.MainActivity">


<android.support.v4.view.ViewPager
android:id="@+id/myViewPager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@+id/myBottomLayout" />


<com.buaa.yyg.baidupager.view.MyBottomLayout
android:id="@+id/myBottomLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:background="@mipmap/image_titlebar_background" />


</RelativeLayout>

创建BaseActivity

这一步可以省略,不过为了代码的可复用性建议加上,方便功能的添加

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* Created by yyg on 2016/4/22.
*/

public abstract class BaseActivity extends AppCompatActivity implements View.OnClickListener{

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

initView();
initData();
initListener();
}

@Override
public void onClick(View v) {
progress();
}

public abstract void initView();
public abstract void initData();
public abstract void initListener();
public abstract void progress();
}

配置MainActivity

需要注意的是使用了butterknife,其他初始化数据方法都需要在其之后执行。
实现了前面的回调监听接口,通过ViewPager方法改变当前的item值,实现自定义view时点击导航item改变页面的功能。
ViewPager使用addOnPageChangeListener进行页面的滑动监听,实现功能滑动时导航item跟着变化。

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
public class MainActivity extends BaseActivity {

@Bind(R.id.myViewPager)
ViewPager myViewPager;
@Bind(R.id.myBottomLayout)
MyBottomLayout myBottomLayout;

@Override
protected void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
//super必须在最后,这样先setContentView然后super到
//BaseActivity的onCreate,之后才会调用initData等方法
//不这样会报空指针异常
super.onCreate(savedInstanceState);
}

@Override
public void initView() {
}

@Override
public void initData() {
myViewPager.setAdapter(new MyFragmentAdapter(getSupportFragmentManager()));
}

@Override
public void initListener() {
//设值注入,初始化MyBottomLayout页面的回调实例
myBottomLayout.setOnCallbackListener(new MyCallBackListener());

//ViewPager页面监听 使用add而不是set
myViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}

@Override
public void onPageSelected(int position) {
}

@Override
public void onPageScrollStateChanged(int state) {
//0是静止,1是正在滑动,2是停止滑动
if (state == 2) {
//设置滑动ViewPager导航同步变化
myBottomLayout.setResidAndColor(myViewPager.getCurrentItem());
}
}
});
}

/**
* 实现回调监听方法,用于改变当前item值
* 在FragmentPagerAdapter的getItem方法中切换Fragment
*/

private class MyCallBackListener implements MyBottomLayout.ICallbackListener {

@Override
public void click(int id) {
switch (id) {
case R.id.homeLayout:
myViewPager.setCurrentItem(0);
break;
case R.id.chosenLayout:
myViewPager.setCurrentItem(1);
break;
case R.id.searchLayout:
myViewPager.setCurrentItem(2);
break;
case R.id.localLayout:
myViewPager.setCurrentItem(3);
break;
case R.id.settingLayout:
myViewPager.setCurrentItem(4);
break;
}
}
}

/**
* viewPager的adapter,改变当前fragment
*/

private class MyFragmentAdapter extends FragmentPagerAdapter {

public MyFragmentAdapter(FragmentManager fm) {
super(fm);
}

@Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return new HomeFragment();
case 1:
return new ChosenFragment();
case 2:
return new SearchFragment();
case 3:
return new LocalFragment();
case 4:
return new SettingFragment();
}
return null;
}

@Override
public int getCount() {
//一共5个页面
return 5;
}
}

@Override
public void progress() {
}
}

Demo下载地址 使用ViewPager和Fragment实现底部导航滑动重构版