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