ツイートを検索するクラスの作成
ツイートを検索するクラスを作成します。このクラスは、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から画像を検索して、表示するアプリを作成する予定です。
