项目中经常会使用Tab方式进行导航、页面切换,以前采用了自定义Tab加Fragment的方式实现,但是实际项目应用中发现了几个问题:
- 自定义Tab比较繁琐,Tab位置固定,不能在横竖屏切换的时候进行适当调整
- 每个Tab页作为一个Fragment,Frament内部的View切换/后退必须要自己管理
- Fragment相互切换时采用hide/show方式才能保持页面状态,这样可能会造成内存紧张
新的解决方案:
- ActionBar是在android 3.0版本中出现的,放在屏幕上方,可以在上面设置标题、logo、添加MenuItem、添加Tab、也可以添加自定义的View,而且在横竖屏切换的时候会自动调整Tab的位置,通过在Activity中设置android:uiOptions=”splitActionBarWhenNarrow” 当纵向ActionBar空间不足时会自动将MenuItem分开放到屏幕下方。所以这里用ActionBar的Tab代替自定义的Tab
- 在android 4.2版本中Fragment进行了改进(通过support包可以兼容到2.3),Fragment中可以嵌套子Fragment。这样就可以很方便的通过Fragment的stack来管理后退顺序问题。这里利用Fragment嵌套来代替以前的View
实现效果:
横屏首页
竖屏首页
竖屏栈测试
竖屏栈测试点击进入下层fragment(切换后Tab后,状态不变)
实现思路:
代码实现:
引入android-support-v4.jar。由于ActionBar并没有在补充的support包中,所以最低只能兼容到3.0版本。
FragmentActivity实现,主要是ActionBar的初始化、最外层ParentFragment初始化以及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
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
|
package com.example.actionbardemo;
import android.app.ActionBar;
import android.app.ActionBar.Tab;
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.app.FragmentTransaction;
import android.view.Menu;
import android.view.MenuInflater;
import android.widget.Toast;
public class FragmentNestingTabs extends FragmentActivity {
GlobalFragment mCurrentFragment;
@Override
protected void onCreate(Bundle savedInstanceState) {
FragmentManager.enableDebugLogging(true);
super.onCreate(savedInstanceState);
final ActionBar bar = getActionBar();
bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);//设置ActionBar为Tab导航模式
bar.setDisplayOptions(0, ActionBar.DISPLAY_SHOW_TITLE);//设置标题不可见
bar.addTab(bar.newTab()
.setText("首页")
.setTabListener(new TabListener<HomePageFragment>(
this, "menus", HomePageFragment.class)));//添加首页标签
bar.addTab(bar.newTab()
.setText("栈测试")
.setTabListener(new TabListener<FragmentStackFragment>(
this, "stack", FragmentStackFragment.class)));//添加栈测试标签
/**
* 如果没有保存过状态,那么就默认切换到第一个标签
*/
if (savedInstanceState != null) {
bar.setSelectedNavigationItem(savedInstanceState.getInt("tab", 0));
}
}
/**
* 添加actionbar菜单,
* 通过 android:showAsAction 设置是否放在ActionBar上,以及在没用空间时是否显示 ifRoom/never/always
*/
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.actions, menu);
return true;
}
/**
* 在横竖屏切换时保持tab页
*/
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt("tab", getActionBar().getSelectedNavigationIndex());
}
public class TabListener<T extends Fragment> implements ActionBar.TabListener {
private final FragmentActivity mActivity;
private final String mTag;
private final Class<T> mClass;
private final Bundle mArgs;
private Fragment mFragment;
public TabListener(FragmentActivity activity, String tag, Class<T> clz) {
this(activity, tag, clz, null);
}
public TabListener(FragmentActivity activity, String tag, Class<T> clz, Bundle args) {
mActivity = activity;
mTag = tag;
mClass = clz;
mArgs = args;
mFragment = mActivity.getSupportFragmentManager().findFragmentByTag(mTag);
if (mFragment != null && !mFragment.isDetached()) {
FragmentTransaction ft = mActivity.getSupportFragmentManager().beginTransaction();
ft.detach(mFragment);
ft.commit();
}
}
@Override
public void onTabReselected(Tab tab, android.app.FragmentTransaction fts) {
Toast.makeText(mActivity, "再次选中!", Toast.LENGTH_SHORT).show();
}
/**
* tab选中事件
*/
@Override
public void onTabSelected(Tab tab, android.app.FragmentTransaction fts) {
FragmentTransaction ft = mActivity.getSupportFragmentManager().beginTransaction();
if (mFragment == null) {//如果为空就新初始化
mFragment = (GlobalFragment) Fragment.instantiate(mActivity, mClass.getName(), mArgs);
ft.add(android.R.id.content, mFragment, mTag);
} else {//否则就直接放入activity
ft.attach(mFragment);
}
mCurrentFragment = (GlobalFragment) mFragment;
ft.commit();
}
/**
* tab未选中(离开)事件
*/
@Override
public void onTabUnselected(Tab tab, android.app.FragmentTransaction fts) {
FragmentTransaction ft = mActivity.getSupportFragmentManager().beginTransaction();
if (mFragment != null) {
ft.detach(mFragment);//从activity中移除
ft.commit();
}
}
}
/**
* back事件交给fragment处理
*/
@Override
public void onBackPressed() {
mCurrentFragment.onBackPressed();
}
}
|
接下来要进行最外层父Fragment的实现,这里让所有的Fragment继承GlobalFragment (它继承自Fragment),这样方便Fragment公共内容的管理,比如后退事件管理,甚至在项目后期加入数据统计的sdk。只需要在GlobalFragment 中实现就可以了,节省和很多工作量而且方便管理。
先看GlobalFragment,代码很简单,一个空的OnBackPressed方法,目的是将FragmentActivity中的后退事件分发到外层Fragment,因为外层Fragment中的子Fragment是响应不到系统OnBackPressed事件的。
1
2
3
4
5
6
7
8
|
package com.example.actionbardemo;
import android.support.v4.app.Fragment;
public class GlobalFragment extends Fragment{
public void onBackPressed() {
}
}
|
下面看“栈测试”标签的外层FragmentStackFragment,主要要注意的地方是这里的FragmentManager 是 ChildFragmentManager、还有saveInstanceState的使用,以及OnBackPressed的重写来管理子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
|
package com.example.actionbardemo;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class FragmentStackFragment extends GlobalFragment {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState == null) {//如果没有保存状态那么就add HomePageFragment1 到FrameLayout
Fragment newFragment = new HomePageFragment1();
FragmentTransaction ft = this.getChildFragmentManager().beginTransaction();
ft.add(R.id.simple_fragment, newFragment).commit();
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_stack, container, false);
return v;
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
}
/**
* 返回事件,这里要进行子Fragment的后退操作,popBackStack。
*/
@Override
public void onBackPressed() {
if(getChildFragmentManager().getBackStackEntryCount()>0){//如果栈内存在内容那么就pop出栈
getChildFragmentManager().popBackStack();
}else{//否则就说明到了最上层,那么就退出
this.getActivity().finish();
}
}
}
|
第一个子Fragment,HomePageFragment1。在这里得到的FragmentManager其实就是上面的父Fragment -FragmentStackFragment中的getChildFragmentManager。在这个页面通过点击”进入下一层”按钮切换Fragment到HomePageFragment2
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
|
package com.example.actionbardemo;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.Button;
public class HomePageFragment1 extends GlobalFragment {
private View mainView;
private Button btnNext;
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
btnNext = (Button) mainView.findViewById(R.id.btnNext);
/**
* 点击进入下一层
*/
btnNext.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
FragmentTransaction ft = HomePageFragment1.this.getFragmentManager().beginTransaction();
Fragment f = new HomePageFragment2();
ft.replace(R.id.simple_fragment, f, "homepage2");
ft.addToBackStack(null);//加入后退栈
ft.commit();
}
});
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
mainView = inflater.inflate(R.layout.homepage1, container, false);
return mainView;
}
}
|
最后来看HomePageFragment2
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
|
package com.example.actionbardemo;
import java.util.ArrayList;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ListView;
public class HomePageFragment2 extends GlobalFragment {
private View mainView;
private ListView listview;
private DataAdapter dataAdapter ;
int currentIndex ;
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
listview = (ListView) mainView.findViewById(R.id.listview);
dataAdapter = new DataAdapter(HomePageFragment2.this.getActivity(),new ArrayList<String>());
listview.setAdapter(dataAdapter);
if(savedInstanceState!=null){//获得listview上次的位置
currentIndex = savedInstanceState.getInt("currentIndex");
}
//listview初始化为上次位置,避免横竖屏切换的时候listview初始化到位置0
listview.setSelectionFromTop(currentIndex, 0);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
mainView = inflater.inflate(R.layout.homepage2, container, false);
return mainView;
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if(listview!=null){//保存页面状态变化前的listview 位置
outState.putInt("currentIndex", listview.getFirstVisiblePosition());
}
}
}
|
没有评论:
发表评论