在使用ViewPager + Fragment 的时候报错:The specified child already has a parent. You must call removeView() on the child’s parent first.在解释这个问题之前我们需要了解ViewPager的预加载机制。
ViewPager会预加载当前页面的左右两边各一个页面,即左边一个页面,右边一个页面。
即:
假设ViewPager中依次存放3个View,分别是A、B、C即:A-->B-->C。
若当前页面时A,那么viewpager会缓存B,但不会缓存C;
若当前页面时B,那么viewpager会缓存A,也会缓存C;
若当前页面时C,那么viewpager会缓存B,但不会缓存A;
布局通过viewpager + 4个button来实现页面的切换,用fragment填充viewpager。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity" >
<android.support.v4.view.ViewPager
android:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:orientation="horizontal" >
<Button
android:id="@+id/weixin"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:onClick="click"
android:text="微信" />
<Button
android:id="@+id/contact"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:onClick="click"
android:text="联系人" />
<Button
android:id="@+id/discover"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:onClick="click"
android:text="发现" />
<Button
android:id="@+id/me"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:onClick="click"
android:text="我" />
LinearLayout>
LinearLayout>
package com.android.weixinfragment2;
import java.util.ArrayList;
import java.util.List;
import com.android.weixinfragment.R;
import com.android.weixinfragment2.fragment.FragmentContacts;
import com.android.weixinfragment2.fragment.FragmentDiscover;
import com.android.weixinfragment2.fragment.FragmentMe;
import com.android.weixinfragment2.fragment.FragmentWX;
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.view.ViewPager;
import android.view.View;
public class MainActivity extends FragmentActivity {
private ViewPager viewPager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
FragmentManager fm = getSupportFragmentManager();
List fragList = new ArrayList();
fragList.add(new FragmentWX());
fragList.add(new FragmentContacts());
fragList.add(new FragmentDiscover());
fragList.add(new FragmentMe());
viewPager = (ViewPager) findViewById(R.id.viewpager);
MyAdapter adapter = new MyAdapter(fm, fragList);
viewPager.setAdapter(adapter);
viewPager.setCurrentItem(0);
}
public void click(View view) {
switch (view.getId()) {
case R.id.weixin:
viewPager.setCurrentItem(0);
break;
case R.id.contact:
viewPager.setCurrentItem(1);
break;
case R.id.discover:
viewPager.setCurrentItem(2);
break;
case R.id.me:
viewPager.setCurrentItem(3);
break;
}
}
}
其中:if条件里面的代码保留,只注释掉if条件和else
// if (view == null) {
view = View.inflate(getActivity(), R.layout.fragmentcontacts, null);//保留
// } else {
// ViewParent parent = view.getParent();
// if (parent instanceof ViewGroup) {
// ((ViewGroup) parent).removeView(view);// 去掉一个parent
// }
// }
package com.android.weixinfragment2.fragment;
import com.android.weixinfragment.R;
import android.os.Bundle;
import android.os.Handler;
import android.os.SystemClock;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ProgressBar;
import android.widget.TextView;
public class FragmentWX extends Fragment {
private ProgressBar pb1;
private TextView tv1;
private View view;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// if (view == null) {//防止重新加载导致流量消耗
view = View.inflate(getActivity(), R.layout.fragmentwx, null);
// } else {
// ViewParent parent = view.getParent();
// if (parent instanceof ViewGroup) {
// ((ViewGroup)parent).removeView(view);//去掉一个parent
// }
// }
pb1 = (ProgressBar) view.findViewById(R.id.pb1);
tv1 = (TextView) view.findViewById(R.id.tv1);
handler.sendEmptyMessageDelayed(0, 2000);
return view;
}
// 模拟联网耗时操作
Handler handler = new Handler() {
public void handleMessage(android.os.Message msg) {
pb1.setVisibility(View.GONE);
tv1.setVisibility(View.VISIBLE);
};
};
}
1 为了显示出联网的效果,使用了handler,延迟了2秒,所以viewpager的预加载也就多了2秒。
2 但这样就出现了一个bug:就是当我们从第1个界面进入到第3、4个界面后,再回到第1个页面,第1个页面会重新加载(viewpager只预加载左右两边各1个)
3 而这会消耗用户流量,我们需要做一个判断。即:当前的view == null,创建view并加载数据。放开if的注释,见下面:
if (view == null) {//防止重新加载导致流量消耗
view = View.inflate(getActivity(), R.layout.fragmentwx, null);
}
// else {
// ViewParent parent = view.getParent();
// if (parent instanceof ViewGroup) {
// ((ViewGroup)parent).removeView(view);//去掉一个parent
// }
// }
4 解决了上面的问题,但是却导致了另外一个bug,也就是今天要讲的
The specified child already has a parent. You must call removeView() on the child's parent
我们把view添加到viewpager,viewpager会给这个view外面包一层noSaveStateFrameLayout,当view==null时,只包了一层;但但我们返回的是已经创建好的view,即复用了view,那么viewpager会在包一层noSaveStateFrameLayout,导致view有2个parent。所以报错。
不使用if (view == null) {…}来实现节约流量的目的,而是通过改变viewpager预加载数量的方式来实现。
即:viewPager.setOffscreenPageLimit(3);//预加载左右两边各3个。
那么若当前页是A,页面B、C、D都会预加载,节约啦用户流量。
保留if (view == null) {…}同时在else中去掉一个parent。即:
if (view == null) {
view = View.inflate(getActivity(), R.layout.fragmentwx, null);
} else {
ViewParent parent = view.getParent();
if (parent instanceof ViewGroup) {
((ViewGroup)parent).removeView(view);
}
}
点击下载源码(bin目录下含apk)