感觉 Android 到处都是坑,每个地方都要把人折腾半天。
今天来简单说说 Android之ActionBar、Tabs、Fragment、ViewPager 实现标签页切换并缓存页面
关于他们的介绍就不多说了,网上到处都是,只说关键的部分:
我在开发的时候遇到几个疑难问题,花费大量时间处理,总结如下:
1. 关于 Fragment 内部逻辑处理该写在哪个事件回调部分?
2. ViewPager 页面切换动画卡顿,让我头疼了很久。
3. ViewPager 中如何保存 Fragment 当前视图的状态,让 Tabs 页面切换后不会重新加载,这地方很坑爹
4. ActionBar 中的 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
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
|
package com.ai9475.meitian.ui.fragment;
import android.app.Activity;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ListView;
import com.ai9475.meitian.R;
import com.ai9475.meitian.view.DiaryList;
import com.ai9475.util.ZLog;
/** *
* Created by ZHOUZ on 14-1-21.
*/
public class DiaryListFragment extends BaseFragment
{ private static final String TAG = "DiaryListFragment" ;
@Override
public void onAttach(Activity activity)
{
ZLog.i(TAG, "onAttach" );
super .onAttach(activity);
}
@Override
public void onCreate(Bundle savedInstanceState)
{
ZLog.i(TAG, "onCreate" );
super .onCreate(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
ZLog.i(TAG, "onCreateView" );
return inflater.inflate(R.layout.fragment_diary_list, container, false );
}
@Override
public void onActivityCreated(Bundle savedInstanceState)
{
ZLog.i(TAG, "onActivityCreated" );
super .onActivityCreated(savedInstanceState);
ZLog.i(TAG, "DiaryList0" );
DiaryList diaryList = new DiaryList(
getActivity().getApplicationContext(),
(ListView) getView().findViewById(R.id.diaryListCt)
);
ZLog.i(TAG, "DiaryList load0" );
this .setRetainInstance( true );
}
@Override
public void onStart()
{
ZLog.i(TAG, "onStart" );
super .onStart();
}
@Override
public void onResume()
{
ZLog.i(TAG, "onResume" );
super .onResume();
}
@Override
public void onPause()
{
ZLog.i(TAG, "onPause" );
super .onPause();
}
@Override
public void onStop()
{
ZLog.i(TAG, "onStop" );
super .onStop();
}
@Override
public void onDestroyView()
{
ZLog.i(TAG, "onDestroyView" );
super .onDestroyView();
}
@Override
public void onDestroy()
{
ZLog.i(TAG, "onDestroy" );
super .onDestroy();
}
@Override
public void onDetach()
{
ZLog.i(TAG, "onDetach" );
super .onDetach();
}
} |
上面的类中的 on 事件就是Fragment主要处理的时间回调,注意复写父类方法时要回调执行父类同名方法,否则会出错
主要复写 onCreateView 方法,返回该 Fragment 所对应的视图对象,这里可以在返回视图对象前进行一些简单的配置,但千万不要写太耗时的处理阻塞UI主线程。
另外 onActivityCreated 方法,是当 activity 的 onCreate 事件结束时的回调,此时当前的Fragment对应的view已经并入到整个布局中,此时可以使用 getView() 方法获取视图对象。
其他几个事件没什么太多可说,有些我也还不是太清楚,还有些动画调用的事件。
二、切换页面卡顿问题
这个问题的产生主要可能是两方面,
1. 没有使用 ViewPager 的缓存,每次切换都重新加载。
2. 加载 Fragment 内部有耗时耗资源的逻辑处理。
这里主要说下第二种情况,我一开始没处理掉 缓存问题时有一个解决办法,
1
2
3
4
5
6
|
< android.support.v4.view.ViewPager
android:id = "@+id/tabsViewPager"
android:layout_width = "match_parent"
android:layout_height = "match_parent"
>
</ android.support.v4.view.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
|
mViewPager = (ViewPager) findViewById(R.id.tabsViewPager); mViewPager.setOnPageChangeListener( new ViewPager.SimpleOnPageChangeListener() {
private static final String TAG = "ViewPager.SimpleOnPageChangeListener" ;
private ArrayList hasLoadedPages = new ArrayList<Integer>();
@Override
public void onPageSelected( int position) {
ZLog.i(TAG, "onPageSelected position:" + position);
getSupportActionBar().setSelectedNavigationItem(position);
}
@Override
public void onPageScrolled( int position, float positionOffset, int positionOffsetPixels)
{
ZLog.i(TAG, "onPageScrolled position: " + position + ", positionOffset:" + positionOffset + ", positionOffsetPixels:" + positionOffsetPixels);
}
@Override
public void onPageScrollStateChanged( int state) {
ZLog.i(TAG, "onPageScrollStateChanged" );
int position = mViewPager.getCurrentItem();
switch (state) {
// 正在拖动
case ViewPager.SCROLL_STATE_DRAGGING :
ZLog.i(TAG, "ViewPager.SCROLL_STATE_DRAGGING position:" + position);
break ;
// 拖动释放后正在沉降的过程
case ViewPager.SCROLL_STATE_SETTLING :
ZLog.i(TAG, "ViewPager.SCROLL_STATE_SETTLING position:" + position);
break ;
// 切换动画全部完成结束
case ViewPager.SCROLL_STATE_IDLE :
ZLog.i(TAG, "ViewPager.SCROLL_STATE_IDLE position:" + position);
// 已加载过则不再加载
if (hasLoadedPages.contains(position)) break ;
Fragment fragment = mPager.getFragments().get(position);
runCallback(position, fragment);
hasLoadedPages.add(position);
break ;
}
}
public void runCallback( int position, Fragment fragment) {
ZLog.i(TAG, "runCallback" );
DiaryList diaryList;
switch (position) {
case 0 :
ZLog.i(TAG, "DiaryList0" );
diaryList = new DiaryList(
getApplicationContext(),
(ListView) fragment.getView().findViewById(R.id.diaryListCt)
);
ZLog.i(TAG, "DiaryList load0" );
break ;
case 1 :
ZLog.i(TAG, "DiaryList1" );
diaryList = new DiaryList(
getApplicationContext(),
(ListView) fragment.getView().findViewById(R.id.diaryListCt)
);
ZLog.i(TAG, "DiaryList load1" );
break ;
case 2 :
ZLog.i(TAG, "DiaryList2" );
diaryList = new DiaryList(
getApplicationContext(),
(ListView) fragment.getView().findViewById(R.id.diaryListCt)
);
ZLog.i(TAG, "DiaryList load2" );
break ;
}
}
}
|
这里主要用到 public void onPageScrollStateChanged(int state) 页面滚动切换状态变化的事件监听
当滚动动画完全结束 case ViewPager.SCROLL_STATE_IDLE 时再执行 Fragment 的逻辑处理,这样动画就会流畅了。
但经过后来的测试发现有个很简单的解决方案,就是下面要说到的 ViewPager 的缓存功能,其实很简单。
三、缓存 Tabs 页面切换不重新加载数据
我在这地方折腾的最久,而且很多时候无从下手的感觉,网上搜索了很多文章都有说道保存 Fragment 数据和状态,但是没有整整提到如何来保存他的当前view状态,不知道如何保存,当然实际上我还是没搞懂如何仅仅缓存 Fragment 的状态,但对于 ViewPager 缓存 Tab 对应的 Fragment 还是找到了办法,之前花了很大功夫来自己实现,后来偶然发现他居然有个自带的方法
1
2
|
// 设置缓存多少个 Tab对应的 fragment mViewPager.setOffscreenPageLimit(6);
|
我测试时用了 6个 listView 加载图片列表数据,切换动画也没有任何卡顿现象,非常流畅,就这么简单一句就搞定了。
配置该项后,ViewPager在切换时将不会清理不可见的 Fragment,不会触发 Fragment 的任何事件,因此也就不会导致其重新加载。
四、ActionBar 中的 Tabs
这个其实不用操作,在tabs数量超过一屏后,例如我现在设置6个宽度超过了屏幕则会变成可以横向滚动的状态,而不需要自己实现,之前不知道在这方面查了很多资料都无果,只知道可以用 tabhost 可以实现,但在ActionBar 里面却又用不了,自己试了下方多个tab才发现原来会自动实现,无语。
还是贴上 MainActivity.class 完整代码:
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
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
|
package com.ai9475.meitian.ui;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.view.ViewPager;
import android.support.v7.app.ActionBar;
import android.view.Menu;
import android.widget.ListView;
import com.ai9475.meitian.AppManager;
import com.ai9475.meitian.R;
import com.ai9475.meitian.ui.fragment.DiaryListFragment;
import com.ai9475.meitian.ui.fragment.Test2Fragment;
import com.ai9475.meitian.ui.fragment.Test3Fragment;
import com.ai9475.meitian.view.DiaryList;
import com.ai9475.util.ZLog;
import java.util.ArrayList;
public class MainActivity extends BaseActivity
{ private static final String TAG = "MainActivity" ;
private MyTabsPagerAdapter mPager;
private ViewPager mViewPager;
@Override
protected void onCreate(Bundle savedInstanceState)
{
ZLog.i(TAG, "start" );
// 执行父级初始化方法
super .onCreate(savedInstanceState);
//requestWindowFeature(Window.FEATURE_NO_TITLE);
ZLog.i(TAG, "setContentView" );
setContentView(R.layout.activity_main);
// 滑动页面视图配置
ZLog.i(TAG, "MyTabsPagerAdapter start" );
mPager = new MyTabsPagerAdapter(getSupportFragmentManager());
mPager.getFragments().add( new DiaryListFragment());
mPager.getFragments().add( new Test2Fragment());
mPager.getFragments().add( new Test3Fragment());
mPager.getFragments().add( new Test3Fragment());
mPager.getFragments().add( new Test3Fragment());
mPager.getFragments().add( new Test3Fragment());
// 滑动分页容器
mViewPager = (ViewPager) findViewById(R.id.tabsViewPager);
// 设置缓存多少个 fragment
mViewPager.setOffscreenPageLimit( 6 );
mViewPager.setAdapter(mPager);
// 页面滑动事件
mViewPager.setOnPageChangeListener(
new ViewPager.SimpleOnPageChangeListener() {
private static final String TAG = "ViewPager.SimpleOnPageChangeListener" ;
private ArrayList hasLoadedPages = new ArrayList<Integer>();
@Override
public void onPageSelected( int position) {
ZLog.i(TAG, "onPageSelected position:" + position);
getSupportActionBar().setSelectedNavigationItem(position);
}
@Override
public void onPageScrolled( int position, float positionOffset, int positionOffsetPixels)
{
ZLog.i(TAG, "onPageScrolled position: " + position + ", positionOffset:" + positionOffset + ", positionOffsetPixels:" + positionOffsetPixels);
}
@Override
public void onPageScrollStateChanged( int state) {
ZLog.i(TAG, "onPageScrollStateChanged" );
int position = mViewPager.getCurrentItem();
switch (state) {
// 正在拖动
case ViewPager.SCROLL_STATE_DRAGGING :
ZLog.i(TAG, "ViewPager.SCROLL_STATE_DRAGGING position:" + position);
break ;
// 拖动释放后正在沉降的过程
case ViewPager.SCROLL_STATE_SETTLING :
ZLog.i(TAG, "ViewPager.SCROLL_STATE_SETTLING position:" + position);
break ;
// 切换动画全部完成结束
case ViewPager.SCROLL_STATE_IDLE :
ZLog.i(TAG, "ViewPager.SCROLL_STATE_IDLE position:" + position);
/*if (hasLoadedPages.contains(position)) break;
Fragment fragment = mPager.getFragments().get(position);
runCallback(position, fragment);
hasLoadedPages.add(position);*/
break ;
}
}
public void runCallback( int position, Fragment fragment) {
ZLog.i(TAG, "runCallback" );
DiaryList diaryList;
switch (position) {
case 0 :
ZLog.i(TAG, "DiaryList0" );
diaryList = new DiaryList(
getApplicationContext(),
(ListView) fragment.getView().findViewById(R.id.diaryListCt)
);
ZLog.i(TAG, "DiaryList load0" );
break ;
case 1 :
ZLog.i(TAG, "DiaryList1" );
diaryList = new DiaryList(
getApplicationContext(),
(ListView) fragment.getView().findViewById(R.id.diaryListCt)
);
ZLog.i(TAG, "DiaryList load1" );
break ;
case 2 :
ZLog.i(TAG, "DiaryList2" );
diaryList = new DiaryList(
getApplicationContext(),
(ListView) fragment.getView().findViewById(R.id.diaryListCt)
);
ZLog.i(TAG, "DiaryList load2" );
break ;
}
}
}
);
ActionBar actionBar = getSupportActionBar();
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
// Tab 页面切换
MyTabListener listener = new MyTabListener();
// 默认的首页 tab
ActionBar.Tab indexTab = actionBar.newTab()
.setText(getString(R.string.tab_index))
.setTabListener(listener);
actionBar.addTab(indexTab);
actionBar.addTab(actionBar.newTab()
.setText(getString(R.string.tab_hot))
.setTabListener(listener)
);
actionBar.addTab(actionBar.newTab()
.setText(getString(R.string.tab_tag))
.setTabListener(listener)
);
actionBar.addTab(actionBar.newTab()
.setText(getString(R.string.tab_tag))
.setTabListener(listener)
);
actionBar.addTab(actionBar.newTab()
.setText(getString(R.string.tab_tag))
.setTabListener(listener)
);
actionBar.addTab(actionBar.newTab()
.setText(getString(R.string.tab_tag))
.setTabListener(listener)
);
// 显示首页
indexTab.select();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true ;
}
private class MyTabListener implements ActionBar.TabListener
{
@Override
public void onTabSelected(ActionBar.Tab tab, FragmentTransaction ft)
{
int position = tab.getPosition();
ZLog.i(TAG, "tab selected: " + position);
// 数据通信
/*Bundle bundle = new Bundle();
Fragment fragment = mPager.getItem(tab.getPosition());
Toast.makeText(getApplicationContext(), "position:"+ tab.getPosition(), Toast.LENGTH_SHORT).show();
fragment.setArguments(bundle);
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.add(R.id.fragmentContainer, fragment);
fragmentTransaction.commit();*/
mViewPager.setCurrentItem(position);
}
@Override
public void onTabReselected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
ZLog.i(TAG, "tab reselected: " + tab.getPosition());
}
@Override
public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction ft)
{
ZLog.i(TAG, "tab unselected: " + tab.getPosition());
}
};
public class MyTabsPagerAdapter extends FragmentPagerAdapter
{
private ArrayList<Fragment> mFragments = new ArrayList<Fragment>();
public MyTabsPagerAdapter(FragmentManager fm) {
super (fm);
}
public ArrayList<Fragment> getFragments() {
return this .mFragments;
}
@Override
public Fragment getItem( int i) {
return this .mFragments.get(i);
}
@Override
public int getCount() {
return this .mFragments.size();
}
}
} |
相关推荐
ActionBar和Fragment结合ViewPager实现TabActivity
Android 使用Fragment,ActionBar 实现tab标签切换页面的一个简单样例
ActionBar+Fragment+ViewPager三者的使用
android actionbar切换fragment demo
Fragment 和Viewpager,ActionBar运用
Fragment,ViewPager+actionBar运用
actionbar+fragment+viewpager做出界面的效果
很好地实现了actionbar的分页滑动效果,可以滑动,用Tab作为页标签稳定实用
1.几个fragment的使用,代码来自于Demo程序,可用于练习fragment 2.使用了viewpager,参照android4的通讯录代码实现,可用于练习viewPager的使用 3.里面通过getActionBar设置了tab,并去掉默认标题栏
压缩包中有android-support-v7-appcompat,具体使用见http://blog.csdn.net/cs742611497/article/details/21003359
主要介绍了如何灵活使用Android中ActionBar和ViewPager切换页面,感兴趣的小伙伴们可以参考一下
ActionBar.Tab 与 ViewPager关联使用,支持点击tab切换页面和滑动切换页面。
Fragment ViewPager actionBar FragmentPagerAdapter ActionBar.Tab
主要介绍了Android 中ActionBar+fragment实现页面导航的实例的相关资料,希望通过本文能帮助到大家实现这样的功能,需要的朋友可以参考下
ActionBar用Tab+ViewPager+Fragment实现快速导航,
自己做了一个计数的小demo,整合了ActionBar、ViewPager、Fragment、Observable,多用用熟练一下
本文介绍ActionBar与Fragment结合使用的一个实例,ActionBar是一个标识应用程序和用户位置的窗口功能,并且给用户提供操作和导航模式。
ViewPager、ActionBar和Fragment共同实现的例子
这个例子是通过结合ActionBarSherlock与Fragment及ViewPager来实现滑动切换界面的效果,例子比较简单,适合初学者。
android ActionBarTabs+ViewPager+Fragment 实现tab滑动翻页效果,完整代码,多个例子,