下にスクロール時にヘッダーメニューを隠して、上にスクロール時にヘッダーメニューを表示する機能を実装する必要があったのですが、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;
}
private int lastt = 0;
@Override
protected void onScrollChanged(int x, int y, int oldX, int oldY) {
super.onScrollChanged(x, y, oldx, oldy);
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;
private int mOffsetY;
private int mOldOffsetY;
private ListView mListView;
private LinearLayout mHeader;
@Override
protected void onCreate(Bundle savedInstanceState) {
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 xmlnsandroid="http://schemas.android.com/apk/res/android"
androidlayout_height="match_parent"
androidlayout_width="match_parent"
androidorientation="vertical">
<ListView
androidid="@+id/contents_list"
androidorientation="vertical"
androidlayout_width="match_parent"
androidlayout_height="match_parent" />
</ListView>
<LinearLayout
androidid="@+id/store_top_header"
androidlayout_width="match_parent"
androidorientation="vertical"
androidlayout_height="wrap_content">
</LineaLayout>
</FrameLayout>
長くなりましたが、このような感じでスクロールの移動距離を取得する事が出来ましたー。