26
25

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Last updated at Posted at 2014-10-12

やりたいこと

・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>

あーしんど。

26
25
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
26
25

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?