ツイートを検索するクラスの作成
ツイートを検索するクラスを作成します。このクラスは、AsyncTaskLoaderを継承したクラスで、トレンドを元にツイートを検索します。AsyncTaskLoaderの詳細については、過去の記事を参照してください。
OsakaTrendAsyncLoaderクラス
クラス全体のコードは、次のとおりです。
public class OsakaTrendAsyncLoader extends AsyncTaskLoader<List<Status>> { // Twitterオブジェクト private Twitter twitter; public OsakaTrendAsyncLoader(Context context, Twitter _twitter) { super(context); this.twitter = _twitter; } @Override public List<Status> loadInBackground() { // (1) try { // 大阪のWOEID (2) int osaka = 15015370; // トレンドを取得する(3) Trend[] trend = this.twitter.getPlaceTrends(osaka).getTrends(); // 取得したトレンドから、ランダムで1つを選択する(4) Random rnd = new Random(); String q = trend[rnd.nextInt(trend.length)].getQuery(); // (5) // 検索文字列を設定する(6) Query query = new Query(q); query.setLocale("ja"); // 日本語のtweetに限定する query.setCount(20); // 最大20tweetにする(デフォルトは15) // 検索の実行(7) QueryResult result = this.twitter.search(query); return result.getTweets(); // (8) } catch (TwitterException e) { Log.d("twitter", e.getMessage()); } return null; } }
loadInBackgroundメソッドが、非同期に呼び出される部分です。ここにメインの処理を追加します(1)。
まずトレンドを取得します。トレンドを取得するには、TwitterクラスのgetPlaceTrendsメソッドを利用します(3)。このメソッドは、WOEIDで指定した地域のトレンド情報の上位10件分を返します。戻り値は、トレンド情報になっていますが、ここでは利用しやすいように、getTrendsメソッドを使って配列に変換しています。
なおWOEID(Where on Earth IDentifier)とは、全国の地域に割り当てられたIDコードで、Yahoo!が定めたものです。今回は、大阪のWOEID(15015370)を指定して、大阪のトレンド情報を取得します(2)。
次に、取得したトレンド情報の配列から、Randomクラスを使って、1つだけを選択しています(4)。getQueryメソッドは、トレンド情報から、トレンドのキーワードを取得するメソッドです(5)。
最後に、取得したキーワードを使って、ツイートを検索します(7)。検索は、searchメソッドを利用します。searchメソッドの引数には、検索の条件を設定するQueryオブジェクトを指定します。ここでは、ツイートの言語と、取得するツイート数を指定しています(6)。
検索の結果は、QueryResultというオブジェクトになっています。このオブジェクトには、検索結果のさまざまな情報が付加されていますが、このアプリでは、主にツイートのテキストしか利用しませんので、getTweetsメソッドで、ツイート情報のリストを取り出しています(8)。
LoaderCallbacksインターフェースを実装する
今度は、作成したOsakaTrendAsyncLoaderクラスを利用するコードを追加していきます。
OsakaTrendAsyncLoaderクラスは、AsyncTaskLoaderの派生クラスです。AsyncTaskLoaderの派生クラスを利用するには、LoaderCallbacksインターフェースを実装します。インターフェースの実装先は、どのクラスでも可能ですが、今回はビューを保持しているFragmentクラスとしました。
public static class PlaceholderFragment extends Fragment implements LoaderCallbacks<List<Status>> { @Override public Loader<List<Status>> onCreateLoader(int id, Bundle args) { OsakaTrendAsyncLoader loader = null; switch (id) { case 0: // ローダーの初期化(1) loader = new OsakaTrendAsyncLoader(getActivity(), this.twitter); loader.forceLoad(); break; } return loader; } @Override public void onLoadFinished(Loader<List<Status>> loader, List<Status> data) { if (data != null) { // アダプターの初期化(2) ArrayAdapter<String> adapter = new ArrayAdapter<String>( getActivity(),android.R.layout.simple_list_item_1); // アダプターにTweetをセットする(3) for (Status tweet : data) { adapter.add(tweet.getText()); } // ListViewにアダプターをセットする(4) ((ListView) getView().findViewById(R.id.listView1)). setAdapter(adapter); } } }
LoaderCallbacksに定義されているメソッドに、処理を追加していきます。
まず、onCreateLoaderです。このメソッドは、ローダーが新しく作成されたときに呼び出されますので、ここではOsakaTrendAsyncLoaderオブジェクトを生成して設定しています(1)。forceLoadメソッドで、キャッシュではなく毎回実際に通信を行うようにします。
次に、通信が終了した際に呼び出されるonLoadFinishedメソッドに追加します。このサンプルでは、フラグメントのListViewに、取得したツイート内容をセットしています。
ListViewにデータをセットするには、アダプターと呼ばれるデータセットクラスを利用します。今回は、Stringクラスの配列を使ったArrayAdapterを利用しています(2)。なお、android.R.layout.simple_list_item_1というのは、ListViewの行表示に使われているTextViewのIDです。
Statusのリストから、ツイートのテキストを取り出してアダプターにセットした後(3)、ListViewにそのアダプターをセットします(4)。
ローディング中を表示する処理を追加する
ツイートを取得して、ListViewにセットする処理まで追加しましたので、次にローディング中を表示する処理を追加しましょう。インターネットからツイートを取得し、表示するまでには、多少時間がかかります。このような場合には、ちゃんとバックグラウンドで処理が進行していることが、わかるようにすべきです。このサンプルでは、標準で用意されているローディング中のダイアログを表示するようにしています。
public static class PlaceholderFragment extends Fragment implements LoaderCallbacks<List<Status>> { // ローディング表示用ダイアログ private ProgressDialog progressDialog = null; // ローディングダイアログの消去 private void dialogDismiss(){ if (this.progressDialog != null) { this.progressDialog.dismiss(); this.progressDialog = null; } } @Override public void onPause() { super.onPause(); // ローディングダイアログの消去(3) dialogDismiss(); } @Override public Loader<List<Status>> onCreateLoader(int id, Bundle args) { // ローディングダイアログの表示(1) this.progressDialog = ProgressDialog .show(getActivity(), "Please wait", "Loading data..."); ~中略~ } @Override public void onLoadFinished(Loader<List<Status>> loader, List<Status> data) { ~中略~ // ローディングダイアログの消去(2) dialogDismiss(); } }
ローディングダイアログの表示は、とてもシンプルで、ProgressDialogというクラスのshowメソッドを実行するだけです(1)。
ただ注意が必要なのは、ローディングダイアログを消去するタイミングです。一つは、通信が終了したonLoadFinishedメソッドの最後で、ダイアログの消去とオブジェクトの破棄を行います(2)。
もう一つは、通信中に画面を回転した場合にも、ダイアログの消去とオブジェクトの破棄が必要です。通信中に画面を回転すると、Activityが再起動するのですが、自動でダイアログが破棄されないため、何もしないと例外が発生します。
それを防ぐために、Fragmentが非表示になる直前に呼び出されるonPauseメソッドでも、ダイアログの消去とオブジェクトの破棄を行っています(3)。
ボタンクリックでローダーを開始する
最後に、検索ボタンをクリックしたときに、ローダーを開始するようにします。
public static class PlaceholderFragment extends Fragment implements LoaderCallbacks<List<Status>> { @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); // 検索ボタンのイベントリスナーを設定する(1) ((Button)getActivity().findViewById(R.id.button1)) .setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // ローダーの開始 getLoaderManager() .restartLoader(0, null, PlaceholderFragment.this); } }); setRetainInstance(true); } }
ボタンのクリック時に処理を行うには、イベントリスナーを設定します。サンプルでは、Fragmentの作成時(onActivityCreatedメソッド)に、setOnClickListenerメソッドで設定しています(1)。イベントリスナー自体は、OnClickListenerインターフェースを実装した無名クラスとしています。
最後に
今回は、非同期でTwitter APIを利用し、トレンドのキーワードからツイートを検索するアプリを作成しました。次回は、Instagramから画像を検索して、表示するアプリを作成する予定です。