Liping Zou bio photo

Liping Zou

An Android Developer

Email Twitter Instagram Github Stackoverflow

Overview

###预备知识

嵌套Tab在Android应用中用途广泛,之前做过的一些东西都是运用了TabActivity。但是由于在Android Developers中说到了“TabActivity was deprecated in API level 13.” ,并且建议大家使用Fragment。所以学习了嵌套Fragment的使用,参考了这个博客中的相关思路和代码。

在Android Developers中对于Fragment中文版看这里)的描述:A Fragment represents a behaviors or a portion of user interface in an Activity. You can combine multiple fragments in a single activity to build a multi-pane UI and reuse a fragment in multiple activities. You can think of a fragment as a modular section of an activity, which has its own lifecycle, receives its own input events, and which you can add or remove while the activity is running.

简单来说,Fragment就是被嵌入在Activity中用来表现UI的一个可模块化和可重用的组件。Fragment在大屏幕设备中应用可以非常广泛,开发者可以通过自己巧妙的设计让UI更加灵活和美观。

###创建Fragment

创建Fragment,必须创建一个Fragment的子类。

创建一个自己的Fragment,需要创建一个Fragment的子类。Fragment的生命周期和Activity的生命周期类似,它包含了很多与Activity类似的回调函数。

1
public void onCreate (Bundle savedInstanceState)

创建fragment的时候调用onCreate()方法

1
public View onCreateView (LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) 

在第一次绘制UI的时候系统调用该方法,为了绘制UI,返回一个fragment布局的根View。

###将Fragment添加到Activity的方法

  • 在layout文件中声明fragment
1
2
3
4
5
6
7
  <FrameLayout
        android:id="@+id/fragment_container"
        android:layout_width="fill_parent"
        android:layout_height="0dip"
        android:layout_weight="1.0"
        android:background="#fffab3" >
    </FrameLayout>
  • 将一个fragment添加到viewgroup中,使用FragmentTransaction添加、替换或者删除fragment
1
2
3
4
5
private void addFragmentToStack(Fragment fragment) {
		FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
		ft.replace(R.id.fragment_container, fragment);
		ft.commit();
	}

###Fragment生命周期

image

###异常分析

关于解决 java.lang.IllegalStateException The specified child already has a parent. You must call removeView()的方法

image

在运行调试的时候会发现,在第二次点击一个相同的tab的时候,会出现上述异常。

这个异常说的是,这个特定的child view已经存在一个parent view了,必须让parent view调用removeView()方法。

运行顺序:点击tab1,点击tab2,再点击tab1。

具体fragment的生命周期是:

image

对上图进行分析,可以发现,出问题的是viewpager中的view。当切换不同的viewpager(即fragment,每个fragment中装载了一个viewpager)时,调用了startActivity()方法的时候,传入了相同的id,会返回相同的对象。而当我们在第二次调用的时候,传入了相同的id是复用了原来的view,这就导致了view被指定多个parent view。

所以解决办法就是,在使用这个view之前首先判断其是否存在parent view,这调用getParent()方法可以实现。如果存在parent view,那么就调用removeAllViewsInLayout()方法。代码如下:

1
2
3
4
5
6
for (View view : viewList) {
	ViewGroup p = (ViewGroup) view.getParent();
	if (p != null) {
		p.removeAllViewsInLayout();
	}
}

###结果展示

完成之后在模拟器中运行的效果如图:

image