预备知识
嵌套Tab在Android应用中用途广泛,之前做过的一些东西都是运用了TabActivity。但是由于在Android Developers中说到了“TabActivity was deprecated in API level 13." ,并且建议大家使用Fragment。所以学习了嵌套Fragment的使用,参考了这个博客中的相关思路和代码。
创建Fragment
创建一个自己的Fragment,需要创建一个Fragment的子类。Fragment的生命周期和Activity的生命周期类似,它包含了很多与Activity类似的回调函数。
public void onCreate (Bundle savedInstanceState)
创建fragment的时候调用onCreate()方法
public View onCreateView (LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
在第一次绘制UI的时候系统调用该方法,为了绘制UI,返回一个fragment布局的根View。
将Fragment添加到Activity的方法
1.在layout文件中声明fragment
<FrameLayout
android:id="@+id/fragment_container"
android:layout_width="fill_parent"
android:layout_height="0dip"
android:layout_weight="1.0"
android:background="#fffab3" >
</FrameLayout>
2.将一个fragment添加到viewgroup中,使用FragmentTransaction添加、替换或者删除fragment。
private void addFragmentToStack(Fragment fragment) {
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.replace(R.id.fragment_container, fragment);
ft.commit();
}
Fragment生命周期
异常分析
关于解决 java.lang.IllegalStateException The specified child already has a parent. You must call removeView()的方法
在运行调试的时候会发现,在第二次点击一个相同的tab的时候,会出现上述异常。
这个异常说的是,这个特定的child view已经存在一个parent view了,必须让parent view调用removeView()方法。
经过对fragment的生命周期的分析
运行顺序:点击tab1,点击tab2,再点击tab1.
具体fragment的生命周期是:
对上图进行分析,可以发现,出问题的是viewpager中的view。当切换不同的viewpager(即fragment,每个fragment中装载了一个viewpager)时,调用了startActivity()方法的时候,传入了相同的id,会返回相同的对象。而当我们在第二次调用的时候,传入了相同的id是复用了原来的view,这就导致了view被指定多个parent view。
所以解决办法就是,在使用这个view之前首先判断其是否存在parent view,这调用getParent()方法可以实现。如果存在parent view,那么就调用removeAllViewsInLayout()方法。代码如下:
for (View view : viewList) {
ViewGroup p = (ViewGroup) view.getParent();
if (p != null) {
p.removeAllViewsInLayout();
}
}
结果展示
主要代码
TestFragmentActivity.java
package com.test;
import java.util.ArrayList;
import android.annotation.SuppressLint;
import android.app.LocalActivityManager;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.LinearLayout;
/**
* 嵌套Fragment的使用
*
* @author zouliping
*
*/
public class TestFragmentActivity extends FragmentActivity {
private LocalActivityManager manager;
private ArrayList<View> list1 = new ArrayList<View>();
private ArrayList<View> list2 = new ArrayList<View>();
private ArrayList<View> list3 = new ArrayList<View>();
private Fragment[] fragments;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.test_fragments);
manager = new LocalActivityManager(this, false);
manager.dispatchCreate(savedInstanceState);
initViews();
}
/**
* 初始化Views
*/
private void initViews() {
findViewById(R.id.tv1).setOnClickListener(listener);
findViewById(R.id.tv2).setOnClickListener(listener);
findViewById(R.id.tv3).setOnClickListener(listener);
fragments = new FragmentParent[3];
fragments[0] = FragmentParent.newInstance(list1, new String[] {
"page1_1", "page1_2", "page1_3" });
fragments[1] = FragmentParent.newInstance(list2, new String[] {
"page2_1", "page2_2", "page2_3" });
fragments[2] = FragmentParent.newInstance(list3, new String[] {
"page3_1", "page3_2", "page3_3" });
initPager();
findViewById(R.id.tv1).performClick();
}
/**
* 获取view
*
* @param id
* @param intent
* @return
*/
private View getView(String id, Intent intent) {
return manager.startActivity(id, intent).getDecorView();
}
/**
* 根据parent position初始化viewPager
*/
private void initPager() {
Intent intent;
// tab1
intent = new Intent(TestFragmentActivity.this, Test1Activity.class);
list1.add(getView("tab1_1", intent));
intent = new Intent(TestFragmentActivity.this, Test2Activity.class);
list1.add(getView("tab1_2", intent));
intent = new Intent(TestFragmentActivity.this, Test1Activity.class);
list1.add(getView("tab1_3", intent));
// tab2
intent = new Intent(TestFragmentActivity.this, Test1Activity.class);
list2.add(getView("tab2_1", intent));
intent = new Intent(TestFragmentActivity.this, Test2Activity.class);
list2.add(getView("tab2_2", intent));
intent = new Intent(TestFragmentActivity.this, Test1Activity.class);
list2.add(getView("tab2_3", intent));
// tab3
intent = new Intent(TestFragmentActivity.this, Test1Activity.class);
list3.add(getView("tab2_1", intent));
intent = new Intent(TestFragmentActivity.this, Test2Activity.class);
list3.add(getView("tab2_2", intent));
intent = new Intent(TestFragmentActivity.this, Test1Activity.class);
list3.add(getView("tab2_3", intent));
}
private OnClickListener listener = new OnClickListener() {
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.tv1:
addFragmentToStack(fragments[0]);
break;
case R.id.tv2:
addFragmentToStack(fragments[1]);
break;
case R.id.tv3:
addFragmentToStack(fragments[2]);
break;
}
}
};
private void addFragmentToStack(Fragment fragment) {
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.replace(R.id.fragment_container, fragment);
ft.commit();
}
/**
* 嵌套Fragment
*
*/
@SuppressLint("ValidFragment")
public final static class FragmentParent extends Fragment {
/**
* 工厂方法,返回一个新的FragmentParent的实例
*
* @param list
* @param str
* @return
*/
public static final FragmentParent newInstance(ArrayList<View> list,
String[] str) {
FragmentParent framentParent = new FragmentParent();
Bundle bundle = new Bundle();
bundle.putSerializable("pager_view_list", list);
bundle.putStringArray("pager_title_ary", str);
framentParent.setArguments(bundle);
return framentParent;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Log.d("test fragment", "fragment create view");
LinearLayout convertView = (LinearLayout) inflater.inflate(
R.layout.viewpager_fragments, container, false);
ViewPager pager = (ViewPager) convertView.findViewById(R.id.pager);
@SuppressWarnings("unchecked")
final ArrayList<View> viewList = (ArrayList<View>) getArguments()
.getSerializable("pager_view_list");
final String[] titles = getArguments().getStringArray(
"pager_title_ary");
for (View view : viewList) {
ViewGroup p = (ViewGroup) view.getParent();
if (p != null) {
p.removeAllViewsInLayout();
}
}
pager.setAdapter(new PagerAdapter() {
@Override
public boolean isViewFromObject(View view, Object obj) {
return view == obj;
}
@Override
public int getCount() {
return viewList.size();
}
@Override
public void destroyItem(ViewGroup container, int position,
Object object) {
container.removeView(viewList.get(position));
}
@Override
public CharSequence getPageTitle(int position) {
return titles[position];
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
container.addView(viewList.get(position));
return viewList.get(position);
}
});
return convertView;
}
@Override
public void onStart() {
super.onStart();
Log.d("test fragment", "fregment start");
}
@Override
public void onStop() {
super.onStop();
Log.d("test fragment", "fregment stop");
}
@Override
public void onResume() {
super.onResume();
Log.d("test fragment", "fregment resume");
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d("test fragment", "fregment destroy");
}
@Override
public void onDetach() {
super.onDetach();
Log.d("test fragment", "fregment detach");
}
}
}
<?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:orientation="vertical" >
<FrameLayout
android:id="@+id/fragment_container"
android:layout_width="fill_parent"
android:layout_height="0dip"
android:layout_weight="1.0"
android:background="#fffab3" >
</FrameLayout>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:background="@android:color/black"
android:orientation="horizontal" >
<TextView
android:id="@+id/tv1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:layout_marginLeft="10dp"
android:layout_marginTop="10dp"
android:text="tab1"
android:textColor="#ffffff"
android:textIsSelectable="true"
android:textSize="25sp" />
<TextView
android:id="@+id/tv2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:layout_marginLeft="15dp"
android:layout_marginTop="10dp"
android:text="tab2"
android:textColor="#ffffff"
android:textIsSelectable="true"
android:textSize="25sp" />
<TextView
android:id="@+id/tv3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:layout_marginLeft="15dp"
android:layout_marginTop="10dp"
android:text="tab3"
android:textColor="#ffffff"
android:textIsSelectable="true"
android:textSize="25sp" />
</LinearLayout>
</LinearLayout>