やっぱり開発者っていいなと思った話。
(さらに…)ブログ
-
PHP7の新機能が従来のPHPの型の曖昧さを防ぐので嬉しい件
歳を重ねるとプログラム言語のアップデートに脳みそがついていかなくなるという感覚をいだくPHP7のアップデートがあった。
-
WEBサーバーから取得したJSONデータ内のテキストや画像を複数表示する方法 手順1
手順1、新規Androidプロジェクト作成・Volleyをプロジェクトにインポート
JSON を吐き出す WEBサーバーの開発は割愛。
新規AndroidプロジェクトをVolleyTest という名前で作成します。
参考 : http://techfun.cc/android/firstandroid1.html
作成したプロジェクトに Volley をインポートします。
GoogleソースコードのVolleyダウンロードページの master というリンクから tgz のファイルをダウンロードし解凍します。
先ほど作成したプロジェクトツリーを右クリックして
File > Import > Android > Existing Android Code Into Workspace > Next とすすむ。Root Directoryに先ほどダウンロードしたVolleyプロジェクトを指定 > Finish とすすむ。
これでひとまず完了。
次の手順で MainActivity でWEBサーバーにHTTP通信してJSONデータを取得できるかをためす。
-
WEBサーバーから取得したJSONデータ内のテキストや画像を複数表示する方法
Facebookアプリやニュースアプリのように、WEBサーバーから取得したJSONデータ内のテキストや画像を複数表示する方法
以前、WEBサーバーから取得したJSONデータを複数表示 という挑戦をしたところ、読み込む画像が増えてくると Out of Memory というアプリで使用出来るメモリーをオーバーするというメモリリークエラーが発生し、アプリが強制終了する症状が発生しました。
で、どうすればよいのかと調べたり有識者にお聞きした所、 bitmap を再利用したりして使用メモリのオーバーフローを防げば良い とのことです。。。。。うむぅ、、抽象的。
才能の無い小生は、具体的に小さな所からせめていきましょう。
以前のやり方(WEBサーバーから取得したJSONデータを複数表示)では、addView() でview を追加、追加、追加、、、 という方法だが、この無限に増えて行くview が原因でメモリのオーバーフローしてしまう。
どうやらこの方法ではアプリ内の全ての使用メモリをコツコツ管理しないとならなず開発が大変なので、これをやめる。
新しいやり方は、Googleが提供している
Volley
というネットワーク通信用ライブラリと
Lrucache
というメモリに画像データをキャッシュするためのクラスを利用して、WEBサーバーから取得したJsonデータをforでループしながらHashMap等に代入し、Adapter を介してListViewにセットするという方法だ。
そして受け取ったJSONデータを扱うのはGoogleが提供しているGSON
というライブラリがオススメです。
javaでjsonを扱うのはJSON-libやorg.jsonがあるのですが、配列からjsonに変換したりjsonの配列に追加したりするコードはGSONがとても便利です。
以下の参考URLでソースコードをパクりながらコーディングしていこう。
参考)
http://techbooster.org/android/hacks/16474/
http://techbooster.org/android/application/16792/
http://www.youtube.com/watch?v=pMRnGDR6Cu0&list=LLEVE0e9tHyTwhvz21X8KxtQ&feature=share
http://qiita.com/haratchatta/items/86aa8517a91fea1e772f
http://www.technotalkative.com/android-asynchronous-image-loading-in-listview/
-
WEBサーバーから取得したJSONデータを複数表示 手順6
手順6、レイアウトを綺麗にする
画面の見た目を綺麗にしたい。
今の状態だと10年前くらいにパソコン初心者が作成するホームページの見た目の様になってるので、もうすこし綺麗にしまひょ。
hogehoge_sub.xml を修正するよ。
<?xml version=“1.0” encoding=“utf-8”?><LinearLayoutxmlns:android=“http://schemas.android.com/apk/res/android”android:orientation=“vertical”android:layout_width=“wrap_content”android:layout_height=“fill_parent”android:layout_margin=“10dp”><LinearLayoutandroid:orientation=“horizontal”android:layout_width=“fill_parent”android:layout_height=“wrap_content”android:gravity=“center_vertical”android:background=“#F0FFF0”><ImageViewandroid:id=“@+id/itemIcon”android:layout_width=“0dip”android:layout_weight=“.20”android:layout_height=“wrap_content”android:adjustViewBounds=“true”android:scaleType=“centerInside”android:layout_margin=“10dp”/><TextViewandroid:id=“@+id/itemTitle”android:layout_width=“0dip”android:layout_weight=“.80”android:layout_height=“wrap_content”android:textSize=“20sp”android:textStyle=“bold”android:layout_margin=“10dp”/></LinearLayout><TextViewandroid:id=“@+id/itemId”android:layout_width=“fill_parent”android:layout_height=“wrap_content”android:visibility=“gone”/><TextViewandroid:id=“@+id/itemDetail”android:layout_width=“fill_parent”android:layout_height=“wrap_content”android:layout_margin=“20dp”/><ImageViewandroid:id=“@+id/itemImage”android:layout_width=“wrap_content”android:layout_height=“wrap_content”android:scaleType=“fitCenter”android:adjustViewBounds=“true”android:layout_marginLeft=“10dp”android:layout_marginRight=“10dp”/></LinearLayout>・動作確認
ちょっと綺麗な見た目になったー。
ただ、気になるのは画像読み込み中に画像が読み込まれる予定の箇所に何も表示されないからレイアウトが押しつぶされている。。
画像読み込み中..下部の記事の画像が先に読み込まれ..画像を全部読み込み終わってからやっとレイアウトが完成うーーーん。
なんかちょっとなぁ。
画像が読み込まれる箇所にFacebookアプリみたいに空白の領域(プレースホルダー?)を表示させたいなぁ。とりあえず画像が読み込まれる箇所にグルグル回転するプログレスバーを表示させたいな。
・プログレスバーを表示
画像が読み込まれるであろう部分にプログレスバーを表示したい。「バー」といっても、クルクル回るローディング中画像の事です。こういうの↓このクルクル画像が表示されれば「 アレ?なにも表示されない・・バグか。 」となりにくく、ちょっと待つ気持ちになりそう。Progressbar というクラスを使用すると、くるくる回る画像が表示されます。HogehogeActivity.java を修正
package jp.example.hello;import java.io.IOException;import java.io.InputStream;import java.net.HttpURLConnection;import java.net.MalformedURLException;import java.net.URL;import org.apache.http.HttpResponse;import org.apache.http.HttpStatus;import org.apache.http.client.ClientProtocolException;import org.apache.http.client.HttpClient;import org.apache.http.client.methods.HttpGet;import org.apache.http.impl.client.DefaultHttpClient;import org.apache.http.util.EntityUtils;import org.json.JSONArray;import org.json.JSONObject;import android.app.Activity;import android.app.ProgressDialog;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.os.AsyncTask;import android.os.Bundle;import android.util.Log;import android.view.View;import android.widget.ImageView;import android.widget.LinearLayout;import android.widget.ProgressBar;import android.widget.TextView;import android.widget.Toast;public class HogehogeActivity extends Activity {private static final String LogTag = “HogehogeLog”;private ProgressDialog dialog;@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.hogehoge);// jsonを取得して解析するLoadTask();}private String makeApiUrl(){return “http://akamako.com/blogger/sample.php”;}private void LoadTask(){// ProgressDialogを作成dialog = new ProgressDialog(this);dialog.setMessage(“Connect to Server.”);dialog.setCancelable(true);dialog.show();try{String apiUrl = makeApiUrl();// コンソールログに出力Log.d(LogTag+” apiUrl”,apiUrl);HttpGet get = new HttpGet(apiUrl);HttpClient DefaultHttpClient = new DefaultHttpClient();HttpResponse response = DefaultHttpClient.execute(get);// ステータスコードint status = response.getStatusLine().getStatusCode();Log.d(LogTag+” status”,String.valueOf(status));if (status != HttpStatus.SC_OK) {throw new Exception(“Error!”);}// 結果を取得String source = EntityUtils.toString(response.getEntity());Log.d(LogTag+” source”,source);// JSONObject という型があるんだってJSONObject json = new JSONObject(source);if (json.get(“items”) == JSONObject.NULL){throw new Exception(“Error!”);}if (json.get(“count”) == JSONObject.NULL){throw new Exception(“Error!”);}// 記事データJSONArray items = json.getJSONArray(“items”);// 記事の数String count = json.getString(“count”);if(count.equals(“0”) == true){// 記事データが無かったお}// items の中身の数int maxI = items.length();LinearLayout layout = (LinearLayout) findViewById(R.id.hogehoge_layout);for(int i = 0; i < maxI; i++) {// 1つ取り出すJSONObject item = items.getJSONObject(i);// 表示項目を初期化String itemId = “”;String itemTitle=“”;String itemDetail=“”;String itemIconUrl=“”;String itemImageUrl=“”;if(item.has(“id”)) {itemId = item.getString(“id”);}if(item.has(“title”)) {itemTitle = item.getString(“title”);}if(item.has(“detail”)) {itemDetail = item.getString(“detail”);}if(item.has(“icon”)) {itemIconUrl = item.getString(“icon”);}if(item.has(“image”)) {itemImageUrl = item.getString(“image”);}Log.d(LogTag + ” itemId”, itemId);Log.d(LogTag + ” itemTitle”, itemTitle);Log.d(LogTag + ” itemDetail”, itemDetail);Log.d(LogTag + ” itemIconUrl”, itemIconUrl);Log.d(LogTag + ” itemImageUrl”, itemImageUrl);// layout/hogehoge_list.xml が1記事のテンプレートですView view = getLayoutInflater().inflate(R.layout.hogehoge_sub, null);layout.addView(view);// 各表示項目のスキーマ(?)を取り出す// TextView itemId_v = (TextView) view.findViewById(R.id.itemId);TextView itemTitle_v = (TextView) view.findViewById(R.id.itemTitle);TextView itemDetail_v = (TextView) view.findViewById(R.id.itemDetail);ImageView itemIcon_v = (ImageView) view.findViewById(R.id.itemIcon);ImageView itemImage_v = (ImageView) view.findViewById(R.id.itemImage);// 該当の表示個所に当て込む// itemId_v.setText(itemId);itemTitle_v.setText(itemTitle);itemDetail_v.setText(itemDetail);// プログレスバーを表示ProgressBar progressBarIcon = (ProgressBar) view.findViewById(R.id.progressBarIcon);ProgressBar progressBarImage = (ProgressBar) view.findViewById(R.id.progressBarImage);// 非同期処理new LoadImageTask(itemIcon_v,progressBarIcon).execute(itemIconUrl);new LoadImageTask(itemImage_v,progressBarImage).execute(itemImageUrl);// クリックしたときにitemTitleClickListener()を実行するitemTitle_v.setClickable(true);itemTitle_v.setOnClickListener(itemTitleClickListener(itemId));// itemTitleをクリックしたときにitemTitleClickListener()を実行するitemIcon_v.setClickable(true);itemIcon_v.setOnClickListener(itemIconClickListener(itemTitle));}// 接続を解除するDefaultHttpClient.getConnectionManager().shutdown();}catch (ClientProtocolException e){Log.d(LogTag + ” ClientProtocolException”, e.getMessage());}catch (IOException e){Log.d(LogTag + ” IOException”, e.getMessage());}catch(Exception e){Log.d(LogTag + ” Exception”, e.getMessage());}if (dialog != null && dialog.isShowing()) {dialog.dismiss();}}// クリック時に呼ばれるprivate View.OnClickListener itemTitleClickListener(final String itemId){return new View.OnClickListener() {@Overridepublic void onClick(View view){Toast.makeText(getApplicationContext(),“itemId =” + itemId ,Toast.LENGTH_SHORT).show();}};}// クリック時に呼ばれるprivate View.OnClickListener itemIconClickListener(final String string){return new View.OnClickListener() {@Overridepublic void onClick(View view){Toast.makeText(getApplicationContext(),“title =” + string ,Toast.LENGTH_SHORT).show();}};}public class LoadImageTask extends AsyncTask<String, Void, Bitmap> {// アイコンを表示するビューprivate ImageView imageView;// タスクごとのプログレスバーprivate ProgressBar progressBar;// コンストラクタpublic LoadImageTask(ImageView imageView, ProgressBar progressBar) {this.imageView = imageView;this.progressBar = progressBar;}// バックグラウンドで行う時間のかかる処理をします。@Overrideprotected Bitmap doInBackground(String… urls) {Bitmap img = null;try {Log.d(LogTag+” loadImageTask url : “,urls[0]);URL imageUrl = new URL(urls[0]);HttpURLConnection itemIconCon = (HttpURLConnection)(imageUrl).openConnection();InputStream is = itemIconCon.getInputStream();img = BitmapFactory.decodeStream(is);} catch (MalformedURLException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}return img;}// doInBackgroundメソッドの処理終了後、UIスレッドに返します@Overrideprotected void onPostExecute(Bitmap result) {// キャンセルされていたらなにもしないif (isCancelled()) {result = null;}if (result != null) {if (imageView != null) {progressBar.setVisibility(View.GONE);imageView.setVisibility(View.VISIBLE);imageView.setImageBitmap(result);}}}}}hogehoge_sub.xml も修正
<?xml version=“1.0” encoding=“utf-8”?><LinearLayout xmlns:android=“http://schemas.android.com/apk/res/android”android:layout_width=“fill_parent”android:layout_height=“fill_parent”android:layout_margin=“10dp”android:orientation=“vertical” ><LinearLayoutandroid:orientation=“horizontal”android:layout_width=“fill_parent”android:layout_height=“wrap_content”android:gravity=“center_vertical”android:background=“#F0FFF0”><ProgressBarandroid:id=“@+id/progressBarIcon”style=“?android:attr/progressBarStyleSmall”android:layout_width=“wrap_content”android:layout_height=“wrap_content”android:adjustViewBounds=“true”android:scaleType=“centerInside”android:layout_margin=“10dp”android:visibility=“visible”/><ImageViewandroid:id=“@+id/itemIcon”android:layout_width=“0dip”android:layout_weight=“.20”android:layout_height=“wrap_content”android:adjustViewBounds=“true”android:scaleType=“centerInside”android:layout_margin=“10dp”android:visibility=“gone”/><TextViewandroid:id=“@+id/itemTitle”android:layout_width=“0dip”android:layout_weight=“.80”android:layout_height=“wrap_content”android:textSize=“20sp”android:textStyle=“bold”android:layout_margin=“10dp”/></LinearLayout><LinearLayoutandroid:orientation=“vertical”android:layout_width=“fill_parent”android:layout_height=“wrap_content”android:gravity=“center_horizontal”android:layout_margin=“10dp”><TextViewandroid:id=“@+id/itemDetail”android:layout_width=“fill_parent”android:layout_height=“wrap_content”android:layout_marginLeft=“20dp”android:layout_marginRight=“20dp”/><ProgressBarandroid:id=“@+id/progressBarImage”style=“@id/progressBarImage”android:layout_width=“240dp”android:layout_height=“240dp”android:padding=“100dp”android:scaleType=“center”android:visibility=“visible” /><ImageViewandroid:id=“@+id/itemImage”android:layout_width=“wrap_content”android:layout_height=“wrap_content”android:scaleType=“fitCenter”android:adjustViewBounds=“true”android:visibility=“gone”/></LinearLayout></LinearLayout>・動作確認
ぐるぐる、、からの読み込み完了。おぉー、WEBサイトっぽい。
-
WEBサーバーから取得したJSONデータを複数表示 手順5
手順5、タイトルをクリックしたり画像をクリックしたらナニガシする
そうだな、タイトル文字をクリックしたら該当の記事の itemId がトーストされるようにしてみよう。
かつ、アイコン画像をクリックしたら該当の記事の itemTitle がトーストされるようにしてみよう。
ループ処理の中で該当のIDが取得出来るかどうか、新しい処理を起動出来るかどうか??
以下HogehogeActivity.javaの修正箇所を赤文字で記載しました。
package jp.example.hello;import java.io.IOException;import java.io.InputStream;import java.net.HttpURLConnection;import java.net.MalformedURLException;import java.net.URL;import org.apache.http.HttpResponse;import org.apache.http.HttpStatus;import org.apache.http.client.ClientProtocolException;import org.apache.http.client.HttpClient;import org.apache.http.client.methods.HttpGet;import org.apache.http.impl.client.DefaultHttpClient;import org.apache.http.util.EntityUtils;import org.json.JSONArray;import org.json.JSONObject;import android.app.Activity;import android.app.ProgressDialog;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.os.AsyncTask;import android.os.Bundle;import android.util.Log;import android.view.View;import android.widget.ImageView;import android.widget.LinearLayout;import android.widget.TextView;import android.widget.Toast;public class HogehogeActivity extends Activity {private static final String LogTag = “HogehogeLog”;private ProgressDialog dialog;@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.hogehoge);// jsonを取得して解析するLoadTask();}private String makeApiUrl(){return “http://akamako.com/blogger/sample.php”;}private void LoadTask(){// ProgressDialogを作成dialog = new ProgressDialog(this);dialog.setMessage(“Connect to Server.”);dialog.setCancelable(true);dialog.show();try{String apiUrl = makeApiUrl();// コンソールログに出力Log.d(LogTag+” apiUrl”,apiUrl);HttpGet get = new HttpGet(apiUrl);HttpClient DefaultHttpClient = new DefaultHttpClient();HttpResponse response = DefaultHttpClient.execute(get);// ステータスコードint status = response.getStatusLine().getStatusCode();Log.d(LogTag+” status”,String.valueOf(status));if (status != HttpStatus.SC_OK) {throw new Exception(“Error!”);}// 結果を取得String source = EntityUtils.toString(response.getEntity());Log.d(LogTag+” source”,source);// JSONObject という型があるんだってJSONObject json = new JSONObject(source);if (json.get(“items”) == JSONObject.NULL){throw new Exception(“Error!”);}if (json.get(“count”) == JSONObject.NULL){throw new Exception(“Error!”);}// 記事データJSONArray items = json.getJSONArray(“items”);// 記事の数String count = json.getString(“count”);if(count.equals(“0”) == true){// 記事データが無かったお}// items の中身の数int maxI = items.length();LinearLayout layout = (LinearLayout) findViewById(R.id.hogehoge_layout);// URL aURL;// URLConnection conn;// InputStream is;// BufferedInputStream bis;// Bitmap bm;for(int i = 0; i < maxI; i++) {// 1つ取り出すJSONObject item = items.getJSONObject(i);// 表示項目を初期化String itemId = “”;String itemTitle=“”;String itemDetail=“”;String itemIconUrl=“”;String itemImageUrl=“”;if(item.has(“id”)) {itemId = item.getString(“id”);}if(item.has(“title”)) {itemTitle = item.getString(“title”);}if(item.has(“detail”)) {itemDetail = item.getString(“detail”);}if(item.has(“icon”)) {itemIconUrl = item.getString(“icon”);}if(item.has(“image”)) {itemImageUrl = item.getString(“image”);}Log.d(LogTag + ” itemId”, itemId);Log.d(LogTag + ” itemTitle”, itemTitle);Log.d(LogTag + ” itemDetail”, itemDetail);Log.d(LogTag + ” itemIconUrl”, itemIconUrl);Log.d(LogTag + ” itemImageUrl”, itemImageUrl);// layout/hogehoge_list.xml が1記事のテンプレートですView view = getLayoutInflater().inflate(R.layout.hogehoge_sub, null);layout.addView(view);// 各表示項目のスキーマ(?)を取り出すTextView itemId_v = (TextView) view.findViewById(R.id.itemId);TextView itemTitle_v = (TextView) view.findViewById(R.id.itemTitle);TextView itemDetail_v = (TextView) view.findViewById(R.id.itemDetail);// TextView itemIcon_v = (TextView) view.findViewById(R.id.itemIcon);// TextView itemImage_v = (TextView) view.findViewById(R.id.itemImage);ImageView itemIcon_v = (ImageView) view.findViewById(R.id.itemIcon);ImageView itemImage_v = (ImageView) view.findViewById(R.id.itemImage);// 該当の表示個所に当て込むitemId_v.setText(itemId);itemTitle_v.setText(itemTitle);itemDetail_v.setText(itemDetail);// itemIcon_v.setText(itemIconUrl);// itemImage_v.setText(itemImageUrl);// aURL = new URL(itemIconUrl);// conn = aURL.openConnection();// conn.connect();// is = conn.getInputStream();// bis = new BufferedInputStream(is);// bm = BitmapFactory.decodeStream(bis);// bis.close();// is.close();// itemIcon_v.setImageBitmap(bm);//// aURL = new URL(itemImageUrl);// conn = aURL.openConnection();// conn.connect();// is = conn.getInputStream();// bis = new BufferedInputStream(is);// bm = BitmapFactory.decodeStream(bis);// bis.close();// is.close();// itemImage_v.setImageBitmap(bm);new LoadImageTask(itemIcon_v).execute(itemIconUrl);new LoadImageTask(itemImage_v).execute(itemImageUrl);// クリックしたときにitemTitleClickListener()を実行するitemTitle_v.setClickable(true);itemTitle_v.setOnClickListener(itemTitleClickListener(itemId));// itemTitleをクリックしたときにitemTitleClickListener()を実行するitemIcon_v.setClickable(true);itemIcon_v.setOnClickListener(itemIconClickListener(itemIconUrl));}// 接続を解除するDefaultHttpClient.getConnectionManager().shutdown();}catch (ClientProtocolException e){Log.d(LogTag + ” ClientProtocolException”, e.getMessage());}catch (IOException e){Log.d(LogTag + ” IOException”, e.getMessage());}catch(Exception e){Log.d(LogTag + ” Exception”, e.getMessage());}if (dialog != null && dialog.isShowing()) {dialog.dismiss();}}// クリック時に呼ばれるprivate View.OnClickListener itemTitleClickListener(final String itemId){return new View.OnClickListener() {@Overridepublic void onClick(View view){Toast.makeText(getApplicationContext(),“itemId =” + itemId ,Toast.LENGTH_SHORT).show();}};}// クリック時に呼ばれるprivate View.OnClickListener itemIconClickListener(final String string){return new View.OnClickListener() {@Overridepublic void onClick(View view){Toast.makeText(getApplicationContext(),“url =” + string ,Toast.LENGTH_SHORT).show();}};}public class LoadImageTask extends AsyncTask<String, Void, Bitmap> {// アイコンを表示するビューprivate ImageView imageView;// コンストラクタpublic LoadImageTask(ImageView imageView) {this.imageView = imageView;}// バックグラウンドで行う時間のかかる処理をします。@Overrideprotected Bitmap doInBackground(String… urls) {Bitmap img = null;try {Log.d(LogTag+” loadImageTask url : “,urls[0]);URL imageUrl = new URL(urls[0]);HttpURLConnection itemIconCon = (HttpURLConnection)(imageUrl).openConnection();InputStream is = itemIconCon.getInputStream();img = BitmapFactory.decodeStream(is);} catch (MalformedURLException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}return img;}// doInBackgroundメソッドの処理終了後、UIスレッドに返します@Overrideprotected void onPostExecute(Bitmap result) {// キャンセルされていたらなにもしないif (isCancelled()) {result = null;}if (result != null) {if (imageView != null) {imageView.setImageBitmap(result);}}}}}
クリック動作を判別してくれる View.OnClickListener というインターフェース を利用したのです。
itemTitle_v.setClickable(true); でクッリックできるようにしてあげます。
itemTitle_v.setOnClickListener(itemTitleClickListener(itemId)); でクリックしたときにitemTitleClickListener()が実行されます。
関数の記述が我々PHP WEBエンジニアにとっては謎です。
private View.OnClickListener itemTitleClickListener(final String itemId)
View.OnClickListener はreturnされるオブジェクトの型ですね。引数の final String itemId って部分は、何でfinalなんだろうかねえ。ループ処理内で利用されているから?わからないです。・動作確認
まずはタイトルをくりっくしてみると、、ループ処理内での itemId が表示されました。
今度はアイコン画像をクリックしてみると、、
該当の記事がトーストされたー!やたー!
-
WEBサーバーから取得したJSONデータを複数表示 手順4
手順4、画像 http://なんちゃら.jpg を表示する、しかも非同期処理で
手順3では画面に文字列を出力することができたものの、http://なんちゃら.jpg の様な画像ファイルを表示する事ができてません。
HTML であれば <img src=”http://なんちゃら.jpg”> とすれば、ブラウザ上に表示してくれるのですが、世界はAndroidアプリですのでHTMLタグは使えません。
(WebViewというものを用いれば利用出来ますが・・)Androidアプリでは ImageView と Bitmap を用いて画像を描画します。
また、画像データをインターネット経由で取得し秒がするという処理は少し時間がかかる処理なので、メインのUIスレッド(画面に文字を表示する処理)とは別の非同期のスレッド(画像を読み込んで読み込み終わったら画面に出力する処理)を走らせてあげる必要があります、とどっかのサイトに書いてありました。
・画像URLを画像として表示するためにレイアウトファイルの修正
hogehoge_sub.xml を修正します。赤字が修正したところ。 TextViewからImageViewにしたよ。<?xml version=“1.0” encoding=“utf-8”?><LinearLayoutxmlns:android=“http://schemas.android.com/apk/res/android”android:orientation=“vertical”android:layout_width=“wrap_content”android:layout_height=“fill_parent”><TextViewandroid:id=“@+id/itemId”android:layout_width=“fill_parent”android:layout_height=“wrap_content”/><TextViewandroid:id=“@+id/itemTitle”android:layout_width=“fill_parent”android:layout_height=“wrap_content”/><TextViewandroid:id=“@+id/itemDetail”android:layout_width=“fill_parent”android:layout_height=“wrap_content”/><ImageViewandroid:id=“@+id/itemIcon”android:layout_width=“wrap_content”android:layout_height=“wrap_content”/><ImageViewandroid:id=“@+id/itemImage”android:layout_width=“wrap_content”android:layout_height=“wrap_content”/></LinearLayout>・画像URLを画像として表示させるためのアクティビティを修正
HTMLなら画像タグ<img src=”なんちゃら.jpg”> で完了なんだけどもなぁ。何行も追加することになりましたね。まずは非同期処理ではなく単純に画像を表示させてみます。package jp.example.hello;import java.io.BufferedInputStream;import java.io.IOException;import java.io.InputStream;import java.net.URL;import java.net.URLConnection;import org.apache.http.HttpResponse;import org.apache.http.HttpStatus;import org.apache.http.client.ClientProtocolException;import org.apache.http.client.HttpClient;import org.apache.http.client.methods.HttpGet;import org.apache.http.impl.client.DefaultHttpClient;import org.apache.http.util.EntityUtils;import org.json.JSONArray;import org.json.JSONObject;import android.app.Activity;import android.app.ProgressDialog;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.os.Bundle;import android.util.Log;import android.view.View;import android.widget.ImageView;import android.widget.LinearLayout;import android.widget.TextView;public class HogehogeActivity extends Activity {privatestaticfinalString LogTag = “HogehogeLog”;private ProgressDialog dialog;@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.hogehoge);// jsonを取得して解析するLoadTask();}private StringmakeApiUrl(){return “http://akamako.com/blogger/sample.php”;}privatevoidLoadTask(){// ProgressDialogを作成dialog = new ProgressDialog(this);dialog.setMessage(“Connect to Server.”);dialog.setCancelable(true);dialog.show();try{String apiUrl = makeApiUrl();// コンソールログに出力Log.d(LogTag+” apiUrl”,apiUrl);HttpGet get = new HttpGet(apiUrl);HttpClient DefaultHttpClient = new DefaultHttpClient();HttpResponse response = DefaultHttpClient.execute(get);// ステータスコードint status = response.getStatusLine().getStatusCode();Log.d(LogTag+” status”,String.valueOf(status));if (status != HttpStatus.SC_OK) {throw new Exception(“Error!”);}// 結果を取得String source =EntityUtils.toString(response.getEntity());Log.d(LogTag+” source”,source);// JSONObject という型があるんだってJSONObject json = new JSONObject(source);if (json.get(“items”) == JSONObject.NULL){throw new Exception(“Error!”);}if (json.get(“count”) == JSONObject.NULL){throw new Exception(“Error!”);}// 記事データJSONArray items = json.getJSONArray(“items”);// 記事の数String count = json.getString(“count”);if(count.equals(“0”) == true){// 記事データが無かったお}// items の中身の数int maxI = items.length();LinearLayout layout = (LinearLayout) findViewById(R.id.hogehoge_layout);URL aURL;URLConnection conn;InputStream is;BufferedInputStream bis;Bitmap bm;for(int i = 0; i < maxI; i++) {// 1つ取り出すJSONObject item = items.getJSONObject(i);// 表示項目を初期化String itemId = “”;String itemTitle=“”;String itemDetail=“”;String itemIconUrl=“”;String itemImageUrl=“”;if(item.has(“id”)) {itemId = item.getString(“id”);}if(item.has(“title”)) {itemTitle = item.getString(“title”);}if(item.has(“detail”)) {itemDetail = item.getString(“detail”);}if(item.has(“icon”)) {itemIconUrl = item.getString(“icon”);}if(item.has(“image”)) {itemImageUrl = item.getString(“image”);}Log.d(LogTag + ” itemId”, itemId);Log.d(LogTag + ” itemTitle”, itemTitle);Log.d(LogTag + ” itemDetail”, itemDetail);Log.d(LogTag + ” itemIconUrl”, itemIconUrl);Log.d(LogTag + ” itemImageUrl”, itemImageUrl);// layout/hogehoge_list.xml が1記事のテンプレートですView view = getLayoutInflater().inflate(R.layout.hogehoge_sub, null);layout.addView(view);// 各表示項目のスキーマ(?)を取り出すTextView itemId_v = (TextView) view.findViewById(R.id.itemId);TextView itemTitle_v = (TextView) view.findViewById(R.id.itemTitle);TextView itemDetail_v = (TextView) view.findViewById(R.id.itemDetail);// TextView itemIcon_v = (TextView) view.findViewById(R.id.itemIcon);// TextView itemImage_v = (TextView) view.findViewById(R.id.itemImage);ImageView itemIcon_v = (ImageView) view.findViewById(R.id.itemIcon);ImageView itemImage_v = (ImageView) view.findViewById(R.id.itemImage);// 該当の表示個所に当て込むitemId_v.setText(itemId);itemTitle_v.setText(itemTitle);itemDetail_v.setText(itemDetail);// itemIcon_v.setText(itemIconUrl);// itemImage_v.setText(itemImageUrl);aURL = new URL(itemIconUrl);conn = aURL.openConnection();conn.connect();is = conn.getInputStream();bis = new BufferedInputStream(is);bm = BitmapFactory.decodeStream(bis);bis.close();is.close();itemIcon_v.setImageBitmap(bm);aURL = new URL(itemImageUrl);conn = aURL.openConnection();conn.connect();is = conn.getInputStream();bis = new BufferedInputStream(is);bm = BitmapFactory.decodeStream(bis);bis.close();is.close();itemImage_v.setImageBitmap(bm);}// 接続を解除するDefaultHttpClient.getConnectionManager().shutdown();}catch (ClientProtocolException e){Log.d(LogTag + ” ClientProtocolException”, e.getMessage());}catch (IOException e){Log.d(LogTag + ” IOException”, e.getMessage());}catch(Exception e){Log.d(LogTag + ” Exception”, e.getMessage());}if (dialog != null && dialog.isShowing()) {dialog.dismiss();}}}・動作確認
うまくいくかなぁやたー!写真が表示されたったー!あれ、でもスクロールされないから下の方の記事データが見れない・・・・スクロールビュー
スクロールビューを使うと画面上にスクロールできる枠を作れるらしい。なんとなく 子ビューのhogehoge_sub.xmlではなく、親ビューhogehoge.xmlで子ビューが当て込まれるLinearLayoutをScrollViewで囲ってあげれば良いのではないか?!(直感)<?xml version=“1.0” encoding=“utf-8”?><ScrollView xmlns:android=“http://schemas.android.com/apk/res/android”android:id=“@+id/ScrollView01”android:layout_width=“fill_parent”android:layout_height=“fill_parent”><LinearLayoutandroid:id=“@+id/hogehoge_layout”android:orientation=“vertical”android:layout_height=“wrap_content“android:layout_width=“fill_parent”></LinearLayout></ScrollView>・動作確認
おぉ!うまくスクロールになった!マグレ!ラッキー!でもやっぱ表示されるまで多少時間がかかるね。数秒。・非同期処理をアクティビティに埋め込む
さてここからですな。
非同期処理を実装してみよう。
非同期処理で重い処理(画像の読み込み・表示部分)を任せると、テキストデータだけパッと表示されて、しばらくすると画像が現れ始めるオシャレな動きになるはずや!
jqueryでいうところのajaxの様に、容量の大きな画像を読み込んでいる間やサーバー側の更新処理の完了待ちの間、画面を動かす事ができる。と。
やっぱり画面が固まるってのは、近年では”ダサい”と思われちゃう。大変な世の中です。Android開発の必殺技AsyncTasc超基本形(超コピペ用)
public class hogeHogeTask extends AsyncTask<Void, Void, Void>{// コンストラクタpublic hogeHogeTask(){}// 最初にUIスレッドで呼び出されます。UIに関わる処理をします。protected void onPreExecute() {}// バックグラウンドで行う時間のかかる処理をします。protected Void doInBackground(Void… params) {return null;}// doInBackgroundメソッドの処理終了後、UIスレッドに返しますprotected void onPostExecute(Void result){}}このクラスを取り敢えずHogehogeアクティビティのプライベートクラスなどで定義してみます。
この書き方を最初に目にした時は
AsyncTask<Void, Void, Void> の部分と (Void… params) の部分が全く意味が分からず「うぅ!(嫌悪)」とドギマギしました。
引数にさ … って何やねん!しゃっきりせい! って。
大丈夫。これらの書き方は単なる 呪文(決まり事)らしいです。
ここでは説明を省きますが、詳しくはGoogle先生に聞いて下さいね(笑)クラス名は、画像をロードするから LoadImageTask にしよっと。
HogehogeActivity.java に追記した所が赤文字、コメントアウトしたところが灰色です。
package jp.example.hello;import java.io.IOException;import java.io.InputStream;import java.net.HttpURLConnection;import java.net.MalformedURLException;import java.net.URL;import org.apache.http.HttpResponse;import org.apache.http.HttpStatus;import org.apache.http.client.ClientProtocolException;import org.apache.http.client.HttpClient;import org.apache.http.client.methods.HttpGet;import org.apache.http.impl.client.DefaultHttpClient;import org.apache.http.util.EntityUtils;import org.json.JSONArray;import org.json.JSONObject;import android.app.Activity;import android.app.ProgressDialog;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.os.AsyncTask;import android.os.Bundle;import android.util.Log;import android.view.View;import android.widget.ImageView;import android.widget.LinearLayout;import android.widget.TextView;public class HogehogeActivity extends Activity {private static final String LogTag = “HogehogeLog”;private ProgressDialog dialog;@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.hogehoge);// jsonを取得して解析するLoadTask();}private String makeApiUrl(){return “http://akamako.com/blogger/sample.php”;}private void LoadTask(){// ProgressDialogを作成dialog = new ProgressDialog(this);dialog.setMessage(“Connect to Server.”);dialog.setCancelable(true);dialog.show();try{String apiUrl = makeApiUrl();// コンソールログに出力Log.d(LogTag+” apiUrl”,apiUrl);HttpGet get = new HttpGet(apiUrl);HttpClient DefaultHttpClient = new DefaultHttpClient();HttpResponse response = DefaultHttpClient.execute(get);// ステータスコードint status = response.getStatusLine().getStatusCode();Log.d(LogTag+” status”,String.valueOf(status));if (status != HttpStatus.SC_OK) {throw new Exception(“Error!”);}// 結果を取得String source = EntityUtils.toString(response.getEntity());Log.d(LogTag+” source”,source);// JSONObject という型があるんだってJSONObject json = new JSONObject(source);if (json.get(“items”) == JSONObject.NULL){throw new Exception(“Error!”);}if (json.get(“count”) == JSONObject.NULL){throw new Exception(“Error!”);}// 記事データJSONArray items = json.getJSONArray(“items”);// 記事の数String count = json.getString(“count”);if(count.equals(“0”) == true){// 記事データが無かったお}// items の中身の数int maxI = items.length();LinearLayout layout = (LinearLayout) findViewById(R.id.hogehoge_layout);// URL aURL;// URLConnection conn;// InputStream is;// BufferedInputStream bis;// Bitmap bm;for(int i = 0; i < maxI; i++) {// 1つ取り出すJSONObject item = items.getJSONObject(i);// 表示項目を初期化String itemId = “”;String itemTitle=“”;String itemDetail=“”;String itemIconUrl=“”;String itemImageUrl=“”;if(item.has(“id”)) {itemId = item.getString(“id”);}if(item.has(“title”)) {itemTitle = item.getString(“title”);}if(item.has(“detail”)) {itemDetail = item.getString(“detail”);}if(item.has(“icon”)) {itemIconUrl = item.getString(“icon”);}if(item.has(“image”)) {itemImageUrl = item.getString(“image”);}Log.d(LogTag + ” itemId”, itemId);Log.d(LogTag + ” itemTitle”, itemTitle);Log.d(LogTag + ” itemDetail”, itemDetail);Log.d(LogTag + ” itemIconUrl”, itemIconUrl);Log.d(LogTag + ” itemImageUrl”, itemImageUrl);// layout/hogehoge_list.xml が1記事のテンプレートですView view = getLayoutInflater().inflate(R.layout.hogehoge_sub, null);layout.addView(view);// 各表示項目のスキーマ(?)を取り出すTextView itemId_v = (TextView) view.findViewById(R.id.itemId);TextView itemTitle_v = (TextView) view.findViewById(R.id.itemTitle);TextView itemDetail_v = (TextView) view.findViewById(R.id.itemDetail);// TextView itemIcon_v = (TextView) view.findViewById(R.id.itemIcon);// TextView itemImage_v = (TextView) view.findViewById(R.id.itemImage);ImageView itemIcon_v = (ImageView) view.findViewById(R.id.itemIcon);ImageView itemImage_v = (ImageView) view.findViewById(R.id.itemImage);// 該当の表示個所に当て込むitemId_v.setText(itemId);itemTitle_v.setText(itemTitle);itemDetail_v.setText(itemDetail);// itemIcon_v.setText(itemIconUrl);// itemImage_v.setText(itemImageUrl);// aURL = new URL(itemIconUrl);// conn = aURL.openConnection();// conn.connect();// is = conn.getInputStream();// bis = new BufferedInputStream(is);// bm = BitmapFactory.decodeStream(bis);// bis.close();// is.close();// itemIcon_v.setImageBitmap(bm);//// aURL = new URL(itemImageUrl);// conn = aURL.openConnection();// conn.connect();// is = conn.getInputStream();// bis = new BufferedInputStream(is);// bm = BitmapFactory.decodeStream(bis);// bis.close();// is.close();// itemImage_v.setImageBitmap(bm);new LoadImageTask(itemIcon_v).execute(itemIconUrl);new LoadImageTask(itemImage_v).execute(itemImageUrl);}// 接続を解除するDefaultHttpClient.getConnectionManager().shutdown();}catch (ClientProtocolException e){Log.d(LogTag + ” ClientProtocolException”, e.getMessage());}catch (IOException e){Log.d(LogTag + ” IOException”, e.getMessage());}catch(Exception e){Log.d(LogTag + ” Exception”, e.getMessage());}if (dialog != null && dialog.isShowing()) {dialog.dismiss();}}public class LoadImageTask extends AsyncTask<String, Void, Bitmap> {// アイコンを表示するビューprivate ImageView imageView;// コンストラクタpublic LoadImageTask(ImageView imageView) {this.imageView = imageView;}// バックグラウンドで行う時間のかかる処理をします。@Overrideprotected Bitmap doInBackground(String… urls) {Bitmap img = null;try {Log.d(LogTag+” loadImageTask url : “,urls[0]);URL imageUrl = new URL(urls[0]);HttpURLConnection itemIconCon = (HttpURLConnection)(imageUrl).openConnection();InputStream is = itemIconCon.getInputStream();img = BitmapFactory.decodeStream(is);} catch (MalformedURLException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}return img;}// doInBackgroundメソッドの処理終了後、UIスレッドに返します@Overrideprotected void onPostExecute(Bitmap result) {// キャンセルされていたらなにもしないif (isCancelled()) {result = null;}if (result != null) {if (imageView != null) {imageView.setImageBitmap(result);}}}}}クラスの中にクラスがある!!
PHPエンジニアからするとあまり見ない記述。
まぁeclipseでシンタックスエラーになってないからいいか。・動作確認
ドキドキ。。お!?ぬ、お!?おー!でけたー!徐々に画像が読み込まれてスクロールバーも徐々に長くなっていった。
うまくできましたねぇ。 -
WEBサーバーから取得したJSONデータを複数表示 手順3
手順3、TextViewで文字列を表示してみる
ViewHolderを利用するとViewを使い回せてメモリの消費量が押さえ
・・えぇい やかましい!
今は表示出来るようになる、という所に重点を。複数のデータを決まったフォーマットで縦に並べてくれる「ListView」に「ArrayAdapter」で「add」した項目を「ViewHolder」を用いてview形式を保持しながら表示させると、スクロールバーも自動ででるしメモリ対策にもなるので便利。。ということで当初はそのように開発してみたものの、後々にうまく出来ないところが他出しダメでした。ListViewは単純に3項目くらいのテキストの表示項目をリスト表示するのにはとても便利かもしれませんが、今回のように1記事あたりに複数のテキストデータと複数の画像を表示しなければならない場合は、子ビュー(サブビュー)を記事テンプレートとしてメインビューにaddviewで下部に追加していく方法で実装してみようと思います。
・子ビューの作成
1記事毎のレイアウトファイルを新規作成します。
layout/hogehoge_sub.xml というファイル名にしよっと。
<?xml version=“1.0” encoding=“utf-8”?><LinearLayoutxmlns:android=“http://schemas.android.com/apk/res/android”android:orientation=“vertical”android:layout_width=“wrap_content”android:layout_height=“fill_parent”><TextViewandroid:id=“@+id/itemId”android:layout_width=“fill_parent”android:layout_height=“wrap_content”/><TextViewandroid:id=“@+id/itemTitle”android:layout_width=“fill_parent”android:layout_height=“wrap_content”/><TextViewandroid:id=“@+id/itemDetail”android:layout_width=“fill_parent”android:layout_height=“wrap_content”/><TextViewandroid:id=“@+id/itemIcon”android:layout_width=“fill_parent”android:layout_height=“wrap_content”/><TextViewandroid:id=“@+id/itemImage”android:layout_width=“fill_parent”android:layout_height=“wrap_content”/></LinearLayout>手順1の記事データを画面に表示してみます。
・HogehogeActivityを修正
では先ほど作った子ビューを使い画面に表示する処理をHogehogeActivity.javaに追加していきます。赤字の部分が追加したコードです。package jp.example.hello;import java.io.IOException;import org.apache.http.HttpResponse;import org.apache.http.HttpStatus;import org.apache.http.client.ClientProtocolException;import org.apache.http.client.HttpClient;import org.apache.http.client.methods.HttpGet;import org.apache.http.impl.client.DefaultHttpClient;import org.apache.http.util.EntityUtils;import org.json.JSONArray;import org.json.JSONObject;import android.app.Activity;import android.app.ProgressDialog;import android.os.Bundle;import android.util.Log;import android.view.View;import android.widget.LinearLayout;import android.widget.TextView;public class HogehogeActivity extends Activity {private static final String LogTag = “HogehogeLog”;private ProgressDialog dialog;@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.hogehoge);// jsonを取得して解析するLoadTask();}private String makeApiUrl(){return “http://akamako.com/blogger/sample.php”;}private void LoadTask(){// ProgressDialogを作成dialog = new ProgressDialog(this);dialog.setMessage(“Connect to Server.”);dialog.setCancelable(true);dialog.show();try{String apiUrl = makeApiUrl();// コンソールログに出力Log.d(LogTag+” apiUrl”,apiUrl);HttpGet get = new HttpGet(apiUrl);HttpClient DefaultHttpClient = new DefaultHttpClient();HttpResponse response = DefaultHttpClient.execute(get);// ステータスコードint status = response.getStatusLine().getStatusCode();Log.d(LogTag+” status”,String.valueOf(status));if (status != HttpStatus.SC_OK) {throw new Exception(“Error!”);}// 結果を取得String source = EntityUtils.toString(response.getEntity());Log.d(LogTag+” source”,source);// JSONObject という型があるんだってJSONObject json = new JSONObject(source);if (json.get(“items”) == JSONObject.NULL){throw new Exception(“Error!”);}if (json.get(“count”) == JSONObject.NULL){throw new Exception(“Error!”);}// 記事データJSONArray items = json.getJSONArray(“items”);// 記事の数String count = json.getString(“count”);if(count.equals(“0”) == true){// 記事データが無かったお}// items の中身の数int maxI = items.length();LinearLayout layout = (LinearLayout) findViewById(R.id.hogehoge_layout);for(int i = 0; i < maxI; i++) {// 1つ取り出すJSONObject item = items.getJSONObject(i);// 表示項目を初期化String itemId = “”;String itemTitle=“”;String itemDetail=“”;String itemIconUrl=“”;String itemImageUrl=“”;if(item.has(“id”)) {itemId = item.getString(“id”);}if(item.has(“title”)) {itemTitle = item.getString(“title”);}if(item.has(“detail”)) {itemDetail = item.getString(“detail”);}if(item.has(“icon”)) {itemIconUrl = item.getString(“icon”);}if(item.has(“image”)) {itemImageUrl = item.getString(“image”);}Log.d(LogTag + ” itemId”, itemId);Log.d(LogTag + ” itemTitle”, itemTitle);Log.d(LogTag + ” itemDetail”, itemDetail);Log.d(LogTag + ” itemIconUrl”, itemIconUrl);Log.d(LogTag + ” itemImageUrl”, itemImageUrl);// layout/hogehoge_list.xml が1記事のテンプレートですView view = getLayoutInflater().inflate(R.layout.hogehoge_sub, null);layout.addView(view);// 各表示項目のスキーマ(?)を取り出すTextView itemId_v = (TextView) view.findViewById(R.id.itemId);TextView itemTitle_v = (TextView) view.findViewById(R.id.itemTitle);TextView itemDetail_v = (TextView) view.findViewById(R.id.itemDetail);TextView itemIcon_v = (TextView) view.findViewById(R.id.itemIcon);TextView itemImage_v = (TextView) view.findViewById(R.id.itemImage);// 該当の表示個所に当て込むitemId_v.setText(itemId);itemTitle_v.setText(itemTitle);itemDetail_v.setText(itemDetail);itemIcon_v.setText(itemIconUrl);itemImage_v.setText(itemImageUrl);}// 接続を解除するDefaultHttpClient.getConnectionManager().shutdown();}catch (ClientProtocolException e){Log.d(LogTag + ” ClientProtocolException”, e.getMessage());}catch (IOException e){Log.d(LogTag + ” IOException”, e.getMessage());}catch(Exception e){Log.d(LogTag + ” Exception”, e.getMessage());}if (dialog != null && dialog.isShowing()) {dialog.dismiss();}}・動作確認3
なぜかプログレスダイアログが表示されませんが、
記事のデータが画面に出力されました! わーい!
サーバーに通信してから画面表示するまでの数秒間、プログレスダイアログ(グルグル回るダイアログ)を表示したいのですが、表示されないのはUIスレッドでメインの処理を行っているからだそうです。
本当は、というか推奨としては、UIスレッドではHTTP通信を行うべきではないらしいので、追々直して行こう。
まずは表示できて嬉しいね。補足)
強制終了してしまう場合はLogCatでエラーログを確認してみるとE/AndroidRuntime( 372): java.lang.NullPointerException
こんなエラーを頻繁に見かけます。
これは略してヌルポと呼ばれるエラーで、定義されていないオブジェクトを代入しようとしたりするとエラーになるようです。
ループ処理の中のjsonデータが空だったり、オブジェクトが生成されていない等が考えられます。
どこでそのエラーが起きているか?を調べるには
Log.d(“test abc”,”abc”);
というデバッグログ1行をソースコードに記述し、ログが出力されるかどうかを調べます。
該当のアクティビティを実行したときにLogCat上に
D/test abc( 768): abc
というデバッグログが出力されればていればその場所は通過しているという事になります。
少しずつ後の方に記述を持って行き、デバッグログが出力されなくなったら、その直前の処理に何か問題があるということになります。
あとはGoogle先生に聞いてエラーを潰します。 -
WEBサーバーから取得したJSONデータを複数表示 手順2
手順2、手順1の通信で取得した記事データをループ処理の中で1記事ごとに取り出す
package jp.example.hello;
import java.io.IOException;
import org.apache.http.HttpResponse;import org.apache.http.HttpStatus;import org.apache.http.client.ClientProtocolException;import org.apache.http.client.HttpClient;import org.apache.http.client.methods.HttpGet;import org.apache.http.impl.client.DefaultHttpClient;import org.apache.http.util.EntityUtils;import org.json.JSONArray;import org.json.JSONObject;
import android.app.Activity;import android.app.ProgressDialog;import android.os.Bundle;import android.util.Log;
public class HogehogeActivity extends Activity {
private static final String LogTag = “HogehogeLog”;private ProgressDialog dialog;
@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.hogehoge);
// jsonを取得して解析するLoadTask();}
private String makeApiUrl(){return “http://akamako.com/blogger/sample.php”;}
private void LoadTask(){// ProgressDialogを作成dialog = new ProgressDialog(this);dialog.setMessage(“Connect to Server.”);dialog.setCancelable(true);dialog.show();
try{String apiUrl = makeApiUrl();
// コンソールログに出力Log.d(LogTag+” apiUrl”,apiUrl);
HttpGet get = new HttpGet(apiUrl);HttpClient DefaultHttpClient = new DefaultHttpClient();HttpResponse response = DefaultHttpClient.execute(get);
// ステータスコードint status = response.getStatusLine().getStatusCode();Log.d(LogTag+” status”,String.valueOf(status));if (status != HttpStatus.SC_OK) {throw new Exception(“Error!”);}
// 結果を取得String source = EntityUtils.toString(response.getEntity());
Log.d(LogTag+” source”,source);
// JSONObject という型があるんだってJSONObject json = new JSONObject(source);
// 記事データJSONArray items = json.getJSONArray(“items”);
// 記事の数String count = json.getString(“count”);
if(count.equals(“0”) == true){// 記事データが無かったお}
// items の中身の数int maxI = items.length();for(int i = 0; i < maxI; i++) {// 1つ取り出すJSONObject item = items.getJSONObject(i);// 表示項目を初期化String itemId = “”;String itemTitle=“”;String itemDetail=“”;String itemIconUrl=“”;String itemImageUrl=“”;if(item.has(“id”)) {itemId = item.getString(“id”);}if(item.has(“title”)) {itemTitle = item.getString(“title”);}if(item.has(“detail”)) {itemDetail = item.getString(“detail”);}if(item.has(“icon”)) {itemIconUrl = item.getString(“icon”);}if(item.has(“image”)) {itemImageUrl = item.getString(“image”);}Log.d(LogTag + ” itemId”, itemId);Log.d(LogTag + ” itemTitle”, itemTitle);Log.d(LogTag + ” itemDetail”, itemDetail);Log.d(LogTag + ” itemIconUrl”, itemIconUrl);Log.d(LogTag + ” itemImageUrl”, itemImageUrl);}DefaultHttpClient.getConnectionManager().shutdown();}catch (ClientProtocolException e){Log.d(LogTag + ” ClientProtocolException”, e.getMessage());}catch (IOException e){Log.d(LogTag + ” IOException”, e.getMessage());}catch(Exception e){Log.d(LogTag + ” Exception”, e.getMessage());}if (dialog != null && dialog.isShowing()) {dialog.dismiss();}}}
・動作確認
エミュレータを起動してHogehogeActivityに記述した Log.d(LogTag+” apiUrl”,apiUrl); がLogCat内に表示されるのを確認してください。
===========================08-16 20:56:15.438: D/HogehogeLog apiUrl(732): http://akamako.com/blogger/sample.php08-16 20:56:15.638: D/HogehogeLog status(732): 20008-16 20:56:15.648: D/HogehogeLog source(732): {“count”:4,”items”:[{“id”:”1234″,”title”:”\u30bf\u30a4\u30c8\u30eb\u3068\u304b1234″,”detail”:”\u8a73\u7d30\u5185\u5bb9\u3068\u304b\u8a73\u7d30\u5185\u5bb9\u3068\u304b\u8a73\u7d30\u5185\u5bb9\u3068\u304b1234″,”icon”:”http:\/\/akamako.com\/blogger\/pict\/1.jpg”,”image”:”http:\/\/akamako.com\/blogger\/pict\/2.jpg”},{“id”:”1235″,”title”:”\u30bf\u30a4\u30c8\u30eb\u3068\u304b1235″,”detail”:”\u8a73\u7d30\u5185\u5bb9\u3068\u304b\u8a73\u7d30\u5185\u5bb9\u3068\u304b\u8a73\u7d30\u5185\u5bb9\u3068\u304b1235″,”icon”:”http:\/\/akamako.com\/blogger\/pict\/3.jpg”,”image”:”http:\/\/akamako.com\/blogger\/pict\/4.jpg”},{“id”:”1236″,”title”:”\u30bf\u30a4\u30c8\u30eb\u3068\u304b1236″,”detail”:”\u8a73\u7d30\u5185\u5bb9\u3068\u304b\u8a73\u7d30\u5185\u5bb9\u3068\u304b\u8a73\u7d30\u5185\u5bb9\u3068\u304b1236″,”icon”:”http:\/\/akamako.com\/blogger\/pict\/5.jpg”,”image”:”http:\/\/akamako.com\/blogger\/pict\/6.jpg”},{“id”:”1237″,”title”:”\u30bf\u30a4\u30c8\u30eb\u3068\u304b1237″,”detail”:”\u8a73\u7d30\u5185\u5bb9\u3068\u304b\u8a73\u7d30\u5185\u5bb9\u3068\u304b\u8a73\u7d30\u5185\u5bb9\u3068\u304b1237″,”icon”:”http:\/\/akamako.com\/blogger\/pict\/7.jpg”,”image”:”http:\/\/akamako.com\/blogger\/pict\/8.png”}]}08-16 20:56:15.678: D/HogehogeLog itemId(732): 123408-16 20:56:15.678: D/HogehogeLog itemTitle(732): タイトルとか123408-16 20:56:15.678: D/HogehogeLog itemDetail(732): 詳細内容とか詳細内容とか詳細内容とか123408-16 20:56:15.678: D/HogehogeLog itemIconUrl(732): http://akamako.com/blogger/pict/1.jpg08-16 20:56:15.678: D/HogehogeLog itemImageUrl(732): http://akamako.com/blogger/pict/2.jpg08-16 20:56:15.688: D/HogehogeLog itemId(732): 123508-16 20:56:15.688: D/HogehogeLog itemTitle(732): タイトルとか123508-16 20:56:15.688: D/HogehogeLog itemDetail(732): 詳細内容とか詳細内容とか詳細内容とか123508-16 20:56:15.688: D/HogehogeLog itemIconUrl(732): http://akamako.com/blogger/pict/3.jpg08-16 20:56:15.698: D/HogehogeLog itemImageUrl(732): http://akamako.com/blogger/pict/4.jpg08-16 20:56:15.698: D/HogehogeLog itemId(732): 123608-16 20:56:15.698: D/HogehogeLog itemTitle(732): タイトルとか123608-16 20:56:15.698: D/HogehogeLog itemDetail(732): 詳細内容とか詳細内容とか詳細内容とか123608-16 20:56:15.698: D/HogehogeLog itemIconUrl(732): http://akamako.com/blogger/pict/5.jpg08-16 20:56:15.698: D/HogehogeLog itemImageUrl(732): http://akamako.com/blogger/pict/6.jpg08-16 20:56:15.698: D/HogehogeLog itemId(732): 123708-16 20:56:15.708: D/HogehogeLog itemTitle(732): タイトルとか123708-16 20:56:15.708: D/HogehogeLog itemDetail(732): 詳細内容とか詳細内容とか詳細内容とか123708-16 20:56:15.708: D/HogehogeLog itemIconUrl(732): http://akamako.com/blogger/pict/7.jpg08-16 20:56:15.708: D/HogehogeLog itemImageUrl(732): http://akamako.com/blogger/pict/8.png===========================
おぉーーーー!WEBサーバーからjsonデータを取得してjava内の変数(オブジェクト)に代入できたってことじゃね!?これらの表示データをAndroidの画面に出力(Viewにワタス?)できればよさそうだな。
-
WEBサーバーから取得したJSONデータを複数表示 手順1
手順1、WEBサーバーからHTTP経由でjson形式の複数の記事データを取得する
・Hogehogeアクティビティを作る
起動するアクティビティを作るよ。
ファイル名は HogehogeActivity.java となるかとおもわれ。
置き場所は hello/src/jp.example.hello 配下です。
アクティビティとは http://example.jp/〇〇.php の 〇〇.php にあたるものだ。多分。
package jp.example.hello;
import android.app.Activity;
import android.os.Bundle;
public class HogehogeActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.hogehoge);
}
}
おぉ短い。・res/layout/hogehoge.xmlをつくる
アクティビティで setContentView(R.layout.hogehoge); とすると hogehoge.xml を作らないとエラーになります。
エミュレーターが「アプリが予期せず停止しました。やり直してください。」等と言って起動してくれません。
こういう「んん?」っていうエラーでツマヅキまくるんだよね。
<?xml version=“1.0” encoding=“utf-8”?><LinearLayout xmlns:android=“http://schemas.android.com/apk/res/android” android:id = “@+id/mainLayout”android:orientation=“vertical”android:layout_height=“fill_parent”android:layout_width=“fill_parent”><ListView
android:id=“@+id/list”android:layout_width=“fill_parent”android:layout_height=“wrap_content”android:fadeScrollbars=“false”/></LinearLayout>・AndroidManifest.xmlを修正
まず、インターネットに接続しますよーというマニフェストをかかげるべく、AndroidManifest.xml に次の1行を追記します。
<uses-permission android:name=“android.permission.INTERNET”/>
次に、HogehogeActivity をアクティビティとして使いますよーというマニフェストを宣言すべく、AndroidManifest.xml に次の1行を追記します。
<activity android:name=“HogehogeActivity”></activity>・エミュレーターを起動し動作確認
eclipseメニューの「実行」→「実行」でエミュレータを起動させて動作確認。
はい。確認できましたね?さて次のステップに進みましょう。・・・みたいな感じの記事がばかりで困る!!
なぜかエミュレーターが起動しないんだよー。
筆者は様々なエラーでつまづく星の元に生まれたので、そのエラー例を以下に紹介します。・パッケージ内のどれかで 構文エラー
→解決策:eclipse 「パッケージ・エクスプローラー」内に 赤いバッテンのマーク がでている場合はそのファイルを開き、ソースコードの右側にあるバッテンをダブルクリックして解決策を読む。または自動的に保管してくれるeclipseの機能も多用する。・R.javaが見つかりません。と言われている気がするエラー
→解決策:eclipseのメニュー「プロジェクト」→「クリーン」してみる
→解決策:layout/なんちゃら.xml でエラーしてる可能性があるので直す・AndroidManifest.xmlに先ほど作成したHogehogeActivityの宣言が無くてエラー
→解決策:解決策の参考・主に画像ファイルを保存するdrawableディレクトリやdrawble-なんちゃらディレクトリに内のファイルのうち、大文字を含むファイル名のファイルが存在することによるエラー
→解決策:ファイル名に大文字を使用せず、小文字とかアンダースコアだけにリネームしましょう・Androidのプロジェクトビルドターゲットが違うことによるエラー
→解決策:今回のアプリはandroid 2.3.3以上で利用出来るようにしたい。「パッケージ・エクスプローラー」内の該当のパッケージディレクトリツリー内にAndroid 2.3.3 というライブラリ(?)が有るか確認。無ければ「パッケージ・エクスプローラー」の該当のパッケージ名を右クリック→「ビルド・パス」→「ビルド・パスの構成」→「リソース」以下の「Android」をクリック「プロジェクト・ビルド・ターゲット」を「Android 2.3.3」にしてください。その他、起動しない方に助言できる事は、
なんとかエミュレーターが起動して挙動が確認出来るまでガンガレ!!
ここを乗り切れれば、自分で要領を得て色々と対処できるようになるかと思われ。
・jsonデータを取得してみる
WEBサーバーからjsonデータを取得する。
HogehogeActivity.javaを修正します。
赤文字がHogehogeActivityに追記したところです。
package jp.example.hello;
import java.io.IOException;
import org.apache.http.HttpResponse;import org.apache.http.HttpStatus;import org.apache.http.client.ClientProtocolException;import org.apache.http.client.HttpClient;import org.apache.http.client.methods.HttpGet;import org.apache.http.impl.client.DefaultHttpClient;import org.apache.http.util.EntityUtils;
import android.app.Activity;import android.app.ProgressDialog;import android.os.Bundle;import android.util.Log;
public class HogehogeActivity extends Activity {
private static final String LogTag = “HogehogeLog”;private ProgressDialog dialog;
@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.hogehoge);
// jsonを取得して解析するLoadTask();}
private String makeApiUrl(){return “http://akamako.com/blogger/sample.php”;}
private void LoadTask(){// ProgressDialogを作成dialog = new ProgressDialog(this);dialog.setMessage(“Connect to Server.”);dialog.setCancelable(true);dialog.show();
try{String apiUrl = makeApiUrl();
// コンソールログに出力Log.d(LogTag+” apiUrl”,apiUrl);
HttpGet get = new HttpGet(apiUrl);HttpClient DefaultHttpClient = new DefaultHttpClient();HttpResponse response = DefaultHttpClient.execute(get);
// ステータスコードint status = response.getStatusLine().getStatusCode();Log.d(LogTag+” status”,String.valueOf(status));if (status != HttpStatus.SC_OK) {throw new Exception(“Error!”);}
// 結果を取得String source = EntityUtils.toString(response.getEntity());
Log.d(LogTag+” source”,source);DefaultHttpClient.getConnectionManager().shutdown();}catch (ClientProtocolException e){Log.d(LogTag + ” ClientProtocolException”, e.getMessage());}catch (IOException e){Log.d(LogTag + ” IOException”, e.getMessage());}catch(Exception e){Log.d(LogTag + ” Exception”, e.getMessage());}if (dialog != null && dialog.isShowing()) {dialog.dismiss();}}}なんかいきなり長くなっちったな。
・動作確認
エミュレータを起動して「ウインドウ」→「ビューの表示」→「その他」→「Android」以下の「LogCat」をクリックするとコンソールのようなものが開きます。開くかな? →ガンガレ!HogehogeActivityに記述した Log.d(LogTag+” source”,source); がLogCat内に表示されるのを確認してください。
===========================D/HogehogeLog source( 1466): {“count”:4,”items”:[{“id”:”1234″,”title”:”\u30bf\u30a4\u30c8\u30eb\u3068\u304b1234″,”detail”:”\u8a73\u7d30\u5185\u5bb9\u3068\u304b\u8a73\u7d30\u5185\u5bb9\u3068\u304b\u8a73\u7d30\u5185\u5bb9\u3068\u304b1234″,”icon”:”http:\/\/akamako.com\/blogger\/pict\/1.jpg”,”image”:”http:\/\/akamako.com\/blogger\/pict\/2.jpg”},{“id”:”1235″,”title”:”\u30bf\u30a4\u30c8\u30eb\u3068\u304b1235″,”detail”:”\u8a73\u7d30\u5185\u5bb9\u3068\u304b\u8a73\u7d30\u5185\u5bb9\u3068\u304b\u8a73\u7d30\u5185\u5bb9\u3068\u304b1235″,”icon”:”http:\/\/akamako.com\/blogger\/pict\/3.jpg”,”image”:”http:\/\/akamako.com\/blogger\/pict\/4.jpg”},{“id”:”1236″,”title”:”\u30bf\u30a4\u30c8\u30eb\u3068\u304b1236″,”detail”:”\u8a73\u7d30\u5185\u5bb9\u3068\u304b\u8a73\u7d30\u5185\u5bb9\u3068\u304b\u8a73\u7d30\u5185\u5bb9\u3068\u304b1236″,”icon”:”http:\/\/akamako.com\/blogger\/pict\/5.jpg”,”image”:”http:\/\/akamako.com\/blogger\/pict\/6.jpg”},{“id”:”1237″,”title”:”\u30bf\u30a4\u30c8\u30eb\u3068\u304b1237″,”detail”:”\u8a73\u7d30\u5185\u5bb9\u3068\u304b\u8a73\u7d30\u5185\u5bb9\u3068\u304b\u8a73\u7d30\u5185\u5bb9\u3068\u304b1237″,”icon”:”http:\/\/akamako.com\/blogger\/pict\/7.jpg”,”image”:”http:\/\/akamako.com\/blogger\/pict\/8.png”}]}
===========================キタコレ! 記事データがテキストで受け取れた!!