JSON(Volley)+ListViewでページング。くるくる付き。

----
sqlite版を作り、JSON版もほぼ同じに作りたかったけど、経験と能力不足で挫折。とりあえず動くコードをメモ。

###やりたいこと
・WebAPI（PHP）からJSONを取得し、ページング表示。移動（待ち）時間には「くるくる」を表示。
・データの最後まできたら「これ以上データはない」的なメッセージを出す。

###作ったファイル
1. MyActivity.java （説明なし）
2. Member.java（取り扱うデータ）
3. MemberAdapter.java（カスタムアダプター）
4. MyData.java（データを非同期で取得するクラス）
5. footer.xml（フッター用レイアウト）
6. row.xml（ListViewの1行ずつのレイアウト）

###課題・考察
sqliteを使う場合と統合ができなかった。。。それは後日の課題とする。
最初の読み込み時だけは、処理を変えたかったけど、可読性重視で特に何もしてない。
（データがないとonScrollが発生するので、そのまま読み込みに利用）

###ポイント
MyData.javaにMyActivity.javaの方から、Volleyに必要なContextとRequestQueue。そして、非同期でデータ追加の対象となるadapterを引数で渡してやってます。

###ソース

```MyActivity.java
public class MyActivity extends Activity implements AbsListView.OnScrollListener{

    private RequestQueue myQueue;

    private ArrayList<Member> members;
    private MemberAdapter adapter;
    private ListView myListView;
    private View myFooter;

    private MyData myData;
    private AsyncTask<Void,Void,Void> myTask;

    //
    private int page = 0;
    private int page_rows = 20;
    private int offset = page * page_rows;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_my);

        //queue
        myQueue = Volley.newRequestQueue(this);

        //array list
        members = new ArrayList<Member>();

        //adapter
        adapter = new MemberAdapter(this,0,members);

        //ListView
        myListView = (ListView)findViewById(R.id.myListView);
        myListView.setAdapter(adapter);

        //footer
        myFooter = getLayoutInflater().inflate(R.layout.footer,null);
        myListView.addFooterView(myFooter);

        //リスナー設定（忘れがち）
        myListView.setOnScrollListener(this);

        //getdata（初期化）
        myData = new MyData(this,myQueue,adapter);
        //ここで最初のデータをとってもいいけど、とりあえず同じロジックであとまわし
        //myData.getData();

    }

    @Override
    public void onScrollStateChanged(AbsListView absListView, int i) {
        //
    }

    @Override
    public void onScroll(AbsListView absListView, int i, int i2, int i3) {
        if(i3 == i + i2){
            //データの最後を判別（本当はgetCount関数等を使って取得する）
            if(i3>=100){
                myListView.removeFooterView(myFooter);
                Toast.makeText(this,"これ以上データはありません。",Toast.LENGTH_SHORT).show();
                return;
            }
            addItems();
        }
    }

    //追加用関数
    private void addItems(){

        //すでに処理中であれば何もしない
        if(myTask != null && myTask.getStatus() == AsyncTask.Status.RUNNING){
            return;
        }

        //タスク実行
        myTask = new MyAsyncTask().execute();
    }

    //非同期クラス
    public class MyAsyncTask extends AsyncTask<Void,Void,Void>{

        @Override
        protected Void doInBackground(Void... voids) {

            //ダミーのくるくる表示
            try{
                Thread.sleep(1000);
            }catch(Exception e){
                e.printStackTrace();
            }
            offset = page * page_rows;

            //データを取得（そして追加）
            myData.getData(offset,page_rows);

            return null;
        }

        @Override
        protected void onPostExecute(Void aVoid) {
            super.onPostExecute(aVoid);

            //成功したらpageをインクリメント
            page++;

        }
    }
}

```

```Member.java
public class Member {

    private String name;
    private String url;

    public String getName(){
        return this.name;
    }

    public void setName(String name){
        this.name = name;
    }

    public String getUrl(){
        return this.url;
    }

    public void setUrl(String url){
        this.url = url;
    }
}
```

```MemberAdapter.java
public class MemberAdapter extends ArrayAdapter<Member>{

    private LayoutInflater layoutInflater;

    public MemberAdapter(Context context, int resource,ArrayList<Member> members) {
        super(context, resource,members);
        this.layoutInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent){

        if(convertView == null){
            convertView = layoutInflater.inflate(R.layout.row,null);
        }

        Member member = (Member)getItem(position);

        TextView name = (TextView)convertView.findViewById(R.id.name);
        name.setText(member.getName());
        TextView url = (TextView)convertView.findViewById(R.id.url);
        url.setText(member.getUrl());

        return convertView;
    }
}
```
まあ肝と言えば、肝のクラス。

```MyData.java
public class MyData {

    private RequestQueue myQueue;
    private MemberAdapter adapter;

    private ArrayList<Member> members;

    //コンストラクタ
    //コンテキストと、Volley用のQueue、そして追加処理をおこなうためのadapterを受け取る
    public MyData(Context context,RequestQueue myQueue,MemberAdapter adapter) {

        this.myQueue = myQueue;
        this.adapter = adapter;
    }

    //データの取得と更新（offsetとlimitを受け取る）
    public void getData(int offset,int limit){

        members = new ArrayList<Member>();

        String url = "http://www.bluecode.jp/test/getMembers.php?offset="+offset+"&limit="+limit;

        JsonObjectRequest request = new JsonObjectRequest(Request.Method.GET,url,null,
                new Response.Listener<JSONObject>() {
                    @Override
                    public void onResponse(JSONObject jsonObject) {

                        try{
                            JSONArray json_members = jsonObject.getJSONArray("members");
                            for(int i=0;i<json_members.length();i++){
                                JSONObject json_member = json_members.getJSONObject(i);

                                String name = json_member.getString("name");
                                String url = json_member.getString("url");

                                Member member = new Member();
                                member.setName(name);
                                member.setUrl(url);

                                members.add(member);

                            }

                            //adapterに追加・反映
                            adapter.addAll(members);

                        }catch (Exception e){
                            e.printStackTrace();
                        }

                    }
                }
                ,
                new Response.ErrorListener(){
                    @Override
                    public void onErrorResponse(VolleyError volleyError) {
                        //とりあえず何もしない
                    }
                }
        );

        //Queueに追加（JSON取得処理）
        myQueue.add(request);
    }

}
```

```footer.xml
<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal" android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center_vertical|center">

    <ProgressBar
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/progressBar" />
</LinearLayout>
```

```row.xml
<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="fill_parent"
    android:padding="3dp"
    android:layout_height="wrap_content">

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/ic_launcher"
        android:id="@+id/image" />

    <LinearLayout
        android:orientation="vertical"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content">

        <TextView
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:text="name"
            android:id="@+id/name" />

        <TextView
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:text="url"
            android:id="@+id/url" />
    </LinearLayout>
</LinearLayout>
```