1. 程式人生 > >ViewPager實現可滑動的Fragment,滑動頁面時報錯的解決方案

ViewPager實現可滑動的Fragment,滑動頁面時報錯的解決方案

1. 主Activity和主佈局,線性佈局中只有一個ViewPager控制元件:
MainActivity.java
activity.xml
2. 三個繼承Fragment的類和對應佈局:
FragmentOne.java
FragmentTwo.java
FragmentThree.java
fragment_one.xml
fragment_two.xml
fragment_three.xml
3. 繼承FragmentPagerAdapter的類,用於管理ViewPager控制元件:
MyPagerAdapter.java

以上是實驗的環境,本人想要實現如下的效果:


1. 問題描述

但是,在左右滑動Fragment時,會報錯:

Caused by: java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.

2. 部分程式碼

在FragmentOne的onCreateView方法中,我是按如下程式碼寫的。注意紅字部分,我對view進行了判斷,如果為空才建立。這是為了防止多次呼叫onCreateView而創建出多個view。

@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container,
			Bundle savedInstanceState) {
		if(view == null) {//<span style="color:#ff0000;">注意這裡,view是私有成員變數。</span>
			view = inflater.inflate(R.layout.fragment_one, container, false);
			Log.i("my", "s");
		}
		Log.i("my", "FragmentOne.onCreateView");
		return view;
	}

MyPagerAdapter.java程式碼如下。我只重寫了構造建構函式和getItem(int arg0), getCount()兩個方法。

package guoweidong.android.test06;

import java.util.ArrayList;
import java.util.List;

import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.util.Log;
import android.view.ViewGroup;

public class MyPagerAdapter extends FragmentPagerAdapter{
	private List<Fragment> list;
	private FragmentManager fm;
	public MyPagerAdapter(FragmentManager fm) {
		super(fm);
		this.fm = fm;
		list = new ArrayList<Fragment>();
	}
	public MyPagerAdapter(FragmentManager fm, List<Fragment> list) {
		super(fm);
		this.fm = fm;
		this.list = list;
	}
	@Override
	public Fragment getItem(int arg0) {
		// TODO Auto-generated method stub
		return list.get(arg0);
	}

	@Override
	public int getCount() {
		// TODO Auto-generated method stub
		return list.size();
	}
	
	
}

3. 問題發現

在FragmentPagerAdapter中有兩個方法:
public Object instantiateItem(ViewGroup container, int position);
public void destroyItem(ViewGroup container, int position, Object object);
這兩個方法中第一個是用來將Fragment通過FragmentManager新增到ViewPager中的;第二個是用來將Fragment移除ViewPager的。在我們左右滑動Fragment時,在ViewPager中始終只保留兩個Fragment,剩餘的Fragment會移除ViewPager,但是並不會將此Fragment移除FragmentManager。 我們考慮一個問題:當Fragment被移除ViewPager後,又重新呼叫instantiateItem方法新增到ViewPager,但是在instantiateItem方法中不光新增到ViewPager,還新增到FragmentManager中,這就帶來了重複新增問題。

4. 解決方案

方案一: 刪掉FragmentOne的onCreateView的if判斷語句,得到下面程式碼:
@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container,
			Bundle savedInstanceState) {
		view = inflater.inflate(R.layout.fragment_one, container, false);
		return view;
	}
這樣每次返回的view都是新建的,所以不存在重複新增問題。但是這麼做,會不斷增加FragmentManager的負擔。 方案二: 複寫FragmentViewPager的instantiateItem和destroyItem方法,使它不重複新增Fragment。 在MyPagerAdapter中新增下面的方法:
	@Override
	public Object instantiateItem(ViewGroup container, int position) {
		String name = ""+ container.getId() + position;
		Fragment fragment = fm.findFragmentByTag(name);
		Log.i("my", name);
		if(fragment != null) {
			fm.beginTransaction()
			.attach(fragment)
			.commit();
			Log.i("my", "attach");
		} else {
			fragment = getItem(position);
			fm.beginTransaction()
			.add(container.getId(), fragment, name)
			.commit();
		}
		return fragment;
	}
	@Override
	public void destroyItem(ViewGroup container, int position, Object object) {
		// TODO Auto-generated method stub
		String name = ""+ container.getId() + position;
		Fragment fragment = fm.findFragmentByTag(name);
		if(fragment != null) {
			fm.beginTransaction()
			.detach(fragment);
		}
	}