View のない non-UI ( non-graphical ) fragment の1つの使い道
fragment に関する公式ドキュメントを読んでると、UI を持たない fragment を作ることもできるとあるけれど、一体何に使うのかよくわかりませんでした。
ところが最近、会社の先輩からその1つの使い道を教えてもらったので、忘れないようにメモ。
DialogFragment の Helper クラスとして使う
たとえば、以下の様な要件があるときに、non-UI fragment が使えます。
- ネット上の画像を取得して、DialogFragment の layout にその画像を表示させたい
- Activity に LoaderCallbacks を implements させたくない
このとき、以下のように素直に DialogFragment を継承したクラスに LoaderCallbacks を implements させて AsyncTaskLoader を呼びだそうとすると、
「IllegalStateException: Fragment not attached to Activity」が発生します。
public class SampleDialogFragment extends DialogFragment implements LoaderCallbacks<Bitmap>{ private FragmentManager mManager; private String mTag; private Bitmap mImageData; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View dialogView = inflater.inflate(R.layout.dialog_fragment_layout, container, false); ImageView imageView = (ImageView) dialogView.findViewById( R.id.image ); imageView.setImageBitmap( mImageData ); return dialogView; } // Activity からはこのメソッドを呼び出す public void showDialog ( FragmentManager manager, String tag ) { mManager = manager; mTag = tag; getLoaderManager().initLoader(0, null, this ); } @Override public Loader<Bitmap> onCreateLoader(int arg0, Bundle arg1) { return new SomeAsyncTaskLoader( getActivity() ); } @Override public void onLoadFinished(Loader<Bitmap> arg0, Bitmap imageData) { mImageData = imageData; show( mManager, mTag ); } @Override public void onLoaderReset(Loader<Bitmap> arg0) {} }
LoaderManager は Activity/Fragment のライフサイクルと連動して動くみたいなんですが、
上記の showDialog () を呼んだ時点ではまだこの fragment 自体が activity に紐付いておらず、activity のライフサイクルと連動していないので、結果としてLoaderManager が連動するライフサイクルが定まらないためにこの Exception が投げられるのかなと思います。
これを回避するために、UI を持たないフラグメントをただのインタフェースとして用意し、Activity にそのフラグメントを追加し、Activity からはそのフラグメントが提供するメソッドを介して DialogFragment を表示させるようにします。
おおまかな手順は、
- non-UI fragment を用意し、LoaderCallbacks をこれに implements する。
- Activity で non-UI fragment を add する
- Activity から non-UI fragement が提供するメソッドが呼び出されたら、そのメソッド内で loader を起動する
- onLoadFinished() で 実際の DialogFragment を呼び出す
という感じです。
しかし、まだ問題があります。
Can not perform this action inside of onLoadFinished
たとえば素直に onLoadFinished() 内で DialogFramgnet のインスタンスを生成し、表示させようとします。
@Override public void onLoadFinished(Loader<Bitmap> arg0, Bitmap imageData) { SampleDialogFragment dialog = new SampleDialogFragment(); dialog.show( getFragmentManager(), TAG, imageData ); }
すると以下の様な exception が投げられます。
FATAL EXCEPTION: main
java.lang.IllegalStateException: Can not perform this action inside of onLoadFinished
これについては、以下の stack over flow あたりを参考に回避します。
android - This Handler class should be static or leaks might occur: IncomingHandler - Stack Overflow
もろもろまとめると
サンプルプロジェクトを以下に置いてみました。
kiyotakagoto/NonUIFragmentSample · GitHub