Kuwapp's Blog

Android と Flutter やってます

【Android】ViewPagerのページを動的に追加する

ViewPagerのページに表示するデータを最初にセットせずに逐次読み込みたかったので
一番端のページに到達したら新しくデータを追加し新規ページが追加される処理を入れてみた。

流れ

ページが端に到達したか確認

データを追加しAdapterを更新

実装

MainActivity.java
public class MainActivity extends FragmentActivity implements OnPageChangeListener{

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		//初期データ
		ArrayList<Integer> items = new ArrayList<Integer>();
		items.add(0);
		items.add(1);
		items.add(2);

		MyPagerAdapter adapter = new MyPagerAdapter(getSupportFragmentManager());
		adapter.addAll(items);

		final ViewPager viewPager = (ViewPager) findViewById(R.id.viewPager);
		viewPager.setAdapter(adapter);
		viewPager.setCurrentItem(1);
		viewPager.setOnPageChangeListener(this);
	}

	@Override
	public void onPageSelected(int position) {
	}

	@Override
	public void onPageScrolled(int position, float positionOffset,
			int positionOffsetPixels) {
	}

	@Override
	public void onPageScrollStateChanged(int state) {
		if (state == ViewPager.SCROLL_STATE_IDLE) {
			ViewPager viewPager = (ViewPager)findViewById(R.id.viewPager);
			MyPagerAdapter adapter = (MyPagerAdapter) viewPager.getAdapter();
			
			ArrayList<Integer> indexes = adapter.getAll();
			
			int currentPage = viewPager.getCurrentItem();
			if( currentPage != 0 && currentPage != indexes.size() - 1){
				//最初でも最後のページでもない場合処理を抜ける
				return;
			}
			
			int nextPage = 0;
			if(currentPage == 0){
				//最初のページに到達
				nextPage = 1;
				indexes.add(0, indexes.get(0) - 1);
				//1ページ目は既に存在するため、Fragmentを全て破棄する
				adapter.destroyAllItem(viewPager);
				adapter.notifyDataSetChanged();
			}else if(currentPage == indexes.size() - 1){
				//最後のページに到達
				nextPage = currentPage;
				indexes.add(indexes.get(indexes.size() - 1) + 1);
			}
			adapter.addAll(indexes);
			viewPager.setAdapter(adapter);
			viewPager.setCurrentItem(nextPage);
		}
	}

}

ViewPagerにsetOnPageChangeListenerをセットしてonPageScrollStateChangedで状態を取る。
stateがSCROLL_STATE_IDLEの場合にページの移動が完了しているので最初か最後のページかを判断して新しくページを追加する。
最初のページに到達し新しくページを追加した場合は既にあるFragmentが使い回されてページが更新されないためFragmentを破棄する処理を入れる。

onPageSelectedでなくonPageScrollStateChangedを使用するのがミソ。
onPageSelectedだと次のページが選択されたときに呼び出されるようで
ページの移動が完了したときではないみたいでした。
なので挙動が不自然になる。


FragmentとAdapterは以下

MyFragment.java
public class MyFragment extends Fragment{

	@Override
	public View onCreateView(LayoutInflater inflater,
			@Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
		
		View view = inflater.inflate(R.layout.fragment_pageritem, container, false);
		
		Bundle bundle = getArguments();
		int index = bundle.getInt("INDEX");
		
		TextView textView = (TextView)view.findViewById(R.id.textView1);
		textView.setText("Index:" + index);
		
		return view;
	}
}
MyPagerAdapter.java
public class MyPagerAdapter extends FragmentPagerAdapter {

	private ArrayList<Integer> mIndexes = new ArrayList<Integer>();

	public MyPagerAdapter(FragmentManager fm) {
		super(fm);
	}

	@Override
	public Fragment getItem(int position) {

		Bundle bundle = new Bundle();
		bundle.putInt("INDEX", mIndexes.get(position));

		MyFragment fragment = new MyFragment();
		fragment.setArguments(bundle);

		return fragment;
	}

	@Override
	public int getCount() {
		return mIndexes.size();
	}
	
	@Override
  	public int getItemPosition(Object object) {
    		return POSITION_NONE;
	}
  	
  	public void destroyAllItem(ViewPager pager) {
    		for (int i = 0; i < getCount() - 1; i++) {
      			try {
        			Object obj = this.instantiateItem(pager, i);
		 	if (obj != null)
          			destroyItem(pager, i, obj);
        		} catch (Exception e) {
        		}
      		}
  	}
 
	@Override
	public void destroyItem(ViewGroup container, int position, Object object) {
		super.destroyItem(container, position, object);
	
		if (position <= getCount()) {
			FragmentManager manager = ((Fragment) object).getFragmentManager();
			FragmentTransaction trans = manager.beginTransaction();
			trans.remove((Fragment) object);
			trans.commit();
	        }
	}
	
	public void addAll(ArrayList<Integer> indexes) {
		mIndexes = indexes;
	}

	public ArrayList<Integer> getAll() {
		return mIndexes;
	}

}

FragmentPagerAdapterのFragment破棄部分は以下で書いています。

【Android】FragmentPagerAdapterでFragmentを更新する - Kuwappブログ-アプリ開発記