2013年7月12日金曜日

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

FragmentPagerAdapter を使って各画面に Fragment を使う場合、ある画面の Fragment インスタンスを保持しておいてそれに対して処理を行うということがあります。
  1. public class MainActivity extends SherlockFragmentActivity implements LibraryListener, DrawerListener {  
  2.   
  3.     private MainFragment mMainFragment;  
  4.   
  5.     @Override  
  6.     protected void onCreate(Bundle savedInstanceState) {  
  7.         super.onCreate(savedInstanceState);  
  8.         setContentView(R.layout.activity_main);  
  9.   
  10.         ViewPager viewPager = (ViewPager) findViewById(R.id.pager);  
  11.         viewPager.setAdapter(new MainPagerAdapter(getSupportFragmentManager()));  
  12.     }  
  13.   
  14.     public class MainPagerAdapter extends FragmentPagerAdapter {  
  15.   
  16.         public MainPagerAdapter(FragmentManager fm) {  
  17.             super(fm);  
  18.         }  
  19.   
  20.         @Override  
  21.         public Fragment getItem(int position) {  
  22.             switch (position) {  
  23.                 case 0:  
  24.                     return (mMainFragment = new MainFragment());  
  25.                 case 1:  
  26.                     return new SubFragment();  
  27.             }  
  28.   
  29.             return null;  
  30.         }  
  31.   
  32.         @Override  
  33.         public int getCount() {  
  34.             return 2;  
  35.         }  
  36.     }  
  37.   
  38.     private void hoge() {  
  39.         mMainFragment.fuga();  
  40.     }  
  41. }  
このような場合、画面回転後に hoge() を呼ぶと mMainFragment が null なので NPE になります。
なぜ mMainFragment が null になるかというと、FragmentPagerAdapter が一度 getItem() で生成した Fragment を再利用してくれるので、画面回転時に getItem() が呼ばれないからです。

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


2013年7月4日木曜日

Volley を使って XML を処理する

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

Entity から InputStream を取得して、XmlPullParser を使って parse している処理があったとします。
これの通信部分を Volley を使うようにするには、Request<InputStream> を継承したクラスを作ればいいわけです。
  1. public class InputStreamRequest extends Request<InputStream> {  
  2.   
  3.     private final Listener<inputstream> mListener;  
  4.   
  5.     /** 
  6.      *  
  7.      * @param method 
  8.      * @param url 
  9.      * @param listener 
  10.      * @param errorListener 
  11.      */  
  12.     public InputStreamRequest(int method, String url,   
  13.                                           Listener<InputStream> listener,   
  14.                                           ErrorListener errorListener) {  
  15.         super(method, url, errorListener);  
  16.         mListener = listener;  
  17.     }  
  18.   
  19.     /** 
  20.      *  
  21.      * @param url 
  22.      * @param listener 
  23.      * @param errorListener 
  24.      */  
  25.     public InputStreamRequest(String url, Listener<InputStream> listener,   
  26.                                           ErrorListener errorListener) {  
  27.         this(Method.GET, url, listener, errorListener);  
  28.     }  
  29.   
  30.     @Override  
  31.     protected void deliverResponse(InputStream response) {  
  32.         mListener.onResponse(response);  
  33.     }  
  34.   
  35.     @Override  
  36.     protected Response<InputStream> parseNetworkResponse(NetworkResponse response) {  
  37.         InputStream is = new ByteArrayInputStream(response.data);  
  38.         return Response.success(is, HttpHeaderParser.parseCacheHeaders(response));  
  39.     }  
  40. }  
  41. </inputstream>  
  1. public void doRequest(String url) {  
  2.     InputStreamRequest request = new InputStreamRequest(url,   
  3.        new Listener<InputStream>() {  
  4.   
  5.         @Override  
  6.         public void onResponse(InputStream in) {  
  7.             MyData data = parseXml(in);  
  8.             try {  
  9.                 in.close();  
  10.             } catch (IOException e) {  
  11.                 e.printStackTrace();  
  12.             }  
  13.             if (mListener != null) {  
  14.                 mListener.onParseXml(data);  
  15.             }  
  16.         }  
  17.     }, new ErrorListener() {  
  18.   
  19.         @Override  
  20.         public void onErrorResponse(VolleyError error) {  
  21.             // error  
  22.         }  
  23.     });  
  24.   
  25.     mQueue.add(request);  
  26. }  


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