sqlite版を作り、JSON版もほぼ同じに作りたかったけど、経験と能力不足で挫折。とりあえず動くコードをメモ。
###やりたいこと
・WebAPI(PHP)からJSONを取得し、ページング表示。移動(待ち)時間には「くるくる」を表示。
・データの最後まできたら「これ以上データはない」的なメッセージを出す。
###作ったファイル
- MyActivity.java (説明なし)
- Member.java(取り扱うデータ)
- MemberAdapter.java(カスタムアダプター)
- MyData.java(データを非同期で取得するクラス)
- footer.xml(フッター用レイアウト)
- 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>