Android实习札记(7)---ViewPager+Fragment实例详解

在札记(5)中我们就说过要弄一个模仿微信页面切换的东东,就是ViewPager+Fragment实现的一个东西,札记(6)中也学习了一下ViewPager的一些基本用法,本节就来将两者结合以实现我们想要的效果!

1.ViewPager关于Fragment的说法?

先看下Google官网怎么说:

大概意思就是:

ViewPager更多的时候是与Fragment协同使用,这样可以更加方便地去创建Page和管理Page的生命周期,但是使用的不再是PageAdapter适配器,而是他的子类:FragmentPagerAdapter和FragmentStatePagerAdapter,他们提供了更加简单代码来构建我们的用户界面!

看完这段话,可能会有下面的疑问:

1)FragmentPagerAdapter和FragmentStatePagerAdapter有什么区别,用哪个?

答:前者适用于页面较少的情况,后者对应页面较多的情况,现在只能这样告诉你;貌似这个和他们缓存page的问题有关,以后再研究吧,通常用的都是FragmentPagerAdapter较多!

2)提供更加简单的代码…怎么简单法?

答:如官方文档所述,只需要实现getItem()与getCount()两个方法即可!相比起PagerAdapter要实现四个方法简单多了

2.ViewPager对应Fragment的适配器——FragmentPagerAdapter

必须要实现的两个方法:getItem( )与getCount( )

意思:返回与特定postion(下标)有关联的Fragment!

意思:返回有效的View的数目,就是View集合中的View的个数

3)代码实现微信页面切换效果:

什么都别说,先看下效果图:

上面的实现其实还是蛮简单的,核心就是ViewPager + Fragment来实现的,那么现在就来讲解下如何实现上面的效果

step 1:首先肯定是先弄我们的主布局文件啦:

一条装逼的顶部标题栏+可切换Page的ViewPager+底部导航栏

底部导航栏和札记6中的实现方法是一致的,就不讲了

activity_main.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
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
180
181
182
183
184
185
186
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/LinearLayout1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity" >

<RelativeLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="#22292C" >

<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="18dp"
android:text="微信(4)"
android:textColor="#ffffff"
android:textSize="18sp" />

<Button
android:id="@+id/btnadd"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginRight="15dp"
android:layout_centerVertical="true"
android:layout_alignBaseline="@+id/textView1"
android:layout_alignBottom="@+id/textView1"
android:layout_alignParentRight="true"
android:background="@drawable/ap9" />

<Button
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginRight="15dp"
android:layout_toLeftOf="@id/btnadd"
android:layout_centerVertical="true"
android:background="@drawable/alt"/>

</RelativeLayout>

<android.support.v4.view.ViewPager
android:id="@+id/vPager"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_gravity="center"
android:layout_weight="1.0"
android:background="#000000"
android:flipInterval="30"
android:persistentDrawingCache="animation" />

<ImageView
android:layout_width="match_parent"
android:layout_height="1dp"
android:src="@drawable/aoc"
/>

<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="55dp"
android:background="#FCFCFC" >

<RelativeLayout
android:id="@+id/weixin_layout"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" >

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:orientation="vertical" >

<ImageView
android:id="@+id/weixin_img"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_gravity="center_horizontal"
android:src="@drawable/ahk" />

<TextView
android:id="@+id/weixin_txt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="微信"
android:textColor="#999999" />
</LinearLayout>
</RelativeLayout>

<RelativeLayout
android:id="@+id/tongxunlu_layout"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" >

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:orientation="vertical" >

<ImageView
android:id="@+id/tongxunlu_img"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_gravity="center_horizontal"
android:src="@drawable/ahi" />

<TextView
android:id="@+id/tongxunlu_txt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="通讯录"
android:textColor="#999999" />
</LinearLayout>
</RelativeLayout>

<RelativeLayout
android:id="@+id/faxian_layout"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" >

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:orientation="vertical" >

<ImageView
android:id="@+id/faxian_img"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_gravity="center_horizontal"
android:src="@drawable/ahm" />

<TextView
android:id="@+id/faxian_txt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="发现"
android:textColor="#999999" />
</LinearLayout>
</RelativeLayout>

<RelativeLayout
android:id="@+id/me_layout"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" >

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:orientation="vertical" >


<ImageView
android:id="@+id/me_img"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_gravity="center_horizontal"
android:src="@drawable/aho" />

<TextView
android:id="@+id/me_txt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="我"
android:textColor="#999999" />
</LinearLayout>
</RelativeLayout>

</LinearLayout>

</LinearLayout>

step 2:主布局写完了,接着就写每个Fragment的布局和对应的Fragment类咯,这里每个Fragment就是一个简单的TextView + 不同的背景颜色,一式四份就可以了!

fg1.xml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?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:gravity="center"
android:background="#FAEBD7"
android:orientation="vertical" >

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="微信Fragment"
/>

</LinearLayout>

Fg1.java:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.jay.example.viewpagerfragment;  

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class Fg1 extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fg1, container,false);
return view;
}
}

step 3:接着就要自定义我们的FragmentPagerAdapter,这个也很简单,只要重写那两个基本方法就可以了,分别是getItem( )和getCount( )

MyFragmentPagerAdapter.java

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
package com.jay.example.viewpagerfragment;  

import java.util.ArrayList;

import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;

public class MyFragmentPageAadpter extends FragmentPagerAdapter {

private ArrayList<Fragment> fragmentsList;
public MyFragmentPageAadpter(FragmentManager fm) {super(fm);}
public MyFragmentPageAadpter(FragmentManager fm, ArrayList<Fragment> fragments) {
super(fm);
this.fragmentsList = fragments;
}


@Override
public Fragment getItem(int index) {
return fragmentsList.get(index);
}

@Override
public int getCount() {
return fragmentsList.size();
}

}

step 4:接着就到我们最后一步MainActivity的编写了,同样也是不复杂的,要做什么呢?

①实例化四个Fragment对象后,把他们放到View集合中,通过Adapter适配器与ViewPager进行绑定咯!然后就可以滑动ViewPager进行页面的切换了

②当我们点击底部导航条的按钮时,我们需要切换ViewPager中显示的Fragment,这怎么搞?

答:对点击的按钮的id进行判断,判断点击的是第几个,调用viewpager.setCurrentItem(index)即可

③当我们滑动页面时,底部导航条的图标也要跟着变换,这又怎么搞?

答这个也很简单,重写ViewPager的OnPageChangeListener的onPageScrollStateChanged()方法

当参数等于2时,说明此时滑动完毕,viewpager.getCurrentItem( )获得当前页面序号,从而设置第几个按钮处于选中状态!

MainActivity.java:

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
180
181
package com.jay.example.viewpagerfragment;  

import java.util.ArrayList;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;

import android.support.v4.view.ViewPager;
import android.support.v4.view.ViewPager.OnPageChangeListener;

import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;



public class MainActivity extends FragmentActivity {

//定义四个Fragment
private Fg1 fg1;
private Fg2 fg2;
private Fg3 fg3;
private Fg4 fg4;
//定义一个ViewPager容器
private ViewPager mPager;
private ArrayList<Fragment> fragmentsList;
private MyFragmentPageAadpter mAdapter;
//下面每个Layout对象
private RelativeLayout weixin_layout;
private RelativeLayout tongxunlu_layout;
private RelativeLayout faxian_layout;
private RelativeLayout me_layout;
//依次获得ImageView与TextView
private ImageView weixin_img;
private ImageView tongxunlu_img;
private ImageView faxian_img;
private ImageView me_img;
private TextView weixin_txt;
private TextView tongxunlu_txt;
private TextView faxian_txt;
private TextView me_txt;
//定义颜色值
private int Gray = 0xFF999999;
private int Green =0xFF45C01A;
//定义FragmentManager对象
public FragmentManager fManager;
//定义一个Onclick全局对象
public MyOnClick myclick;
public MyPageChangeListener myPageChange;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getActionBar().hide();
fManager = getSupportFragmentManager();
initViewPager();
initViews();
initState();
}

private void initViews() {
myclick = new MyOnClick();
myPageChange = new MyPageChangeListener();
mPager = (ViewPager) findViewById(R.id.vPager);
weixin_layout = (RelativeLayout) findViewById(R.id.weixin_layout);
tongxunlu_layout = (RelativeLayout) findViewById(R.id.tongxunlu_layout);
faxian_layout = (RelativeLayout) findViewById(R.id.faxian_layout);
me_layout = (RelativeLayout) findViewById(R.id.me_layout);
weixin_img = (ImageView) findViewById(R.id.weixin_img);
tongxunlu_img = (ImageView) findViewById(R.id.tongxunlu_img);
faxian_img = (ImageView) findViewById(R.id.faxian_img);
me_img = (ImageView) findViewById(R.id.me_img);
weixin_txt = (TextView) findViewById(R.id.weixin_txt);
tongxunlu_txt = (TextView) findViewById(R.id.tongxunlu_txt);
faxian_txt = (TextView) findViewById(R.id.faxian_txt);
me_txt = (TextView) findViewById(R.id.me_txt);
mPager.setAdapter(mAdapter);
mPager.setOnPageChangeListener(myPageChange);
weixin_layout.setOnClickListener(myclick);
tongxunlu_layout.setOnClickListener(myclick);
faxian_layout.setOnClickListener(myclick);
me_layout.setOnClickListener(myclick);
}

private void initViewPager()
{
fragmentsList = new ArrayList<Fragment>();
fg1 = new Fg1();
fg2 = new Fg2();
fg3 = new Fg3();
fg4 = new Fg4();
fragmentsList.add(fg1);
fragmentsList.add(fg2);
fragmentsList.add(fg3);
fragmentsList.add(fg4);
mAdapter = new MyFragmentPageAadpter(fManager,fragmentsList);
}

//定义一个设置初始状态的方法
private void initState()
{
weixin_img.setImageResource(R.drawable.ahj);
weixin_txt.setTextColor(Green);
mPager.setCurrentItem(0);
}

public class MyOnClick implements OnClickListener
{
@Override
public void onClick(View view) {
clearChioce();
iconChange(view.getId());
}
}

public class MyPageChangeListener implements OnPageChangeListener
{

@Override
public void onPageScrollStateChanged(int arg0)
{
if(arg0 == 2)
{
int i = mPager.getCurrentItem();
clearChioce();
iconChange(i);
}
}

@Override
public void onPageScrolled(int arg0, float arg1, int arg2) {}

@Override
public void onPageSelected(int index){}

}

//建立一个清空选中状态的方法
public void clearChioce()
{
weixin_img.setImageResource(R.drawable.ahk);
weixin_txt.setTextColor(Gray);
tongxunlu_img.setImageResource(R.drawable.ahi);
tongxunlu_txt.setTextColor(Gray);
faxian_img.setImageResource(R.drawable.ahm);
faxian_txt.setTextColor(Gray);
me_img.setImageResource(R.drawable.aho);
me_txt.setTextColor(Gray);
}

//定义一个底部导航栏图标变化的方法
public void iconChange(int num)
{
switch (num) {
case R.id.weixin_layout:case 0:
weixin_img.setImageResource(R.drawable.ahj);
weixin_txt.setTextColor(Green);
mPager.setCurrentItem(0);
break;
case R.id.tongxunlu_layout:case 1:
tongxunlu_img.setImageResource(R.drawable.ahh);
tongxunlu_txt.setTextColor(Green);
mPager.setCurrentItem(1);
break;
case R.id.faxian_layout:case 2:
faxian_img.setImageResource(R.drawable.ahl);
faxian_txt.setTextColor(Green);
mPager.setCurrentItem(2);
break;
case R.id.me_layout:case 3:
me_img.setImageResource(R.drawable.ahn);
me_txt.setTextColor(Green);
mPager.setCurrentItem(3);
break;
}
}
}

最后说几句:

代码还是比较简单的哈,没什么高大上的技术,只是希望可以帮到和我一样的初学者….

另外viewPager好像有缓冲page的功能

就是保存Page的状态,不过是相邻的两个,比如,你现在在2号页,此时1号和3号是缓存

在内存当中的,但是上面如果我们是1然后切换到4页面的话,会发现,2,3两个界面也加载了!

流程如下:

1 –>2(此时缓存1,2和3) –>3(此时缓存2,3和4) –>4(此时缓存3和4);

总结来说就是:ViewPager会缓存当前页面相邻的两个Page

不过好像有个是设置缓存页面数量的属性吧,一时半伙想不起来,如果有知道的读者欢迎指出,万分感激!
(使用ViewPagerAdapter可以用ViewPager的setOffscreenPageLimit来增加缓冲页。FragmentStatePagerAdapter有点类似ListView只缓冲当前页面)

本节代码下载:

http://pan.baidu.com/s/1dDcTNFj

扩展阅读:PagerAdapter和FragmentPagerAdapter和FragmentStatePagerAdapter区别


ViewPager与其中的子View滑动冲突该如何解决?

子view里使用这个方法,getParent().requestDisallowInterceptTouchEvent(true) 可以中断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
public class GalleryViewPager extends ViewPager {

/** the last x position */
private float lastX;

/** if the first swipe was from left to right (->), dont listen to swipes from the right */
private boolean slidingLeft;

/** if the first swipe was from right to left (<-), dont listen to swipes from the left */
private boolean slidingRight;

public GalleryViewPager(final Context context, final AttributeSet attrs) {
super(context, attrs);
}

public GalleryViewPager(final Context context) {
super(context);
}

@Override
public boolean onTouchEvent(final MotionEvent ev) {
final int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:

// Disallow parent ViewPager to intercept touch events.
this.getParent().requestDisallowInterceptTouchEvent(true);

// save the current x position
this.lastX = ev.getX();

break;

case MotionEvent.ACTION_UP:
// Allow parent ViewPager to intercept touch events.
this.getParent().requestDisallowInterceptTouchEvent(false);

// save the current x position
this.lastX = ev.getX();

// reset swipe actions
this.slidingLeft = false;
this.slidingRight = false;

break;

case MotionEvent.ACTION_MOVE:
/*
* if this is the first item, scrolling from left to
* right should navigate in the surrounding ViewPager
*/
if (this.getCurrentItem() == 0) {
// swiping from left to right (->)?
if (this.lastX <= ev.getX() && !this.slidingRight) {
// make the parent touch interception active -> parent pager can swipe
this.getParent().requestDisallowInterceptTouchEvent(false);
} else {
/*
* if the first swipe was from right to left, dont listen to swipes
* from left to right. this fixes glitches where the user first swipes
* right, then left and the scrolling state gets reset
*/
this.slidingRight = true;

// save the current x position
this.lastX = ev.getX();
this.getParent().requestDisallowInterceptTouchEvent(true);
}
} else
/*
* if this is the last item, scrolling from right to
* left should navigate in the surrounding ViewPager
*/
if (this.getCurrentItem() == this.getAdapter().getCount() - 1) {
// swiping from right to left (<-)?
if (this.lastX >= ev.getX() && !this.slidingLeft) {
// make the parent touch interception active -> parent pager can swipe
this.getParent().requestDisallowInterceptTouchEvent(false);
} else {
/*
* if the first swipe was from left to right, dont listen to swipes
* from right to left. this fixes glitches where the user first swipes
* left, then right and the scrolling state gets reset
*/
this.slidingLeft = true;

// save the current x position
this.lastX = ev.getX();
this.getParent().requestDisallowInterceptTouchEvent(true);
}
}

break;
}

super.onTouchEvent(ev);
return true;
}

}