非同期処理、WebAPIの利用、XMLのパース

 非同期処理が自分の中でボトルネックになっていたので、一回やってみた。
 今回作ったものは、ATNDからイベントの情報を取得してリストに表示し、リストの中からイベントを選択すると、詳しい情報を表示するというモノ。

AsyncTaskActivity.java


package com.android.yamaguchi.AsyncTaskActivity;

import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import org.xmlpull.v1.XmlPullParser;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.DialogInterface.OnCancelListener;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.util.Xml;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ListView;

//リストを表示するクティビティ 
public class AsyncTaskActivity extends Activity implements OnItemClickListener{
 private Context context=null;
 ProgressDialog dialog;
 
 /** アダプタ */
 private MyArrayAdapter adapter = null;
 
 private static int DATA_NUM=100;
 int n=0;
 
 //別のActivityにデータを渡すための定数
 private static String START="start";
 private static String END="end";
 private static String UPDATE="update";
 private static String TITLE="title";
 private static String DESCRIPTION="description";
 private static String EVENT_URL="event_url";
 private static String OWNER_NICKNAME="owner_nickname";
 private static String ADDRESS="address";
 private static String PLACE="place";
 private static String LIMIT="limit";
 
 public void onCreate(Bundle savedInstanceState) {
	 super.onCreate(savedInstanceState);
	 setContentView(R.layout.main);
	 
	 //リストを表示
     ListView listView=(ListView)findViewById(R.id.list);
     listView.setScrollingCacheEnabled(false);
	 
     //アダプタ作成
     adapter = new MyArrayAdapter(this);
     listView.setOnItemClickListener(this);
     listView.setAdapter(adapter);
     //リストが空のときに表示されるViewを指定
     listView.setEmptyView(findViewById(R.id.empty));
     
     this.context=this;
	 
     //非同期処理を開始
	 new AdapterAsync().execute();
 }
 
 /*************************** AsyncTaskを継承したクラスを、内部クラスとして作成 ***********************/
 class AdapterAsync extends AsyncTask implements OnCancelListener{
	 			   //↑これはdoInBackground、onProgressUpdate、onPostExecuteの
	 			   //引数と同じものを指定、と考えればよさそう。
	 
	 //前処理。ここでプログレスバーの表示などを行う。
	 @Override
	  protected void onPreExecute() {
	    //プログレスバーを表示
	    dialog = new ProgressDialog(context);
	    dialog.setTitle("イベント情報を取得中");
	    dialog.setMessage("もう少しお待ち下さい...");
	    dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
	    dialog.setCancelable(true);
	    dialog.setOnCancelListener(this);
	    dialog.setMax(DATA_NUM);
	    dialog.setProgress(0);
	    dialog.show();
	  }
	 
	 //非同期処理
	 @Override
	 protected Void doInBackground(Void... params) {
		 //String ornerTwitter="uni_labo";
		 //String kenmei="福井県";
		 final StringBuilder sb_query=new StringBuilder();
		 //final StringBuilder sb_return=new StringBuilder();
		 
		 int eventType;
		 //int eventCount=0;
		 String start=null;
		 String end=null;
		 String update=null;
		 String title=null;
		 String description=null;
		 String event_url=null;
		 String owner_nickname=null;
		 String address=null;
		 String place=null;
		 String limit=null;

		 try {
			 XmlPullParser xmlPullParser = Xml.newPullParser();
				
                         //ATNDのAPIからXMLを取得する
			 sb_query.append("\""+URLEncoder.encode("福井県","UTF-8")+"\"");
			 sb_query.append(",");
			 sb_query.append("\""+URLEncoder.encode("京都府","UTF-8")+"");
			 String urlString="http://api.atnd.org/events/?keyword_or="+
			 new String(sb_query)+"&count="+DATA_NUM;
			 Log.v("XmlPullParserSampleUrl",urlString);
			 URL url = new URL(urlString);
			 URLConnection connection = url.openConnection(); 
			 xmlPullParser.setInput(connection.getInputStream(), "UTF-8");
			 
			 /********************* XMLをパース ********************/
			 while *1 != XmlPullParser.END_DOCUMENT) {
				 //if文で分岐して、XMLから情報を取得。
				 if (eventType == XmlPullParser.START_TAG && "title".equals(xmlPullParser.getName())) {
					 title=xmlPullParser.nextText();
				 }
				 if (eventType == XmlPullParser.START_TAG && "description".equals(xmlPullParser.getName())) {
					 description=xmlPullParser.nextText();
				 }
				 if (eventType == XmlPullParser.START_TAG && "event_url".equals(xmlPullParser.getName())) {
					 event_url=xmlPullParser.nextText();
				 }
				 if (eventType == XmlPullParser.START_TAG && "owner_nickname".equals(xmlPullParser.getName())) {
					 owner_nickname=xmlPullParser.nextText();
				 }
				 if (eventType == XmlPullParser.START_TAG && "address".equals(xmlPullParser.getName())) {
					 address=xmlPullParser.nextText();
				 }
				 if (eventType == XmlPullParser.START_TAG && "place".equals(xmlPullParser.getName())) {
					 place=xmlPullParser.nextText();
				 }
				 if (eventType == XmlPullParser.START_TAG && "started_at".equals(xmlPullParser.getName())) {
					 start=xmlPullParser.nextText();
				 }	
				 if (eventType == XmlPullParser.START_TAG && "ended_at".equals(xmlPullParser.getName())) {
					 end=xmlPullParser.nextText();
				 }
				 if (eventType == XmlPullParser.START_TAG && "updated_at".equals(xmlPullParser.getName())) {
					 update=xmlPullParser.nextText();
				 }
				 if (eventType == XmlPullParser.START_TAG && "limit".equals(xmlPullParser.getName())) {
					 limit=xmlPullParser.nextText();
				 }
				 
				 //eventタグを抜けるときに、リストにアイテムを追加
				 if (eventType == XmlPullParser.END_TAG && "event".equals(xmlPullParser.getName())) {
					 
					 /********** publishProgress()は、onProgressUpdateを呼び出す。onProgressUpdate内で
					  * UIの更新処理(リストへのアイテムの追加や、プログレスバーを更新)を行う。
					  *  *************/
					 publishProgress(title,start,end,description,event_url,
							 owner_nickname,address,place,limit,update);
				 }
			 }
		 } catch (Exception e) {
			 e.printStackTrace();
		 }
		 return null;
	 }
 
	 @Override
	 /**
	  * UIスレッド上で起動
	  * UIの更新処理(リストへのアイテムの追加や、プログレスバーを更新)を行う。
	  */
	 protected void onProgressUpdate(String... item) {
		 			//↑可変長引数
		 //リストに追加するアイテムを作成
		 MyListViewItem myListViewItem = new MyListViewItem();
		 myListViewItem.title=item[0];
		 myListViewItem.started_at=item[1];
		 myListViewItem.ended_at=item[2];
		 myListViewItem.description=item[3];
		 myListViewItem.event_url=item[4];
		 myListViewItem.owner_nickname=item[5];
		 myListViewItem.address=item[6];
		 myListViewItem.place=item[7];
		 myListViewItem.limit=item[8];
		 myListViewItem.updated_at=item[9];
		 addItem(myListViewItem);
		 
		 n++;
		 dialog.setProgress(n);
	 }
	 
	 //後処理、プログレスバーを消したりする。
	 @Override
	 protected void onPostExecute(Void unused) {
		 dialog.dismiss();
	 }
	 @Override
	 public void onCancel(DialogInterface arg0) {
		 // TODO 自動生成されたメソッド・スタブ
		 this.cancel(true);
	 }
	 
	 /*********************************** addItemメソッド **************************************/
	 public void addItem(MyListViewItem item) {
		 //追加
		 adapter.add(item);
	 }
	  
	 @Override
	 protected void onCancelled() {
		 dialog.dismiss();
	 }
 }

 @Override
 public void onItemClick(AdapterView listView, View view, int position, long id) {
	 // TODO 自動生成されたメソッド・スタブ
	 MyListViewItem item = adapter.getItem(position);
	 
	 String startString="設定されていません";
	 if(item.started_at != null){
		 startString=item.started_at.substring(0,9)+":"+item.started_at.substring(11,19);
	 }
	 String endString="設定されていません";
	 Log.v("TEST","ended_at:"+item.ended_at);
	 if(item.ended_at != ""){
		 endString=item.ended_at.substring(0,9)+":"+item.ended_at.substring(11,19);
	 }

	 /******************* 説明文を表示するActivityを呼び出す。 *****************/
	 Intent i = new Intent();
	 //別Actibityに渡すデータを格納
	 i.putExtra(START, startString);	
	 i.putExtra(END, endString);	
	 i.putExtra(UPDATE, item.updated_at);	
	 i.putExtra(TITLE, item.title);
	 i.putExtra(DESCRIPTION, item.description);
	 i.putExtra(EVENT_URL, item.event_url);	
	 i.putExtra(OWNER_NICKNAME, item.owner_nickname);	
	 i.putExtra(ADDRESS, item.address);	
	 i.putExtra(PLACE, item.place);	
	 i.putExtra(LIMIT, item.limit);
	 
	 i.setClassName(
			"com.android.yamaguchi.AsyncTaskActivity",
	 		"com.android.yamaguchi.AsyncTaskActivity.ItemActivity");
	 startActivity(i);
 }


}

イベントの説明文を表示するActivity
ItemActivity.java


package com.android.yamaguchi.AsyncTaskActivity;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.widget.TextView;


public class ItemActivity extends Activity{
	
	TextView textViewTitle;
	TextView textViewStarted_at;
	TextView textViewEnded_at;
	TextView textViewDescription;
	TextView textViewEvent_url;
	TextView textViewOwner_nickname;
	TextView textViewAddress;
	TextView textViewPlace;
	TextView textViewLimit;
	TextView textViewUpdate_at;

	//別のActivityにデータを渡すための定数
	 private static String START="start";
	 private static String END="end";
	 private static String UPDATE="update";
	 private static String TITLE="title";
	 private static String DESCRIPTION="description";
	 private static String EVENT_URL="event_url";
	 private static String OWNER_NICKNAME="owner_nickname";
	 private static String ADDRESS="address";
	 private static String PLACE="place";
	 private static String LIMIT="limit";
	
	public void onCreate(Bundle savedInstanceState) {
		 super.onCreate(savedInstanceState);
		 setContentView(R.layout.item_activity);
		 
		 textViewTitle=(TextView)findViewById(R.id.title);
		 textViewStarted_at=(TextView)findViewById(R.id.started_at);
		 textViewEnded_at=(TextView)findViewById(R.id.ended_at);
		 textViewDescription=(TextView)findViewById(R.id.description);
		 textViewEvent_url=(TextView)findViewById(R.id.event_url);
		 textViewOwner_nickname=(TextView)findViewById(R.id.owner_nickname);
		 textViewAddress=(TextView)findViewById(R.id.address);
		 textViewPlace=(TextView)findViewById(R.id.place);
		 textViewLimit=(TextView)findViewById(R.id.limit);
		 textViewUpdate_at=(TextView)findViewById(R.id.updated_at);
		 
		 //説明文の取得
		 Intent i=getIntent();
		 textViewTitle.setText(i.getStringExtra(TITLE));
		 textViewOwner_nickname.setText("主催者名:"+i.getStringExtra(OWNER_NICKNAME));
		 textViewEvent_url.setText("イベントURL:"+i.getStringExtra(EVENT_URL));
		 textViewLimit.setText("定員:"+i.getStringExtra(LIMIT)+"\n");
		 textViewStarted_at.setText("開始時間:"+i.getStringExtra(START));
		 textViewEnded_at.setText("終了時間:"+i.getStringExtra(END));
		 textViewPlace.setText("開催場所:"+i.getStringExtra(PLACE));
		 textViewAddress.setText("住所:"+i.getStringExtra(ADDRESS)+"\n");
		 textViewDescription.setText(i.getStringExtra(DESCRIPTION)+"\n");
		 textViewUpdate_at.setText("更新日時:"+i.getStringExtra(UPDATE));
	 }
}

MyArrayAdapter.java


package com.android.yamaguchi.AsyncTaskActivity;

import android.content.Context;
import android.graphics.Color;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;

public class MyArrayAdapter extends ArrayAdapter {
	public MyArrayAdapter(Context context) {
		super(context, R.layout.listview_item, R.id.item_text01);
		// 第1引数 ... コンテキスト
		// 第2引数 ... 表示するレイアウト
		// 第3引数 ... 第2引数の中のTextView(何もしなければ、このTextViewにListViewItemのtoString()の結果が表示される)
	}

	@Override
	public View getView(int position, View convertView, ViewGroup parent) {
		//基本的なことは親に任せる
		View view = super.getView(position, convertView, parent);
		
		//この時点でviewにはlistview_itemを生成したものが出来ている。
		//TextViewはListViewItemのtoString()の結果が入っている。
		// ※ここで取得されるViewは、リストのアイテムごとには生成されず、
		//  ListViewへ一度に表示されるアイテム数分のViewを使いまわすことになる点に注意
		
		//アプリ独自の表示内容に書き換えるために、情報を取得。
		MyListViewItem item = getItem(position);
		if (item != null) {
			
			//各コントロールを取得
			TextView textView01 = (TextView) view.findViewById(R.id.item_text01);
			TextView textView02 = (TextView) view.findViewById(R.id.item_text02);
			textView01.setTextColor(Color.WHITE);
			textView02.setTextColor(Color.WHITE);
				
			if (textView01 != null && item.title != null) {
				textView01.setText(item.title);
			}
			if (textView02 != null && item.started_at!= null) {
				textView02.setText(item.started_at.substring(0, 9));
			}
		}
		return view;
	}
}

MyListViewItem.java


package com.android.yamaguchi.AsyncTaskActivity;

public class MyListViewItem {
	public String title = null;
	public String description=null;
	public String event_url=null;
	public String started_at=null;
	public String ended_at=null;
	public String limit=null;
	public String address=null;
	public String place=null;
	public String owner_nickname=null;
	public String owner_twitter_id=null;
	public String updated_at=null;
}


これと、Gmailアカウントを経由してメールを送信するアプリ
参照:http://d.hatena.ne.jp/ttshrk/20110517/1305641955
を組み合わせて、定期的にATNDのイベント情報を通知するアプリを作りたいな、とか考えている。

*1:eventType = xmlPullParser.next(