下にスクロール時にヘッダーメニューを隠して、上にスクロール時にヘッダーメニューを表示する機能を実装する必要があったのですが、ListViewで移動距離を取得する方法が分からなくて調べてみました。
ScrollViewのほうも作ってみたので、まずはScrollViewのほうから、
【ScrollViewを継承したクラスを作成する】
public class ObservableScrollView extends ScrollView{ public interface ScrollViewListener { void onScrollChanged(int x, int y, int oldX, int oldY); } private ScrollViewListener scrollViewListener = null; public ObservableScrollView(Context context) { super(context); } public ObservableScrollView(Context context, AttributeSet attrs) { super(context, attrs); } public ObservableScrollView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } public void setOnScrollViewListener(ScrollViewListener scrollViewListener) { this.scrollViewListener = scrollViewListener; } // onScrollChanged が複数回呼び出されるのを防止するために利用 private int lastt = 0; @Override protected void onScrollChanged(int x, int y, int oldX, int oldY) { super.onScrollChanged(x, y, oldx, oldy); // 高速にスクロールした場合、画面上部で t, oldt がマイナスとなる場合は処理をスキップ if (scrollViewListener == null || lastt == y || y < 0 || oldy < 0) { return; } lastt = y; scrollViewListener.onScrollChanged(x, y, oldX, oldY); } }
【使用方法】
final ObservableScrollView obScrollView = (ObservableScrollView) view.findViewById(R.id.contents_list_scroll_view); obScrollView.setOnScrollViewListener(new ObservableScrollView.ScrollViewListener() { @TargetApi(Build.VERSION_CODES.HONEYCOMB) @Override public void onScrollChanged(int x, int y, int oldX, int oldY) { int diff = y - oldY; // 縦軸の移動量 //以下は、スクロールでヘッダーを表示、非表示にする処理 int translationY = 0 < diff ? Math.max((int) mHeader.getY() - diff, -mHeader.getHeight()) : Math.min((int) mHeader.getY() - diff, 0); mHeader.setTranslationY(translationY); } });
続いて、ListView、
【移動距離を計算するヘルパークラスを用意する】
public class ListViewScrollTracker { private AbsListView mListView; private SparseArray<Integer> mPositions; public ListViewScrollTracker(final AbsListView listView){ mListView = listView; } public int calculateIncrementalOffset(final int firstVisiblePosition, final int visibleItemCount){ SparseArray<Integer> previousPositions = mPositions; mPositions = new SparseArray<Integer>(); for(int i = 0; i < visibleItemCount; i++){ mPositions.put(firstVisiblePosition + i, mListView.getChildAt(i).getTop()); } if(previousPositions != null){ for(int i = 0; i < previousPositions.size(); i++){ int position = previousPositions.keyAt(i); int previousTop = previousPositions.get(position); Integer newTop = mPositions.get(position); if(newTop != null){ return newTop - previousTop; } } } return 0; } public void clear(){ mPositions = null; } }
【使用方法】
//スクロール値の相対置を調べるために使用 private ListViewScrollTracker mScrollTracker; //ListViewの相対的なY位置 private int mOffsetY; //ジャンルから探す/並び替えをスクロールに応じて表示、非表示にするために使用 private int mOldOffsetY; private ListView mListView; private LinearLayout mHeader; @Override protected void onCreate(Bundle savedInstanceState) { //使用するListView mListView = (ListView)view.findViewById(R.id.contents_list); mHeader = (LinearLayout)view.findViewById(R.id. store_top_header); mScrollTracker = new ListViewScrollTracker(mListView); mListView.setOnScrollListener(new AbsListView.OnScrollListener() { @TargetApi(Build.VERSION_CODES.HONEYCOMB) @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { mOffsetY += mScrollTracker.calculateIncrementalOffset(firstVisibleItem, visibleItemCount); int diff = mOldOffsetY - mOffsetY; // 縦軸の移動量 //以下は、スクロールでヘッダーを表示、非表示にする処理 int translationY = 0 < diff ? Math.max((int) mHeader.getY() - diff, -mHeader.getHeight()) : Math.min((int) mHeader.getY() - diff, 0); mHeader.setTranslationY(translationY); } mOldOffsetY = mOffsetY; } }); }
簡単にですが、レイアウトのxmlのサンプルも
<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_height="match_parent" android:layout_width="match_parent" android:orientation="vertical"> <ListView android:id="@+id/contents_list" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" /> </ListView> <LinearLayout android:id="@+id/store_top_header" android:layout_width="match_parent" android:orientation="vertical" android:layout_height="wrap_content"> </LineaLayout> </FrameLayout>
長くなりましたが、このような感じでスクロールの移動距離を取得する事が出来ましたー。