分享

嵌套Fragment的使用及遇到The specified child already has a parent. You must call removeView()问题的解决

 quasiceo 2016-01-06


嵌套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的方法

1.layout文件中声明fragment

  1. <FrameLayout  
  2.       android:id="@+id/fragment_container"  
  3.       android:layout_width="fill_parent"  
  4.       android:layout_height="0dip"  
  5.       android:layout_weight="1.0"  
  6.       android:background="#fffab3" >  
  7.   </FrameLayout>  

2.将一个fragment添加到viewgroup中,使用FragmentTransaction添加、替换或者删除fragment


  1. private void addFragmentToStack(Fragment fragment) {  
  2.         FragmentTransaction ft = getSupportFragmentManager().beginTransaction();  
  3.         ft.replace(R.id.fragment_container, fragment);  
  4.         ft.commit();  
  5.     }  

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()方法。代码如下:


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

结果展示


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




主要代码


PS:完整工程点这里下载


TestFragmentActivity.java

  1. package com.test;  
  2.   
  3. import java.util.ArrayList;  
  4.   
  5. import android.annotation.SuppressLint;  
  6. import android.app.LocalActivityManager;  
  7. import android.content.Intent;  
  8. import android.os.Bundle;  
  9. import android.support.v4.app.Fragment;  
  10. import android.support.v4.app.FragmentActivity;  
  11. import android.support.v4.app.FragmentTransaction;  
  12. import android.support.v4.view.PagerAdapter;  
  13. import android.support.v4.view.ViewPager;  
  14. import android.util.Log;  
  15. import android.view.LayoutInflater;  
  16. import android.view.View;  
  17. import android.view.View.OnClickListener;  
  18. import android.view.ViewGroup;  
  19. import android.view.Window;  
  20. import android.widget.LinearLayout;  
  21.   
  22. /** 
  23.  * 嵌套Fragment的使用 
  24.  *  
  25.  * @author zouliping 
  26.  *  
  27.  */  
  28. public class TestFragmentActivity extends FragmentActivity {  
  29.   
  30.     private LocalActivityManager manager;  
  31.   
  32.     private ArrayList<View> list1 = new ArrayList<View>();  
  33.     private ArrayList<View> list2 = new ArrayList<View>();  
  34.     private ArrayList<View> list3 = new ArrayList<View>();  
  35.   
  36.     private Fragment[] fragments;  
  37.   
  38.     @Override  
  39.     protected void onCreate(Bundle savedInstanceState) {  
  40.         super.onCreate(savedInstanceState);  
  41.         requestWindowFeature(Window.FEATURE_NO_TITLE);  
  42.         setContentView(R.layout.test_fragments);  
  43.   
  44.         manager = new LocalActivityManager(thisfalse);  
  45.         manager.dispatchCreate(savedInstanceState);  
  46.   
  47.         initViews();  
  48.     }  
  49.   
  50.     /** 
  51.      * 初始化Views 
  52.      */  
  53.     private void initViews() {  
  54.         findViewById(R.id.tv1).setOnClickListener(listener);  
  55.         findViewById(R.id.tv2).setOnClickListener(listener);  
  56.         findViewById(R.id.tv3).setOnClickListener(listener);  
  57.   
  58.         fragments = new FragmentParent[3];  
  59.         fragments[0] = FragmentParent.newInstance(list1, new String[] {  
  60.                 "page1_1""page1_2""page1_3" });  
  61.         fragments[1] = FragmentParent.newInstance(list2, new String[] {  
  62.                 "page2_1""page2_2""page2_3" });  
  63.         fragments[2] = FragmentParent.newInstance(list3, new String[] {  
  64.                 "page3_1""page3_2""page3_3" });  
  65.   
  66.         initPager();  
  67.   
  68.         findViewById(R.id.tv1).performClick();  
  69.     }  
  70.   
  71.     /** 
  72.      * 获取view 
  73.      *  
  74.      * @param id 
  75.      * @param intent 
  76.      * @return 
  77.      */  
  78.     private View getView(String id, Intent intent) {  
  79.         return manager.startActivity(id, intent).getDecorView();  
  80.     }  
  81.   
  82.     /** 
  83.      * 根据parent position初始化viewPager 
  84.      */  
  85.     private void initPager() {  
  86.         Intent intent;  
  87.   
  88.         // tab1  
  89.         intent = new Intent(TestFragmentActivity.this, Test1Activity.class);  
  90.         list1.add(getView("tab1_1", intent));  
  91.         intent = new Intent(TestFragmentActivity.this, Test2Activity.class);  
  92.         list1.add(getView("tab1_2", intent));  
  93.         intent = new Intent(TestFragmentActivity.this, Test1Activity.class);  
  94.         list1.add(getView("tab1_3", intent));  
  95.   
  96.         // tab2  
  97.         intent = new Intent(TestFragmentActivity.this, Test1Activity.class);  
  98.         list2.add(getView("tab2_1", intent));  
  99.         intent = new Intent(TestFragmentActivity.this, Test2Activity.class);  
  100.         list2.add(getView("tab2_2", intent));  
  101.         intent = new Intent(TestFragmentActivity.this, Test1Activity.class);  
  102.         list2.add(getView("tab2_3", intent));  
  103.   
  104.         // tab3  
  105.         intent = new Intent(TestFragmentActivity.this, Test1Activity.class);  
  106.         list3.add(getView("tab2_1", intent));  
  107.         intent = new Intent(TestFragmentActivity.this, Test2Activity.class);  
  108.         list3.add(getView("tab2_2", intent));  
  109.         intent = new Intent(TestFragmentActivity.this, Test1Activity.class);  
  110.         list3.add(getView("tab2_3", intent));  
  111.     }  
  112.   
  113.     private OnClickListener listener = new OnClickListener() {  
  114.         @Override  
  115.         public void onClick(View v) {  
  116.             switch (v.getId()) {  
  117.             case R.id.tv1:  
  118.                 addFragmentToStack(fragments[0]);  
  119.                 break;  
  120.             case R.id.tv2:  
  121.                 addFragmentToStack(fragments[1]);  
  122.                 break;  
  123.             case R.id.tv3:  
  124.                 addFragmentToStack(fragments[2]);  
  125.                 break;  
  126.             }  
  127.   
  128.         }  
  129.     };  
  130.   
  131.     private void addFragmentToStack(Fragment fragment) {  
  132.         FragmentTransaction ft = getSupportFragmentManager().beginTransaction();  
  133.         ft.replace(R.id.fragment_container, fragment);  
  134.         ft.commit();  
  135.     }  
  136.   
  137.     /** 
  138.      * 嵌套Fragment 
  139.      *  
  140.      */  
  141.     @SuppressLint("ValidFragment")  
  142.     public final static class FragmentParent extends Fragment {  
  143.   
  144.         /** 
  145.          * 工厂方法,返回一个新的FragmentParent的实例 
  146.          *  
  147.          * @param list 
  148.          * @param str 
  149.          * @return 
  150.          */  
  151.         public static final FragmentParent newInstance(ArrayList<View> list,  
  152.                 String[] str) {  
  153.             FragmentParent framentParent = new FragmentParent();  
  154.             Bundle bundle = new Bundle();  
  155.             bundle.putSerializable("pager_view_list", list);  
  156.             bundle.putStringArray("pager_title_ary", str);  
  157.             framentParent.setArguments(bundle);  
  158.             return framentParent;  
  159.         }  
  160.   
  161.         @Override  
  162.         public View onCreateView(LayoutInflater inflater, ViewGroup container,  
  163.                 Bundle savedInstanceState) {  
  164.             Log.d("test fragment""fragment create view");  
  165.   
  166.             LinearLayout convertView = (LinearLayout) inflater.inflate(  
  167.                     R.layout.viewpager_fragments, container, false);  
  168.             ViewPager pager = (ViewPager) convertView.findViewById(R.id.pager);  
  169.   
  170.             @SuppressWarnings("unchecked")  
  171.             final ArrayList<View> viewList = (ArrayList<View>) getArguments()  
  172.                     .getSerializable("pager_view_list");  
  173.             final String[] titles = getArguments().getStringArray(  
  174.                     "pager_title_ary");  
  175.   
  176.             for (View view : viewList) {  
  177.                 ViewGroup p = (ViewGroup) view.getParent();  
  178.                 if (p != null) {  
  179.                     p.removeAllViewsInLayout();  
  180.                 }  
  181.             }  
  182.   
  183.             pager.setAdapter(new PagerAdapter() {  
  184.   
  185.                 @Override  
  186.                 public boolean isViewFromObject(View view, Object obj) {  
  187.                     return view == obj;  
  188.                 }  
  189.   
  190.                 @Override  
  191.                 public int getCount() {  
  192.                     return viewList.size();  
  193.                 }  
  194.   
  195.                 @Override  
  196.                 public void destroyItem(ViewGroup container, int position,  
  197.                         Object object) {  
  198.                     container.removeView(viewList.get(position));  
  199.                 }  
  200.   
  201.                 @Override  
  202.                 public CharSequence getPageTitle(int position) {  
  203.                     return titles[position];  
  204.                 }  
  205.   
  206.                 @Override  
  207.                 public Object instantiateItem(ViewGroup container, int position) {  
  208.                     container.addView(viewList.get(position));  
  209.                     return viewList.get(position);  
  210.                 }  
  211.             });  
  212.   
  213.             return convertView;  
  214.         }  
  215.   
  216.         @Override  
  217.         public void onStart() {  
  218.             super.onStart();  
  219.             Log.d("test fragment""fregment start");  
  220.         }  
  221.   
  222.         @Override  
  223.         public void onStop() {  
  224.             super.onStop();  
  225.             Log.d("test fragment""fregment stop");  
  226.         }  
  227.   
  228.         @Override  
  229.         public void onResume() {  
  230.             super.onResume();  
  231.             Log.d("test fragment""fregment resume");  
  232.         }  
  233.   
  234.         @Override  
  235.         public void onDestroy() {  
  236.             super.onDestroy();  
  237.             Log.d("test fragment""fregment destroy");  
  238.         }  
  239.   
  240.         @Override  
  241.         public void onDetach() {  
  242.             super.onDetach();  
  243.             Log.d("test fragment""fregment detach");  
  244.         }  
  245.   
  246.     }  
  247. }  


布局文件 test_fragments.xml

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas./apk/res/android"  
  3.     android:layout_width="match_parent"  
  4.     android:layout_height="match_parent"  
  5.     android:orientation="vertical" >  
  6.   
  7.     <FrameLayout  
  8.         android:id="@+id/fragment_container"  
  9.         android:layout_width="fill_parent"  
  10.         android:layout_height="0dip"  
  11.         android:layout_weight="1.0"  
  12.         android:background="#fffab3" >  
  13.     </FrameLayout>  
  14.   
  15.     <LinearLayout  
  16.         android:layout_width="fill_parent"  
  17.         android:layout_height="wrap_content"  
  18.         android:layout_gravity="bottom"  
  19.         android:background="@android:color/black"  
  20.         android:orientation="horizontal" >  
  21.   
  22.         <TextView  
  23.             android:id="@+id/tv1"  
  24.             android:layout_width="wrap_content"  
  25.             android:layout_height="wrap_content"  
  26.             android:layout_marginBottom="10dp"  
  27.             android:layout_marginLeft="10dp"  
  28.             android:layout_marginTop="10dp"  
  29.             android:text="tab1"  
  30.             android:textColor="#ffffff"  
  31.             android:textIsSelectable="true"  
  32.             android:textSize="25sp" />  
  33.   
  34.         <TextView  
  35.             android:id="@+id/tv2"  
  36.             android:layout_width="wrap_content"  
  37.             android:layout_height="wrap_content"  
  38.             android:layout_marginBottom="10dp"  
  39.             android:layout_marginLeft="15dp"  
  40.             android:layout_marginTop="10dp"  
  41.             android:text="tab2"  
  42.             android:textColor="#ffffff"  
  43.             android:textIsSelectable="true"  
  44.             android:textSize="25sp" />  
  45.   
  46.         <TextView  
  47.             android:id="@+id/tv3"  
  48.             android:layout_width="wrap_content"  
  49.             android:layout_height="wrap_content"  
  50.             android:layout_marginBottom="10dp"  
  51.             android:layout_marginLeft="15dp"  
  52.             android:layout_marginTop="10dp"  
  53.             android:text="tab3"  
  54.             android:textColor="#ffffff"  
  55.             android:textIsSelectable="true"  
  56.             android:textSize="25sp" />  
  57.     </LinearLayout>  
  58.   
  59. </LinearLayout> 

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多