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っす。

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