【Andoid】AsyncTaskLoaderを使ってみる
Android 3.0から導入されたLoaderについて、
参考サイトをもとに勉強していましたがいまいちよくわからず。。
Loaderを継承した、AsyncTaskLoader
の方が使い方がわかりやすそうだったので、
まずはこちらを使用してみました。
Loaderの基本クラス
Loader
非同期のデータロードを行う為の抽象クラス。
LoaderManager
Loaderインスタンスを管理するためのクラス。
LoaderManagerインスタンスは、1つ以上のLoaderインスタンスを管理する。
Activityまたは、Fragmentごとに1つしか作られない。
LoaderManager.LoaderCallbacks
Activityまたは、FragmentとLoaderManager間で、
双方向にやりとりするためのコールバックインターフェース。
AsyncTaskLoaderを使ってみる
Activity側の実装
Activity側の実装では、Loaderを管理するLoaderManager
インスタンスを初期化する必要があります。
また、LoaderManagerから状態を受け取るためのコールバックとしてLoaderManager.LoaderCallbacks
インターフェースが用意されているため、Activityにこれを実装します。
LoaderManager
の初期化には、getLoaderManager().initLoader()
を使用します。
initLoader()
は、Activity.onStart()
よりも早く呼び出す必要があります。
引数について、以下のとおりです。
引数 | 詳細 |
---|---|
第一引数: int id |
Loaderの識別子。 onCreateLoader()第一引数に渡される。 |
第二引数: Bundle args |
Loaderへのパラメタ。 onCreateLoaderメソッドの第二引数に渡される。 Loaderがすでに生成されている場合は、この値は無視される。 |
第三引数: LoaderCallback callback |
Loaderの状態変化に使用される。 LoaderCallbackインターフェースを継承したクラスを指定する。 |
以下に、実装のサンプルを記載します。
// LoaderCallbacksのジェネリクスには、Loaderの戻り値の型を指定する(以下は、String) public class MainActivity extends Activity implements LoaderCallbacks<String> { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // LoaderManagerの初期化 getLoaderManager().initLoader(0, null, this); } @Override public Loader<String> onCreateLoader(int id, Bundle args) { // Loderを初期化する MyLoader loader = new MyLoader(this); return loader; } @Override public void onLoadFinished(Loader<String> loader, String data) { // dataでは、Loderクラスの戻り値が返される // Loderの終了処理 Log.d("","onLoadFinished"); } @Override public void onLoaderReset(Loader<String> arg0) { // Loderが、リセットされるときに呼ばれる。 // ここで、もらっているdataを破棄する必要がある。 Log.d("","onLoaderReset"); } }
Loaderの実装
非同期処理を行うLoaderクラスは、AsyncTaskLoader
クラスを継承して作成します。
Loaderが処理を開始するには、forceLoad()
を呼び出す必要があり、
以下のサンプルでは、Loderの準備が完了した際に呼ばれるonStartLoading()
で実行しています。
実際にバックグラウンドで行う処理は、loadInBackground()
に記載します。
public static class MyLoader extends AsyncTaskLoader<String> { public MyLoader(Context context) { super(context); } @Override public void deliverResult(String data) { // Loderが処理した結果を返す。(メインスレッドで実行される) super.deliverResult(data); } @Override public String loadInBackground() { // Loderが実行するバックグラウンド処理 return "abc"; } @Override protected void onStartLoading() { // Loder側の準備ができたタイミングで呼び出される // UIスレッドで実装される forceLoad(); } }
注意点
AsyncTaskLoaderを使う上で幾つか注意すべき点があります。
呼び出し元のActivityがアクティブでないと、
onLoadFinished()
コールバックが呼ばれないため、 それを考慮した実装を行う必要があります。Loaderインスタンスの
onStartLoading()
は、Activityがアクティブになったタイミングで呼ばれます。(何度も呼ばれる) その為、一度処理した内容が再度処理されないように意識した実装を行う必要があります。
※こちらを参考にしました。
これらを考慮したサンプルを以下に記載します。
public static class MyLoader extends AsyncTaskLoader<String> { private String data; public AsyncLoader(Context context) { super(context); } @Override public void deliverResult(D data) { if (isReset()) { return; } this.data = data; super.deliverResult(data); } @Override public String loadInBackground() { return "abc"; } @Override protected void onStartLoading() { if (data != null) { deliverResult(data); } if (takeContentChanged() || data == null) { forceLoad(); } } @Override protected void onStopLoading() { cancelLoad(); } @Override protected void onReset() { super.onReset(); onStopLoading(); data = null; } }
参考
http://www.techdoctranslator.com/android/guide/activities/loaders http://blog.loadlimits.info/2012/09/asynctaskloaderのあるactivityに戻ってきたときに再度loadinbackgroundが呼ば/ http://ijoru.com/ijoru/?p=207
【Android】DialogFragmentを使ってみる
Android 3.0以降、ダイアログの表示には、DialogFragment
を使用することが推奨されているようです。
基本的な表示処理は、Fragmentと変わらないみたいなのですが、
使用したことがなかったので試してみました。
DialogFragmentの表示
ダイアログを表示するには、DialogFragment
を継承したクラスを作成して、
各メソッドを継承します。
最低限ダイアログの表示を確認するのであれば、onCreateDialog()
を継承し、
表示したいDIalog
クラスのインスタンスを返却してあげればOKです。
public class MyDialogFragment extends DialogFragment { @Override public Dialog onCreateDialog(Bundle savedInstanceState) { AlertDialog.Builder alert = new AlertDialog.Builder(getActivity()) .setTitle("タイトル") .setMessage("メッセージ") .setPositiveButton("OK", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dismiss(); } }); return alert.create(); } }
ただし、Fragment
を継承したDialogFragment
は、
Fragment
と同様に再生成時にデフォルトコンストラクタが呼ばれる使用のため、
初期化時は、以下のようにsetArguments()
を使用するのがお作法です。
public static final MyDialogFragment newInstance(int flags) { Bundle args = new Bundle(); args.putInt("flags", flags); MyDialogFragment fragment = new MyDialogFragment(); fragment.setArguments(args); return fragment; }
ハマったところ
DialogFragmet
の表示処理(初期化を含む)をActivityのOnCreate()
に記載していたのですが、
回転処理で異常終了が発生する不具合がありました。
DialogFragmet
の実装クラスがインナークラスとして宣言されている場合、
Publicな標準コンストラクタが呼び出せず例外が発生するようです。
そのためDialogFragmet
は、Publicなクラスとして使用するのが一般的なようです。
【Android】Fragmentを使ってみる
Fragment は Android 3.0 ( API レベル "Honeycomb" ) から使用できるようになったコンポーネントです。
Fragmentを使用することで、一つのActivityに対して複数のUIを構築することが可能で、
画面の一部に対してのUI切り替えや遷移などを柔軟に行うことができます。
Fragmentの実装
Fragmentを実装するには、最低限、以下のメソッドを実装する必要があります。
Activityの状態 | Fragmentで呼ばれるコールバック |
---|---|
onCreate() | システムが、Fragmentを作成したときに呼び出される。 コンポーネントの初期化処理などをここで行う。 |
onCreateView() | FragmentのUIが描画されるタイミングでよびだされる。 FragmentのレイアウトのRootになっているViewをここでinflateする。 |
onPause() | Fragmentが停止するときによばれる。 Fragmentで変更されたステータスの保存はここで行う。 |
他にも様々なライフサイクルメソッドが存在しますが、今回は割愛します。
Fragmentに対してlayoutを紐付けるには、以下のようにonCreateView()
でinflateを行います。
public class SampleFragment extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.sample_fragmet, container, false); } }
Fragmentの表示方法
Activityから、Fragmentを表示するためには、FragmentManager
を使用します。
Fragmentの追加・削除・交換などのアクションは、FragmentTransaction
によってコミット単位で管理されており、FragmentManager
から取得できるFragmentTransaction
に対して、
各アクションを適用することで、Fragmentの管理を行うことが可能です。
新規で作成したFragmentの表示を行うためには以下のように記載します。
表示したいViewGroupに対して、Fragmentをadd()
メソッドにより追加し
最後にcommit()
を呼び出すことで対象のFragmentを表示することができます。
FragmentManager fragmentManager = getFragmentManager(); FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); FirstFragment fragment = new FirstFragment(); fragmentTransaction.add(R.id.fragment_container, fragment, "tag"); fragmentTransaction.commit();
Fragmentをバックスタックに追加する
FragmentをFragmentTransaction
のバックスタックに追加することで、
スタック管理を行うことができます。
バックスタックはActivityにより管理されており、
ユーザがBACKキーを押すことにより前のFragmentへ戻ることが可能です。
以下は、表示中のFragmentを新しいFragmentと入れ替えるサンプルです。
addToBackStack(null)
を呼び出すことにより元のFragmentをバックスタックへ追加しています。
FragmentManager fragmentManager = getFragmentManager(); FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); SecondFragment secondFragment = new SecondFragment(); fragmentTransaction.replace(R.id.fragment_container, secondFragment); fragmentTransaction.addToBackStack(null); fragmentTransaction.commit();
Fragment生成時の注意事項
Fragmentの生成処理は、一般的に下記のようなstaticメソッドが使用されます。
public static SampleFragment newInstance(int index) { SampleFragment f = new SampleFragment(); Bundle args = new Bundle(); args.putInt("index", index); f.setArguments(args); return f; }
これは、システムがFragmentの再生成を行うときに、デフォルトのコンストラクタが呼ばれる為、
独自のコンストラクタを使用したパラメタの引き渡しができないという理由からです。
システムは、例えばメモリ不足などで破棄されたFragmentを再生成する時に、 Fragment#instantiate(Context context, String fname, Bundle args)) メソッドを使用します。
このメソッドは、引数で渡されたクラス名(fname)のFragmentを標準コンストラクタで呼び出してインスタンスを生成したのち、第3引数でセットしたBundleをsetArguments()でセットしてから返します。
そのため、Fragment生成に必要な引数がある場合は、
初期化時にsetArguments()
を使用してパラメタを渡す必要があります。
ライフサイクル
Fragmentのライフサイクルは、Activityに似たつくりになっています。
FragmentはActivityによって保持されているため、お互いのライフサイクルは連動していて、
例えば、ActivityがonPause()
を受け取ると、ActivityのそれぞれのFragmentはonPause()
を受け取ります。
Activityの状態とFragmentのコールバックメソッドの関連
Activityの状態 | Fragmentで呼ばれるコールバック |
---|---|
create | OnAttach ↓ OnCreate() ↓ OnCreateView() ↓ OnActivityCreated() |
start | OnStart() |
resume | OnResume() |
pause | OnPause() |
stop | OnStop() |
destroyed | OnDestroyView() ↓ OnDestroy() ↓ OnDetach() |
参考
https://sites.google.com/a/techdoctranslator.com/jp/android/guide/activities/fragments
【Android】ActionBarのAppIconを使ってみる
ActionBarの左上のAppIcon領域には、自由にロゴ画像を配置することが可能です。
また、タップイベントを有効することで画面の遷移ナビゲーションを管理することも可能です。
AppIconのロゴを変更する
1.AndroidManifest.xmlで指定する
AndroidManifest.xml
からAppIconの画像を変更するには、
Activity
要素にあるandroid:logo
に表示したい画像リソースを指定します。
<activity android:name="com.example.actionbarmove.SampleActivity" android:logo="@drawable/icon" > </activity>
2.ソースから指定する
ソース上から、画像を変更するには、getActionBar()
で取得した
ActionBarに対してsetLogo()
を使用します。
getActionBar().setLogo(R.drawable.icon);
タップイベントを取得する
AppIconのタップイベントを取得するには、
ActionBarに対して、setDisplayHomeAsUpEnabled(true)
を実行します。
上記メソッドを実行するとAppIconのアイコンに<
マークが付き、
タップイベントを取得できるようになります。
getActionBar().setDisplayHomeAsUpEnabled(true)
タップイベントを取得するには、
Activityクラスで、onOptionsItemSelected()
メソッドをオーバーライドします。
AppIconには、android.R.id.home
というリソースIDが割り振られいるため、
これをハンドリングします。
下記の実装では、AppIconを押下されたタイミングで、ホーム画面へ遷移を行っています。
Intent.FLAG_ACTIVITY_CLEAR_TOP
を付与して画面遷移を行うことでActivityスタックをクリアし、
どの画面から遷移してもホーム画面へ戻ることが可能です。
@Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case android.R.id.home: // app icon in Action Bar clicked; go home Intent intent = new Intent(this, HomeActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); startActivity(intent); return true; default: return super.onOptionsItemSelected(item); } }
参考
http://www.techdoctranslator.com/android/guide/ui/actionbar
http://dev.classmethod.jp/smartphone/android-tips-10-AppIcon/
【Android】ActionBarにタブコントロールを実装する
ActionBarには、フラグメントを切り替えるタブコントロールを実装することができます。
タブには、タイトル文字列やアイコン画像をセットすることが可能です。
TabListenerの実装
アクションバータブのイベントを制御するにはActionBar.TabListener
を実装する必要があります。
各abstractメソッドは、以下のとおりです。
メソッド | 概要 |
---|---|
onTabSelected() | タブが選択された時に呼ばれる |
onTabUnselected() | タブが非選択になった時に呼ばれる |
onTabReselected() | 同じタブが再度選択された時に呼ばれる |
実装メソッドには、イベントを受け取ったActionBar.Tab
インスタンスおよびFragmentTransaction
インスタンスが引数として渡されます。
コンストラクタにてあらかじめ対象のフラグメントを渡しておき、
上記の実装メソッドでフラグメントの追加や削除を行います。
MyTabListener.java
public class MyTabListener implements ActionBar.TabListener { private Fragment mFragment; // 新規タブを作成する際にフラグメントインスタンスを一緒に渡す public MyTabListener(Fragment fragment) { mFragment = fragment; } @Override public void onTabSelected(Tab tab, FragmentTransaction ft) { // タブが選択された時の処理 // フラグメントを追加する ft.add(R.id.fragment_content, mFragment, null); } @Override public void onTabUnselected(Tab tab, FragmentTransaction ft) { // タブが切り替えられた時の処理 // フラグメントを削除する ft.remove(mFragment); } @Override public void onTabReselected(Tab tab, FragmentTransaction ft) { // 同じタブを再度タップされた時の処理 // do nothing } }
注意事項
トランザクションの管理は、システムが代わりに呼び出しを行うため、
FragmentTransaction
に対してcommit()
を呼び出してはいけません。
自分で呼び出した場合は、例外が投げられる可能性があります。
また、トランザクションをバックスタックに追加することもできません。
ActionBarの実装
ActivityクラスにてgetActionBar()
を使って取得したActionBarインスタンスに対して、
setNavigationMode(ActionBar.NAVIGATION_MODE_TABS)
を設定することで、
ActionBarをタブ表示モードへ切り替えることができます。
final ActionBar actionBar = getActionBar(); actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
ActionBarに対してタブを追加するには、
actionBar.newTab()
で取得したインスタンスをaddTab()
にて追加します。
この時、タブに対してタイトル及びアイコン画像をセットすることが可能です。
また、タブの切り替え制御を行うために、上記で作成したActionBar.TabListener
をセットします。
// フラグメントを生成してTabへセット Fragment fragment1 = new Fragment1(); ActionBar.Tab tab1 = actionBar.newTab(); tab1.setText("タブタイトル1"); // タイトル文字列 tab1.setIcon(R.drawable.ic_launcher); // アイコン tab1.setTabListener(new MyTabListener(fragment1)); // リスナー actionBar.addTab(tab1);
これでActionBarに対してタブコントロールを実装することができました。
今回実装した、サンプルソースを記載します。
MainActivity.java
// APIレベル11以上のみ対応 @SuppressLint("NewApi") public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // アクションバーを取得してモードをタブモードへセット final ActionBar actionBar = getActionBar(); actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); // タイトルを非表示 actionBar.setDisplayShowTitleEnabled(false); // フラグメントを生成してTabへセット Fragment fragment1 = new Fragment1(); // 新しく生成したTabインスタンスには、タイトル文字列、アイコン、リスナーをセットすることができる ActionBar.Tab tab1 = actionBar.newTab(); tab1.setText("タブタイトル1"); tab1.setIcon(R.drawable.ic_launcher); tab1.setTabListener(new MyTabListener(fragment1)); actionBar.addTab(tab1); Fragment fragmet2 = new Fragment2(); actionBar.addTab(actionBar.newTab().setText("タブタイトル2") .setTabListener(new MyTabListener(fragmet2))); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } } class Fragment1 extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.tab1_fragment, container, false); } } class Fragment2 extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.tab2_fragment, container, false); } }
tab1_fragment.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <TextView android:layout_width="match_parent" android:layout_height="match_parent" android:text="フラグメント1"/> </LinearLayout>
tab2_fragment.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <TextView android:layout_width="match_parent" android:layout_height="match_parent" android:text="フラグメント2"/> </LinearLayout>
参考
【Android】ActionBarに検索バーを表示する
ActionBarは、ActionView
というウィジェットを表示する領域が存在します。
ActionView
は、表示するメニューアイテム(ボタン)と連動して対応するウィジェットを表示することが可能です。
今回は、ActionViewに検索バー(SearchView)を表示してみます。
1.メニューリソースを定義
ActionViewのメニューリソースは、以下の2種類の方法で定義することが可能です。
①android:actionViewClass属性を使う場合
menu/menu.main
<menu xmlns:android="http://schemas.android.com/apk/res/android" > <item android:id="@+id/searchView" android:icon="@android:drawable/ic_menu_search" android:showAsAction="always" android:title="hogehoge" android:actionViewClass="android.widget.SearchView"/> </menu>
②android:actionLayout属性を使う場合
menu/menu.main
<menu xmlns:android="http://schemas.android.com/apk/res/android" > <item android:id="@+id/searchView" android:icon="@android:drawable/ic_menu_search" android:showAsAction="always" android:title="hogehoge" android:actionLayout="@layout/layout_search"/> </menu>
layout/layout_search.xml
<?xml version="1.0" encoding="utf-8"?> <SearchView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" > </SearchView>
2.ソースの実装
ActionViewのインスタンスは、MenuItem
インスタンスのgetActionView()
メソッドで取得が可能です。
以下のサンプルでは、SearchViewで入力した文字をTextViewに反映しています。
public class MainActivity extends Activity implements OnQueryTextListener { TextView mTextView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mTextView = (TextView) findViewById(R.id.textView); } @Override @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); // SearchViewを取得する MenuItem searchItem = menu.findItem(R.id.searchView); final SearchView searchView = (SearchView) searchItem.getActionView(); searchView.setOnQueryTextListener(this); return true; } @Override public boolean onQueryTextChange(String newText) { return false; } @Override public boolean onQueryTextSubmit(String query) { // テキストViewに検索文字列を表示 mTextView.setText(query); return false; } }
【Android】ActionBarを使ってみる ① (メニューボタン)
Android 3.0 (API Level 11) から導入されたActionBarについて、
少しずつ勉強していきたいと思います。
ActionBarとは
Android 3.0以降で提供されているアクティビティ用のウィジェットです。 ActionBarは、4つの領域に分かれており各領域からアプリへの操作を制御することが可能です。
1.App icon
アプリのテーマアイコンを表示する領域です。 タップにより、画面ナビゲーション階層の制御を行います。
2.View control
ドロップダウンメニューやタブコントロールなどビュー切り替えの機能を提供しています。 検索バーを表示して検索機能を実装することも可能です。
3.Action buttons
アプリ操作のアクションボタンを配置する領域です。 ボタンを長押しすることで、アクションの名前を表示します。
4.Action overflow
3.Action buttons
では、入りきらないボタンや、使用頻度の低いボタンを配置する領域です。
※ 参考
android:targetSdkVersion
がAPIレベル11以上を指定している場合、
ActionBarはデフォルトで表示されるようになっています。
※ただし、android:minSdkVersion
が11以上でない場合は、
ActionBar
クラスへアクセスして、APIを実行することができません
ActionBarを非表示にするには
AndroidManifest
のActivityにて、
<android:theme="@android:style/Theme.Holo.NoActionBar">
を指定します。
または、
ソースコードからActionBarクラスインスタンスを呼び出し、hide()
を実行することでも、
非表示にすることが可能です。
ActionBar actionbar = getActionBar(); actionbar.hide();
ActionBarにメニューボタンを表示する
Action buttons
とAction overflow
にメニューボタンを配置してみます。
1.メニューリソースの定義
メニューボタンを定義するには、まずres/menu
ディレクトリにあるメニューリソースxmlを作成します。
<menu xmlns:android="http://schemas.android.com/apk/res/android" > <item android:id="@+id/item1" android:icon="@android:drawable/ic_menu_share" android:showAsAction="always" android:title="hogehoge"/> </menu>
属性 | 説明 |
---|---|
id | リソースID |
icon | アイコン画像 |
showAsAction | メニュー項目をどこへ表示するかを指定する |
title | メニュータイトル ※iconが指定されている場合は、長押しするとタイトルが表示される |
※詳しい属性の情報については、メニューリソースのドキュメントを参照してください。
showAsAction
は、各メニューボタンをアクションバーのどこへ配置するかの定義で、
下記5つのオプションを指定可能です。
never:
メニューボタンは、常にAction overflow
領域へ表示される。ifRoom:
表示領域があれば、Action buttons
領域へ表示される。
表示できない場合は、Action overflow
領域へ表示される。withText:
ボタンアイコンと一緒にテキストを表示するcollapseActionView:
action viewを関連付けるて折り畳めるメニューを表示する。
(action viewの指定がない場合は、標準動作となる)always:
常にAction buttons
領域へ表示される。
2.リソースの適用
作成したリソースxmlは、
ActivityクラスのonCreateOptionsMenu()
をオーバーライドして、
inflateすることで画面上に表示されます。
@Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.example_menu, menu); return true; }
3.ボタンイベントのハンドリング
各メニューボタンのイベントは、ActivityクラスのonOptionsItemSelected()
をオーバーライドして、
ハンドリングすることができます。
@Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.item1: Log.d("", "menu1 tap."); return true; default: return super.onOptionsItemSelected(item); } }
参考サイト
http://www.techdoctranslator.com/android/guide/ui/actionbar http://sakplus.jp/2012/02/23/actionbar/