Android開発

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データを取得できるかをためす。

参考 : http://www.backlog.jp/git-guide/

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でソースコードをパクりながらコーディングしていこう。

参考)

Volley

GSON

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”?>
<LinearLayout
    xmlns: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”
>
    <LinearLayout
        android:orientation=“horizontal”
        android:layout_width=“fill_parent”
        android:layout_height=“wrap_content”
        android:gravity=“center_vertical”
        android:background=“#F0FFF0”
    >
        <ImageView
            android: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”
        />
       <TextView
            android: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>
    <TextView
        android:id=“@+id/itemId”
        android:layout_width=“fill_parent”
        android:layout_height=“wrap_content”
        android:visibility=“gone”
    />
    <TextView
        android:id=“@+id/itemDetail”
        android:layout_width=“fill_parent”
        android:layout_height=“wrap_content”
        android:layout_margin=“20dp”
    />
    <ImageView
        android: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;
 
    @Override
    public 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() {
            @Override
            public void onClick(View view){
                Toast.makeText(getApplicationContext(),“itemId =” + itemId ,Toast.LENGTH_SHORT).show();
            }
        };
    }
 
    // クリック時に呼ばれる
    private View.OnClickListener itemIconClickListener(final String string){
        return new View.OnClickListener() {
            @Override
            public 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;
        }
        // バックグラウンドで行う時間のかかる処理をします。
        @Override
        protected 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スレッドに返します
        @Override
        protected 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” >
 
    <LinearLayout
        android:orientation=“horizontal”
        android:layout_width=“fill_parent”
        android:layout_height=“wrap_content”
        android:gravity=“center_vertical”
        android:background=“#F0FFF0”
    >
        <ProgressBar
            android: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”
        />
        <ImageView
            android: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”
        />
       <TextView
            android: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>
 
    <LinearLayout
        android:orientation=“vertical”
        android:layout_width=“fill_parent”
        android:layout_height=“wrap_content”
        android:gravity=“center_horizontal”
        android:layout_margin=“10dp”
    >
        <TextView
            android:id=“@+id/itemDetail”
            android:layout_width=“fill_parent”
            android:layout_height=“wrap_content”
            android:layout_marginLeft=“20dp”
            android:layout_marginRight=“20dp”
        />
 
        <ProgressBar
            android:id=“@+id/progressBarImage”
            style=“@id/progressBarImage”
            android:layout_width=“240dp”
            android:layout_height=“240dp”
            android:padding=“100dp”
            android:scaleType=“center”
            android:visibility=“visible” />
 
        <ImageView
            android: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;
 
    @Override
    public 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() {
            @Override
            public void onClick(View view){
                Toast.makeText(getApplicationContext(),“itemId =” + itemId ,Toast.LENGTH_SHORT).show();
            }
        };
    }
 
    // クリック時に呼ばれる
    private View.OnClickListener itemIconClickListener(final String string){
        return new View.OnClickListener() {
            @Override
            public 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;
        }
        // バックグラウンドで行う時間のかかる処理をします。
        @Override
        protected 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スレッドに返します
    @Override
    protected 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”?>
<LinearLayout
xmlns:android=“http://schemas.android.com/apk/res/android”
android:orientation=“vertical”
android:layout_width=“wrap_content”
android:layout_height=“fill_parent”
>
<TextView
android:id=“@+id/itemId”
android:layout_width=“fill_parent”
android:layout_height=“wrap_content”
/>
<TextView
android:id=“@+id/itemTitle”
android:layout_width=“fill_parent”
android:layout_height=“wrap_content”
/>
<TextView
android:id=“@+id/itemDetail”
android:layout_width=“fill_parent”
android:layout_height=“wrap_content”
/>
<ImageView
android:id=“@+id/itemIcon”
android:layout_width=“wrap_content”
android:layout_height=“wrap_content”
/>
<ImageView
android: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;
@Override
public 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”>
<LinearLayout
android: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;
 
    @Override
    public 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;
        }
        // バックグラウンドで行う時間のかかる処理をします。
        @Override
        protected 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スレッドに返します
    @Override
    protected 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”?>
<LinearLayout
    xmlns:android=“http://schemas.android.com/apk/res/android”
    android:orientation=“vertical”
    android:layout_width=“wrap_content”
    android:layout_height=“fill_parent”
    >
    <TextView
        android:id=“@+id/itemId”
        android:layout_width=“fill_parent”
        android:layout_height=“wrap_content”
    />
    <TextView
        android:id=“@+id/itemTitle”
        android:layout_width=“fill_parent”
        android:layout_height=“wrap_content”
    />
    <TextView
        android:id=“@+id/itemDetail”
        android:layout_width=“fill_parent”
        android:layout_height=“wrap_content”
    />
    <TextView
        android:id=“@+id/itemIcon”
        android:layout_width=“fill_parent”
        android:layout_height=“wrap_content”
    />
    <TextView
        android: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;
 
    @Override
    public 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;

    @Override
    public 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.php

08-16 20:56:15.638: D/HogehogeLog status(732): 200

08-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): 1234

08-16 20:56:15.678: D/HogehogeLog itemTitle(732): タイトルとか1234

08-16 20:56:15.678: D/HogehogeLog itemDetail(732): 詳細内容とか詳細内容とか詳細内容とか1234

08-16 20:56:15.678: D/HogehogeLog itemIconUrl(732): http://akamako.com/blogger/pict/1.jpg
08-16 20:56:15.678: D/HogehogeLog itemImageUrl(732): http://akamako.com/blogger/pict/2.jpg
08-16 20:56:15.688: D/HogehogeLog itemId(732): 1235
08-16 20:56:15.688: D/HogehogeLog itemTitle(732): タイトルとか1235
08-16 20:56:15.688: D/HogehogeLog itemDetail(732): 詳細内容とか詳細内容とか詳細内容とか1235
08-16 20:56:15.688: D/HogehogeLog itemIconUrl(732): http://akamako.com/blogger/pict/3.jpg
08-16 20:56:15.698: D/HogehogeLog itemImageUrl(732): http://akamako.com/blogger/pict/4.jpg
08-16 20:56:15.698: D/HogehogeLog itemId(732): 1236
08-16 20:56:15.698: D/HogehogeLog itemTitle(732): タイトルとか1236
08-16 20:56:15.698: D/HogehogeLog itemDetail(732): 詳細内容とか詳細内容とか詳細内容とか1236
08-16 20:56:15.698: D/HogehogeLog itemIconUrl(732): http://akamako.com/blogger/pict/5.jpg
08-16 20:56:15.698: D/HogehogeLog itemImageUrl(732): http://akamako.com/blogger/pict/6.jpg
08-16 20:56:15.698: D/HogehogeLog itemId(732): 1237
08-16 20:56:15.708: D/HogehogeLog itemTitle(732): タイトルとか1237
08-16 20:56:15.708: D/HogehogeLog itemDetail(732): 詳細内容とか詳細内容とか詳細内容とか1237
08-16 20:56:15.708: D/HogehogeLog itemIconUrl(732): http://akamako.com/blogger/pict/7.jpg
08-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;

    @Override
    public 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”}]}
===========================

キタコレ! 記事データがテキストで受け取れた!!

WEBサーバーから取得したJSONデータを複数表示 手順0

手順0、WEBサーバーのAPIを用意する

WEBサーバーにJSON形式で記事データを吐き出すスクリプトを設置します。

ほいっとな

こんな↑スクリプトを作成します。

WEBエンジニア出身なのでこの行程は出来るんです。

ここまでは、ここまではできるんだ・・

何が分からないかって、このJSON記事データをAndroidアプリ上で表示するのやり方がよくわからなないのです。

HTML5 + CSS3 + jQuery でAndroidアプリを作成する方法(ハイブリットアプリ)やツールもあるけど、今回はネイティブアプリを作ってみたいのです。

次の行程へ

WEBサーバーから取得したJSONデータを複数表示


筆者はPHPの非凡WEBエンジニアです。

RSSリーダーみたいに非同期処理で複数の画像を読み込んだりテキストを表示するAndroidアプリを開発したい。

WEBエンジニアならおなじみの処理なのにAndroidアプリの開発となると、なかなか出来ないんだなぁ。

PHP? LAMP環境で色々できますよ。

Javascript? jqueryで色々できそうです。

Androidアプリ? WEBビューだけのアプリなら作れます。Googleプレイでしょ?


Java? 名前は知ってます!(読めません・・ましてや書けません)

そして家に帰ってからひっそり調べるのです。

Google先生ぇ! 「Android開発 画像 読み込み 非同期」教えて!

・・・約100時間後

苦ぅ! 調べども試せども、うまく動かない!

こ、、これは、、センスがねぇ!

Android開発の知識はついてきたけどもコーディングがおぼつかない。

Githubの世界の素晴らしいエンジニアの素晴らしいソースコードを参考にさせてもらったりしたけど、、なんかうまくできない。

そうじゃないんだよな、、多分ちゃんと自分でコーディングできる力が無いとダメだ。

そりゃ落ちてるソースちょこちょこっといじって出来ましたー、とかってあり得ないよね。

てか java書けないし、てか読めないし、そもそもビルドとかよく知らないしなぁ。

完成系はイメージできても、コーディング手順がわからんのです。

なので、

シュミレーターでデバッグしながら、0からコーディング手順をまとめてみようかと思たよ、ワタシ!

手順0、WEBサーバーのAPIを用意する

手順1、WEBサーバーからHTTP経由でjson形式の複数の記事データを取得する

手順2、手順1の通信でテキストデータは取得できてるので、TextViewでテキストだけ表示しちゃう

手順3、画像 http://なんちゃら.jpg を表示するには数秒時間がかかるので、非同期処理でおこなう

手順4、タイトルをクリックしたり画像をクリックしたらナニガシする

手順5、レイアウトを綺麗にする

という手順を コーディング→エミュレータで動作確認 を繰り返しながらよいアプリにしていくのが普通なんだと思います。

いきなり完成系のソースコードを見ても、とても理解できる気がしませんよね汗泣


以降の記事は起動アクティビティは既にできているものとして話を進めます。
Androidプロジェクト名は jp.example.hello です。
ターゲットSDKバージョンは2.3.3 APIレベルは10です。→参考
開発環境はeclipse JUNOを日本語化してます。