2013年12月7日土曜日

PullToRefreshで下からの引っ張りにも対応する(実装編)

さて、前回準備した環境で実際に「引っ張って更新」を試してみます。

いまもう27時とかめっちゃ眠いので文章で説明するより実際にコードを
見た方がわかりやすいかと思いますので、詳細はコードを貼るとして
とりあえず要点だけ。

・通常の ListView のかわりに PullToRefreshListView を使用

・上下両方の引っ張りに対応するよう Mode.BOTH を設定

・ PullToRefreshListView の setOnRefreshListener にて
 「引き下げ/上げて更新」の挙動を設定
 →OnRefreshListener2 で、引き下げ/上げ のそれぞれについて
  異なる動作を設定できます。
 →ただ、前回書いた通り、同名(PullToRefresh)の異なるライブラリを
  使用している場合、OnRefreshListener2 がなかったりしますので
  ご注意ください。


で、実際のサンプルコードはこんな感じになります。
(上から引っ張る→初期状態に戻す、下から引き上げ→項目を追加する)

■MainActivity のレイアウト(activity_main.xml)
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <com.handmark.pulltorefresh.library.PullToRefreshListView
        android:id="@+id/listView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>


■リストの項目用レイアウト(list_item.xml)
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="60dp"
    android:orientation="vertical" >

    <TextView
        android:id="@+id/listItemText"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center" />

</LinearLayout>


■コード(MainActivity.java)
public class MainActivity extends Activity {

    private ArrayList<String> mListItems;
    private PullToRefreshListView mListView;
    private SampleListAdapter mAdapter;

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

        //項目初期化
        initializeItems();

        //ListView 設定
        mListView = (PullToRefreshListView)findViewById(R.id.listView);
        mAdapter = new SampleListAdapter();
        mListView.setAdapter(mAdapter);

        //PullToRefresh 設定
        mListView.setMode(Mode.BOTH);//モード指定:上下からの引っ張りに対応
        mListView.setOnRefreshListener(new OnRefreshListener2<ListView>(){

            //上から引っ張った場合
            @Override
            public void onPullDownToRefresh(PullToRefreshBase<ListView> refreshView) {
                initializeList();
                new FinishRefresh().execute();
            }

            //下から引き上げた場合
            @Override
            public void onPullUpToRefresh(PullToRefreshBase<ListView> refreshView) {
                addItem();
                new FinishRefresh().execute();
            }

        });
    }

    //項目を初期化
    private void initializeItems(){
        mListItems = new ArrayList<String>(Arrays.asList("Item 1", "Item 2", "Item 3"));
    }

    //リストを初期状態に戻す
    private void initializeList(){
        initializeItems();
        mAdapter.notifyDataSetChanged();
    }

    //リストにアイテムを追加
    private void addItem(){
        int count = mListItems.size();
        mListItems.add("Item " + Integer.toString(++count));
        mAdapter.notifyDataSetChanged();
    }

    //アダプタクラス
    private class SampleListAdapter extends BaseAdapter{

        @Override
        public int getCount() {
            return mListItems.size();
        }

        @Override
        public Object getItem(int index) {
            return mListItems.get(index);
        }

        @Override
        public long getItemId(int index) {
            return index;
        }

        @Override
        public View getView(int index, View view, ViewGroup arg2) {
            if(view == null){
                LayoutInflater inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                view = inflater.inflate(R.layout.list_item, null);
            }
            TextView textView = (TextView)view.findViewById(R.id.listItemText);
            textView.setText(mListItems.get(index));
            return view;
        }
    }

    //リスト更新終了
    private class FinishRefresh extends AsyncTask<Void, Void, Void>{
        @Override
        protected Void doInBackground(Void... params) {
            return null;
        }

        @Override
        protected void onPostExecute(Void result){
            mListView.onRefreshComplete();//更新アニメーション終了
        }
    }
}


で、細かいことは確認してないんですが、どうやら onPullDownToRefresh
とかの中で直接  onRefreshComplete を呼んでも効かない模様?
(「更新中」状態のままとなってしまうみたいです。)

onPullDownToRefresh とかって非メインスレッドで呼ばれてるってこと
なんですかね?あ、「自分でソース確認しろ」とか言うの禁止ね。
 とりあえず↑みたいに AsyncTask の onPostExecute で呼び出してやると
期待する挙動になるみたいですが。。。


一応こんな感じで動きます。

【上から下に引っ張る】


【下から上に引き上げる】




どうでもいいけど、最近ホント夜更かしできなくなったなぁ…
昔はゲーム買ったら31時とかまで平気でやってたのになw
コレがアラサークオリティってやつか。

PullToRefreshで下からの引っ張りにも対応する(導入編)

最近(?)流行の ListView を下に引っ張ると更新する機能を
いざ組み込むべし、てことで。

ついでに上に引き上げた時もなんか動作つけたいなーとか思ったり。

ただ、コレって Android 標準ライブラリの機能じゃないんですな。
いくつかライブラリは公開されてますが PullToRefresh ってのが
一番ポピュラーっぽい。

それにしてもよく見る機能だから簡単に実装できるかと思ったら
意外とめんどいのね。


とりあえず今回は事前準備ということでライブラリの導入まで。
※そんなん言われんでもわかっとるわ、て方は次の回へどぞ。


0.前置き
「引っ張って更新」機能を使用するプロジェクトはもちろん
どんな名前でも構いませんが、とりあえずここでは P2RSample
しておきます。 そのへんは適宜読み替えてください。


1.ライブラリダウンロード
https://github.com/chrisbanes/Android-PullToRefresh
ココからダウンロードできます。
私みたいに Git 慣れてない方は↓から ZIP 取るのが良いかと。

解凍すると Android-PullToRefresh-master とかってフォルダができます。

※ちなみに、ほかに「PullToRefresh」でググると
 https://github.com/johannilsson/android-pulltorefresh
 とかも引っかかりますが、こっちは微妙に機能が違うみたいです。
 そっちを使っちゃうと次回「実装編」で「アレェッー!??」てなりますので
 ご注意を。


2.Eclipse にライブラリ追加
Eclipse の「ファイル」→「インポート」を選択。


で、Androidの既存コード追加を選択。
(「既存プロジェクトをワークスペースへ」でも問題なさそうですが。)


 追加するのは「Library」 フォルダだけでいいみたいっす。

※管理上、「プロジェクトをワークスペースにコピー」としておいた方が
 良いかと。 デスクトップとかにリンク貼られても困るし。


3.プロジェクトのプロパティで使用ライブラリ追加
P2RSample プロジェクトのプロパティからライブラリ追加の設定。


これで準備OKっす。

次回「実装編」はこのライブラリの機能を使って実装していきます。。。