2013年7月12日金曜日

ViewPager の Fragment を画面回転後も保持する方法

FragmentPagerAdapter を使って各画面に Fragment を使う場合、ある画面の Fragment インスタンスを保持しておいてそれに対して処理を行うということがあります。 public class MainActivity extends SherlockFragmentActivity implements LibraryListener, DrawerListener { private MainFragment mMainFragment; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ViewPager viewPager = (ViewPager) findViewById(R.id.pager); viewPager.setAdapter(new MainPagerAdapter(getSupportFragmentManager())); } public class MainPagerAdapter extends FragmentPagerAdapter { public MainPagerAdapter(FragmentManager fm) { super(fm); } @Override public Fragment getItem(int position) { switch (position) { case 0: return (mMainFragment = new MainFragment()); case 1: return new SubFragment(); } return null; } @Override public int getCount() { return 2; } } private void hoge() { mMainFragment.fuga(); } } このような場合、画面回転後に hoge() を呼ぶと mMainFragment が null なので NPE になります。
なぜ mMainFragment が null になるかというと、FragmentPagerAdapter が一度 getItem() で生成した Fragment を再利用してくれるので、画面回転時に getItem() が呼ばれないからです。

画面回転後も mMainFragment に以前のインスタンスを保持させるには、次のように onSaveInstanceState() と onRestoreInstanceState() を使います。 @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); if (mMainFragment != null) { getSupportFragmentManager().putFragment(outState, "main_fragment", mMainFragment); } } @Override protected void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); if (mMainFragment == null) { mMainFragment = (MainFragment) getSupportFragmentManager().getFragment(savedInstanceState, "main_fragment"); } } 実際に Fragment のインスタンスがこの処理で永続化されるわけではなく、画面回転後に以前のものと対応する Fragment を見つけてくるという処理です。


2013年7月4日木曜日

Volley を使って XML を処理する

Volley には JsonRequest とか JsonObjectRequest とか用意されているのですが、XML 用(?)のはありません。
残念ながら XML が返ってくる API を利用せねばならない場合もあります。JSON がいいよー。。。

Entity から InputStream を取得して、XmlPullParser を使って parse している処理があったとします。
これの通信部分を Volley を使うようにするには、Request<InputStream> を継承したクラスを作ればいいわけです。 public class InputStreamRequest extends Request<InputStream> { private final Listener mListener; /** * * @param method * @param url * @param listener * @param errorListener */ public InputStreamRequest(int method, String url, Listener<InputStream> listener, ErrorListener errorListener) { super(method, url, errorListener); mListener = listener; } /** * * @param url * @param listener * @param errorListener */ public InputStreamRequest(String url, Listener<InputStream> listener, ErrorListener errorListener) { this(Method.GET, url, listener, errorListener); } @Override protected void deliverResponse(InputStream response) { mListener.onResponse(response); } @Override protected Response<InputStream> parseNetworkResponse(NetworkResponse response) { InputStream is = new ByteArrayInputStream(response.data); return Response.success(is, HttpHeaderParser.parseCacheHeaders(response)); } } public void doRequest(String url) { InputStreamRequest request = new InputStreamRequest(url, new Listener<InputStream>() { @Override public void onResponse(InputStream in) { MyData data = parseXml(in); try { in.close(); } catch (IOException e) { e.printStackTrace(); } if (mListener != null) { mListener.onParseXml(data); } } }, new ErrorListener() { @Override public void onErrorResponse(VolleyError error) { // error } }); mQueue.add(request); }

Volley の使い方は adamrocker のブログがわかりやすいです 「throw Life : Volley(AndroidのHTTP通信ライブラリ)を使おう」