sqlite + ListViewでページング。くるくる付き。

---


###やりたいこと
・sqliteでデータとってきて、30行表示し、移動するときに「くるくる」を表示し、次に向かう。
・データの最後まできたら「これ以上データはない」的なメッセージを出す。
・sqliteだけじゃなくて、ネットからJSON取る場合にも汎用的に使いたい（これ重要）

###作ったファイル
1. MyActivity.java（説明なし）
2. Member.java （取り扱うデータ）_id,name,urlだけ。
3. MemberAdapter.java （上記を扱うためのカスタムアダプタ）
4. MyDbHelper.java （DBのヘルパー）
5. MyDataControl.java （データの取得を担うクラス。たぶん肝）

6. footer.xml （くるくる表示）
7. row.xml （1行ずつのView）

###その他
pull-to-refreshとかも付けたかったけど、複雑になるのでまた今度。
たぶん、refleshでadapter.add(item)じゃなくて、adapter.insert(item,0)とかやるとうまくいくのだろうと楽観視。

###ソース

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

    //

    //必要なもの初期化
    private MyDbHelper myDbHelper; //dbhelper
    private SQLiteDatabase db; //sqlite
    private ArrayList<Member> members; //ArrayList
    private ListView myListView; //ListView
    private View footerView; //FooterView くるくる用
    private AsyncTask<Integer,Void,ArrayList<Member>> myTask; //AsyncTask 追加処理を行う
    private MemberAdapter adapter; //adapter

    //カスタムのデータ取得クラス
    private MyDataControl myDataControl;

    //ページング関連
    private int page_rows = 30;
    private int page = 0;
    private int offset = page_rows * page;

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

        //DBを取得
        myDataControl = new MyDataControl(this);
        //arrayを取得
        members = new ArrayList(myDataControl.getPagingData(offset,page_rows));

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

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

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

        //adapter追加
        myListView.setAdapter(adapter);

        //scrollリスナ設定。これ忘れがち
        myListView.setOnScrollListener(this);
    }

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

    }

    @Override
    public void onScroll(AbsListView absListView, int i, int i2, int i3) {

        //最後なら
        if(i3 == i + i2){
            //データが無い
            if(i3 >= myDataControl.getCount()){
                myListView.removeFooterView(footerView);
                //これ、連続発生するので本当はflgで表示制御した方がよい。本質ではないので、とりあえずこのまま。
                Toast.makeText(this,"これ以上データはありません。",Toast.LENGTH_SHORT).show();
            }else {
                //データがある
                addItems();
            }
        }
    }

    private void addItems(){
        if(myTask != null && myTask.getStatus() == AsyncTask.Status.RUNNING){
            //何もしない
            return;
        }
        //ページをインクリメント
        page++;

        //非同期タスクを実行
        //pageを渡すもglobal変数にしてあるので意味は無い（引数のテスト）
        myTask = new MyAsyncTask(this).execute(page);
    }


    public class MyAsyncTask extends AsyncTask<Integer,Void,ArrayList<Member>>{

        public MyAsyncTask(MyActivity androidAsyncTaskActivity){

        }

        @Override
        protected ArrayList<Member> doInBackground(Integer... integers) {

            //ダミーの待ち時間（但し、演出として普通でも使う）
            try{
                Thread.sleep(1000);
            }catch(Exception e){
                e.printStackTrace();
            }

            //pageを受けとる
            //但し、gloal変数にしてあるので意味なし
            Log.v("tama","params="+Integer.toString(integers[0]));

            //offset計算
            offset = page * page_rows;

            //データを取得
            ArrayList members = myDataControl.getPagingData(offset,page_rows);
            return members;
        }

        @Override
        protected void onPostExecute(ArrayList<Member> members){

            //adapterに追加
            adapter.addAll(members);

        }
    }
}
```

```Member.java
public class Member {

    private int id;
    private String name;
    private String url;

    public int getId(){
        return this.id;
    }

    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);
        //inflaterを初期化（これ忘れがち）
        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);

        //本当はViewHolderにすべき（わかりやすさ重視）
        //View
        TextView name = (TextView)convertView.findViewById(R.id.text1);
        name.setText(member.getName());
        TextView url = (TextView)convertView.findViewById(R.id.text2);
        url.setText(member.getUrl());

        //Viewを戻す
        return convertView;
    }
}
```


```MyDbHelper.java
public class MyDbHelper extends SQLiteOpenHelper {

    //直接初期化
    public MyDbHelper(Context context) {
        super(context, "test.db", null, 2);
    }

    @Override
    public void onCreate(SQLiteDatabase sqLiteDatabase) {

        //_id
        //name
        //url : これは後で画像の取得に使うつもり

        //create
        sqLiteDatabase.execSQL("create table members(" +
                "_id integer primary key autoincrement," +
                "name text," +
                "url text)");

        //init（テストデータ生成）
        for(int i=0;i<100;i++) {

            String member_name = "member"+i;
            String member_url = String.format("%03d",i);

            sqLiteDatabase.execSQL("insert into members(name,url) values('"+member_name+"','http://www.bluecode.jp/test/image/"+member_url+".jpg')");
        }

    }

    @Override
    public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i2) {

        //delete
        sqLiteDatabase.execSQL("drop table if exists members");

        //
        onCreate(sqLiteDatabase);

    }
}
```


```MyDataControl.java
public class MyDataControl {

    //必要なものを初期化
    MyDbHelper myDbHelper;
    SQLiteDatabase db;
    ArrayList<Member> members;

    //コンストラクタ（DbOpenHelperが使うのでcontextをもらう）
    public MyDataControl(Context context){
        myDbHelper = new MyDbHelper(context);
    }

    //pagingしたデータをArrayList<Member>で返す
    //これ以下をネットからJSONを取得してArrayListで返すようなコードに書き換えれば、WebAPIにも使える（はず）
    public ArrayList<Member> getPagingData(int offset,int limit){

        //DB
        db = myDbHelper.getReadableDatabase();
        //ArrayList初期化
        members = new ArrayList<Member>();

        //取得
        //本当はContentProviderとか使うべきだけど、非同期プロセスから呼ばれるのでとりあえず直接取得。
        Cursor cursor = db.query(
                "members",
                null,
                null,
                null,
                null,
                null,
                null,
                Integer.toString(offset)+","+Integer.toString(limit)
        );

        //ArrayListにコピー（なんか無駄）
        while (cursor.moveToNext()){
            Member member = new Member();

            member.setName(cursor.getString(cursor.getColumnIndex("name")));
            member.setUrl(cursor.getString(cursor.getColumnIndex("url")));

            members.add(member);
        }

        //戻す
        return members;

    }

    //全てのデータ数を返す（おしりの判定に使う）
    public int getCount(){

        Cursor cursor = db.query(
                "members",
                null,
                null,
                null,
                null,
                null,
                null
        );

        int cnt = cursor.getCount();
        return cnt;
    }

}
```


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

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="wrap_content"
    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
    android:orientation="horizontal"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:padding="3dp"
    xmlns:android="http://schemas.android.com/apk/res/android">

    <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="wrap_content"
            android:layout_height="wrap_content"
            android:text="Hoge"
            android:id="@+id/text1"
            android:textSize="14dp" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="foo"
            android:id="@+id/text2"
            android:textSize="12dp" />
    </LinearLayout>
</LinearLayout>
```

あーしんど。