リストを引っ張って更新するライブラリshontauro/android-pulltorefresh-and-loadmoreを使ってみた

shontauro/android-pulltorefresh-and-loadmoreにあるライブラリは、リストを引っ張ってリストを更新するもので、星が371、Forkが200で、人気のあるライブラリのようです。このライブラリを、AsyncTaskLoaderとListFragmentを組み合わせたサンプルにNavigationViewを導入してみたで紹介したサンプルアプリ andropenguin/CustomListFragmentSample7で使ってみました。CustomListFragmentSample7 では、ライブラリを使用せず、リストを上に引っ張って内容更新を行っていましたが、今回は、shontauro/android-pulltorefresh-and-loadmore のライブラリを使います。また、shontauro/android-pulltorefresh-and-loadmore のサンプルでは、FragmentやAndroid Design Support LibraryのNavigationViewを使っていないので、今回、それらを使うようにしています。

IntelliJで、gradleでビルドするため、Gradle Android Moduleで新規プロジェクトを作り、プロジェクト直下に、shontauro/android-pulltorefresh-and-loadmore の pulltorefresh-and-loadmore ディレクトリをコピーし、settings.gradleに

include ':pulltorefresh-and-loadmore'

の行を追加します。また、app/build.gradleに

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:22.2.0'
    compile project(':pulltorefresh-and-loadmore')
    compile 'com.android.support:support-annotations:22.2.0'
    compile 'com.android.support:appcompat-v7:22.2.0'
    compile 'com.android.support:support-v4:22.2.0'
    compile 'com.android.support:design:22.2.0'
    debugCompile 'com.squareup.leakcanary:leakcanary-android:1.3.1'
    releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.3.1'
}

を記述します。

主要なレイアウトファイルは、次の通りです。

layout/activity_main.xml

<android.support.v4.widget.DrawerLayout
        android:id="@+id/main_drawer_layout"
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fitsSystemWindows="true">

    <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">

        <android.support.v7.widget.Toolbar
                android:id="@+id/main_tool_bar"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:background="?attr/colorPrimary"
                android:minHeight="?attr/actionBarSize"
                app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"/>

        <FrameLayout
                android:id="@+id/main_frame"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_below="@id/main_tool_bar"/>

    </RelativeLayout>

    <android.support.design.widget.NavigationView
            android:id="@+id/main_drawer_view"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_gravity="start"
            app:headerLayout="@layout/drawer_header"
            app:menu="@menu/main_drawer"/>

</android.support.v4.widget.DrawerLayout>

layout/drawer_header.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                android:layout_width="match_parent"
                android:layout_height="178dp"
                android:background="@color/secondary_text">

</RelativeLayout>

layout/list_item.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="match_parent">

    <TextView
            android:id="@+id/text"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text=""/>

</LinearLayout>

layout/loadmore.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:orientation="vertical" >

    <!-- We have to indicate that the listview is now a LoadMoreListView -->

    <com.costum.android.widget.LoadMoreListView
            android:id="@+id/android:list"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

</LinearLayout>

loadmore.xmlは、Fragmentのレイアウトファイルになっていて、com.costum.android.widget.LoadMoreListViewタグを内包します。

次に、リスト更新に関係するJavaファイルは次の通りです。

CustomListFragment.java

import android.os.Bundle;
import android.support.v4.app.ListFragment;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.Loader;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.costum.android.widget.LoadMoreListView;
import java.util.List;

public class CustomListFragment extends ListFragment
        implements LoadMoreListView.OnLoadMoreListener,
        LoaderManager.LoaderCallbacks<List<Entry>> {
    private CustomAdapter mCustomAdapter;
    private boolean mIsLoading = false;
    private int mCount = 0;

    private final static String TAG = CustomListFragment.class.getSimpleName();

    public CustomListFragment() {
    }

    public static CustomListFragment newInstance() {
        CustomListFragment fragment = new CustomListFragment();
        return fragment;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        if (container == null) {
            return null;
        }
        return inflater.inflate(R.layout.loadmore, container, false);
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        mCustomAdapter = new CustomAdapter(getActivity());
        setListAdapter(mCustomAdapter);
        ((LoadMoreListView)getListView()).setOnLoadMoreListener(this);
        mIsLoading = true;
        getLoaderManager().initLoader(0, null, this);
    }

    @Override
    public void onLoadMore() {
        Log.d(TAG, "mIsLoading = " + mIsLoading);
        // 既に読み込み中ならスキップ
        if (mIsLoading) {
            return;
        }
        mIsLoading = true;
        getLoaderManager().initLoader(mCount, null, this);
    }

    @Override
    public Loader<List<Entry>> onCreateLoader(int id, Bundle args) {
        return new CustomLoader(getActivity(), mCount);
    }

    @Override
    public void onLoadFinished(Loader<List<Entry>> loader, List<Entry> data) {
        // Call onLoadMoreComplete when the LoadMore task, has finished
        ((LoadMoreListView) getListView()).onLoadMoreComplete();
        mCustomAdapter.setData(data);
        mIsLoading = false;
        mCount++;
    }

    @Override
    public void onLoaderReset(Loader<List<Entry>> loader) {
        mCustomAdapter.setData(null);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();

        // java.lang.IllegalStateException: Content view not yet created となる。
//        ((LoadMoreListView)getListView()).setOnLoadMoreListener(null);
        setListAdapter(null);
        mCustomAdapter.setData(null);
        mCustomAdapter = null;
    }
}

pull-load-example3/app/src/main/java/com/sarltokyo/pull_load_example3/app/CustomListFragment.javaによると、リストを上に引っ張って内容を更新するには、com.costum.android.widget.LoadMoreListView クラスを使い、LoadMoreListView.OnLoadMoreListener インターフェースを実装すればいいようです。

onActivityCreatedメソッド内で、

((LoadMoreListView)getListView()).setOnLoadMoreListener(this);

によってリスナーを設定した後、AsyncTaskLoaderを初期化します。

onLoadMoreメソッドでは、リストを更新するので、mCountで、AsyncTaskLoaderを初期化し直します。

onLoadFinishedメソッドでは、リストの更新が終了したことを伝えるために、

((LoadMoreListView) getListView()).onLoadMoreComplete();

とします。また、mCountを1、増やします.

onDestroyメソッドでは、次のように、メモリーリーク対策を行っていますが、不十分なようです。

        setListAdapter(null);
        mCustomAdapter.setData(null);
        mCustomAdapter = null;

プロジェクトを、

andropenguin/pull-load-example3

に公開します。

参考サイト
[Android]OutOfMemoryError(メモリリーク)対策
Android Design Support Library を少しだけ触ってみました
shontauro/android-pulltorefresh-and-loadmore

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です