Paing ListView (1ページずつスクロールするListView)
iPhoneアプリのNaver画像検索のような画象を縦に1ページずつ
めくって見るようなUIをAndroidでも実装してみた。
(Android版のNaver画象検索もiPhoneと同じような動きをすれば良いのにと
個人的に思う。)
ソースコード
とりあえずgithubにソースコードをアップした。
https://github.com/duyoji/PagingListView
PagingListView.java
package test.duyoji.listview; import test.duyoji.util.SizeUtil; import android.annotation.SuppressLint; import android.content.Context; import android.util.AttributeSet; import android.view.Display; import android.view.View; import android.view.WindowManager; import android.widget.AbsListView; import android.widget.ListView; @SuppressLint({ "NewApi", "NewApi" }) public class PagingListView extends ListView { private Context context; private int screenHeight; private int nowPage = 0; private int totalPage = 0; //constructor---------------------------------------------------------------- public PagingListView(Context context) { super(context); this.context = context; init(); } public PagingListView(Context context, AttributeSet attrs) { super(context, attrs); this.context = context; init(); } public PagingListView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); this.context = context; init(); } //interface------------------------------------------------------------------ public static interface OnPagingListener { public void onScrollStart(int _nowPage); public void onScrollFinish(int _nowPage); public void onNextListLoad(int _nowPage); } private OnPagingListener listener = null; public void setPagingListener(OnPagingListener listener) { this.listener = listener; } //setter, getter -------------------------------------------------------------- public void setNowPage(int _nowPage){ this.nowPage = _nowPage; } public void setTotalPage(int totalPage){ this.totalPage = totalPage; } public int getNowPage(){ return nowPage; } //paging scroll method--------------------------------------------------------------------------- public void scrollNextPage(){ View firstVisibleView = getChildAt(0); nowPage += 1; smoothScrollBy(screenHeight-SizeUtil.getStatusBarHeight(context)-Math.abs(firstVisibleView.getTop())-1, 400); } public void scrollPrevPage(){ View firstVisibleView = getChildAt(0); nowPage -= 1; smoothScrollBy(firstVisibleView.getTop(), 400); // Stops the listview from overshooting. } //initialize --------------------------------------------------------------- private void init(){ WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); Display display = wm.getDefaultDisplay(); this.screenHeight = display.getHeight(); setScrollEvent(); } //setOnScrollListener event----------------------------------------------------- private void setScrollEvent(){ setOnScrollListener(new OnScrollListener() { private boolean flingFlag = false; @Override public void onScrollStateChanged(AbsListView view, int scrollState) { switch (scrollState) { // スクロールしていない case OnScrollListener.SCROLL_STATE_IDLE: if (flingFlag) { flingFlag = false; if (listener != null) { listener.onScrollFinish(nowPage); } }else{ postDelayed(new Runnable() { public void run() { int savedPosition = getFirstVisiblePosition(); View firstVisibleView = getChildAt(0); if (firstVisibleView.getHeight()/2.0 < Math.abs(firstVisibleView.getTop())) { nowPage = savedPosition; smoothScrollBy(screenHeight-SizeUtil.getStatusBarHeight(context)-Math.abs(firstVisibleView.getTop()-1), 400); }else{ nowPage = savedPosition; smoothScrollBy(firstVisibleView.getTop(), 400); // Stops the listview from overshooting. } if (listener != null) { listener.onScrollFinish(nowPage); } } }, 50); } break; // スクロール中 // case OnScrollListener.SCROLL_STATE_TOUCH_SCROLL: // break; // はじいたとき case OnScrollListener.SCROLL_STATE_FLING: flingFlag = true; postDelayed(new Runnable() { public void run() { int savedPosition = getFirstVisiblePosition(); View firstVisibleView = getChildAt(0); if (savedPosition < nowPage){ if (nowPage+1 == totalPage ) { int firstVisibleItem = nowPage-getFirstVisiblePosition(); View lastView = getChildAt(firstVisibleItem); if (lastView.getTop() > 0) { scrollPrevPage(); } }else{ scrollPrevPage(); } }else if (0 < savedPosition || ( 0 == savedPosition && 0 > firstVisibleView.getTop() ) ){ if (nowPage != totalPage-1) { scrollNextPage(); } } listener.onScrollStart(nowPage); } }, 50); break; } } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { if (listener != null) { listener.onNextListLoad(nowPage); } } }); } }
main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" > <test.duyoji.listview.PagingListView android:id="@+id/main_list" android:layout_width="match_parent" android:layout_height="match_parent" android:divider="#00000000" android:dividerHeight="0dip" android:scrollbars="none" android:fadingEdge="none" android:fadingEdgeLength="0dip" android:scrollingCache="false" android:cacheColorHint="#00000000"/> </LinearLayout>
item_paging_list.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" > <ImageView android:id="@+id/item_paging_list_image" android:layout_width="match_parent" android:layout_height="match_parent" android:src="@drawable/ic_launcher"/> </LinearLayout>
MainActivity.java
package test.duyoji.paginglist; import java.util.ArrayList; import test.duyoji.adapter.MenuAdapter; import test.duyoji.listview.PagingListView; import test.duyoji.listview.PagingListView.OnPagingListener; import android.os.Bundle; import android.app.Activity; public class MainActivity extends Activity { private PagingListView listView; private MenuAdapter adapter; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); init(); setListItem(); } private void init(){ this.listView = (PagingListView) findViewById(R.id.main_list); this.listView.setPagingListener(new OnPagingListener() { @Override public void onScrollStart(int _nowPage) { //ここにスクロールが開始したときの処理を書く } @Override public void onScrollFinish(int _nowPage) { //ここにスクロールが終了したときの処理を書く } @Override public void onNextListLoad(int _nowPage) { //ここにまだ読み込んでいない画象を追加したい時に処理を書く } }); } //set listview items private void setListItem(){ ArrayList<String> tmpArray = new ArrayList<String>(); for (int i = 0; i < 10; i++) tmpArray.add("tmp"); listView.setTotalPage(tmpArray.size()); this.listView.setNowPage(0); adapter = new MenuAdapter(this, tmpArray); listView.setAdapter(adapter); } }
MainAdapter.java
package test.duyoji.adapter; import java.util.ArrayList; import test.duyoji.paginglist.R; import test.duyoji.util.SizeUtil; import android.annotation.SuppressLint; import android.content.Context; import android.view.Display; import android.view.LayoutInflater; import android.view.View; import android.view.WindowManager; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.LinearLayout; @SuppressLint("NewApi") public class MainAdapter extends BaseAdapter { private ArrayList<String> items; private LinearLayout.LayoutParams llp; private LayoutInflater inflater; public MainAdapter(Context context, ArrayList<String> items) { this.items = items; this.inflater = LayoutInflater.from(context); WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); Display display = wm.getDefaultDisplay(); this.llp = new LinearLayout.LayoutParams(display.getWidth(), display.getHeight()-SizeUtil.getStatusBarHeight(context)); } public void addItems(ArrayList<String> _items){ items.addAll(items); notifyDataSetChanged(); } //View使い回し用 private static class ViewHolder{ private ImageView imageView; } @Override public View getView(int position, View convertView, ViewGroup parent) { final ViewHolder holder; if (convertView == null) { convertView = inflater.inflate(R.layout.item_paging_list, null); ImageView imageView = (ImageView) convertView.findViewById(R.id.item_paging_list_image); imageView.setLayoutParams(llp); holder = new ViewHolder(); holder.imageView = imageView; convertView.setTag(holder); }else{ holder = (ViewHolder) convertView.getTag(); } holder.imageView.setImageResource(R.drawable.ic_launcher); return convertView; } @Override public int getCount() { return items.size(); } @Override public String getItem(int position) { return items.get(position); } @Override public long getItemId(int position) { return position; } }
SizeUtil.java
package test.duyoji.util; import android.content.Context; import android.util.DisplayMetrics; import android.view.WindowManager; public class SizeUtil { private static final int LOW_DPI_STATUS_BAR_HEIGHT = 19; private static final int MEDIUM_DPI_STATUS_BAR_HEIGHT = 25; private static final int HIGH_DPI_STATUS_BAR_HEIGHT = 38; //ステータスバーの高さを取得する public static final int getStatusBarHeight(Context context) { DisplayMetrics displayMetrics = new DisplayMetrics(); ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getMetrics(displayMetrics); int statusBarHeight; switch (displayMetrics.densityDpi) { case DisplayMetrics.DENSITY_HIGH: statusBarHeight = HIGH_DPI_STATUS_BAR_HEIGHT; break; case DisplayMetrics.DENSITY_MEDIUM: statusBarHeight = MEDIUM_DPI_STATUS_BAR_HEIGHT; break; case DisplayMetrics.DENSITY_LOW: statusBarHeight = LOW_DPI_STATUS_BAR_HEIGHT; break; default: statusBarHeight = MEDIUM_DPI_STATUS_BAR_HEIGHT; } return statusBarHeight; } }
説明
PagingListView
Interfaceを実装しており、スクロールがスタートしたとき,終わったとき,次の画像を読み込みたい時用に
三つ用意している。(OnPagingListener)
setter,getter部分ではListViewのトータルのアイテム数と現在のページをセットする。
getNowpage()を使うことで外部からインスタンスを経由して現在のページを取得できる。
スクロールようのメソッドがあり外部からでもページング出来るようにしている。
「次へ」ボタン「前へ」ボタンを用意して、scrollNextPage(),scrollPrevPage()を呼べば
1ページ分スクロールするようにしている。
init()ではPagingListViewの初期化を行っていて、1ページ分スクロールするため画面から
ステータスバーを引いた画面高さを取得している。
setScrollEvent()でOnScrollListenerを利用してフリックやフリックでないときの1ページスクロール処理
を記述している。
item_paging_list.xml
PagingListViewにセットするコンテンツ。
あとで説明する、MainAdapter.javaでgetViewメソッドのconvertViewで使う。
内容はLinearLayoutにImageViewを入れただけの単純な構造。
MainActivity.java
main.xmlをsetContentViewしていて,init()で初期化している。
listView.setPagingListener()内でここでは処理を記述していないが、
スクロール開始時,スクロール終了時,新しい画象を読み込む時の処理を書く。
setListItem()で気をつけて欲しいのはPagingListViewに現在のページとトータルのアイテム数を
記述すること。これが無いと挙動がおかしくなる。
listView.setTotalPage(tmpArray.size()); this.listView.setNowPage(0);