2011年1月2日日曜日

Android Overscroll を使う簡単な方法

羊さんが素敵なエントリを書いてくれてました「OverScrollでListViewをビョーンってする方法 - hdk_embeddedの日記 -

でも、やっぱりフレームワークの ListView とか ScrollView で on/off できないってことはないんじゃないのかな?と思って調べてみました。

ScrollView の onTouchEvent メソッドのなかでは、ちゃんと overScrollBy メソッドが呼ばれています。

android.wdiget.ScrollView

public boolean onTouchEvent(MotionEvent ev) {

...

case MotionEvent.ACTION_MOVE:
if (mIsBeingDragged) {
// Scroll to follow the motion event
final int activePointerIndex = ev.findPointerIndex(mActivePointerId);
final float y = ev.getY(activePointerIndex);
final int deltaY = (int) (mLastMotionY - y);
mLastMotionY = y;

final int oldX = mScrollX;
final int oldY = mScrollY;
final int range = getScrollRange();
if (overScrollBy(0, deltaY, 0, mScrollY, 0, range,
0, mOverscrollDistance, true)) {
// Break our velocity if we hit a scroll barrier.
mVelocityTracker.clear();
}
onScrollChanged(mScrollX, mScrollY, oldX, oldY);

final int overscrollMode = getOverScrollMode();
if (overscrollMode == OVER_SCROLL_ALWAYS ||
(overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && range > 0)) {
final int pulledToY = oldY + deltaY;
if (pulledToY < 0) {
mEdgeGlowTop.onPull((float) deltaY / getHeight());
if (!mEdgeGlowBottom.isFinished()) {
mEdgeGlowBottom.onRelease();
}
} else if (pulledToY > range) {
mEdgeGlowBottom.onPull((float) deltaY / getHeight());
if (!mEdgeGlowTop.isFinished()) {
mEdgeGlowTop.onRelease();
}
}
if (mEdgeGlowTop != null
&& (!mEdgeGlowTop.isFinished() ||
!mEdgeGlowBottom.isFinished())) {
invalidate();
}
}
}
break;



if (overScrollBy(0, deltaY, 0, mScrollY, 0, range, 0, mOverscrollDistance, true))

という部分です。

この overScrollBy メソッドの 8番目の引数がキモです。
overScrollBy メソッドのリファレンスには

maxOverScrollX Number of pixels to overscroll by in either direction along the X axis.

と書いてあります。つまり、これがオーバースクロールする距離ということです。
では、この mOverscrollDistance には何が入っているでしょう?

android.wdiget.ScrollView

private void initScrollView() {
mScroller = new OverScroller(getContext());
setFocusable(true);
setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
setWillNotDraw(false);
final ViewConfiguration configuration = ViewConfiguration.get(mContext);
mTouchSlop = configuration.getScaledTouchSlop();
mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
mOverscrollDistance = configuration.getScaledOverscrollDistance();
mOverflingDistance = configuration.getScaledOverflingDistance();
}


このように、initScrollView メソッドのなかで、 mOverscrollDistance に ViewConfiguration の getScaledOverscrollDistance メソッドで取得した値を入れています。

では、 ViewConfiguration クラスを見てみましょう。

android.view.ViewConfiguration

package android.view;

import android.content.Context;
import android.util.DisplayMetrics;
import android.util.SparseArray;

/**
* Contains methods to standard constants used in the UI for timeouts, sizes, and distances.
*/
public class ViewConfiguration {

...

/**
* Max distance to overscroll for edge effects
*/
private static final int OVERSCROLL_DISTANCE = 0;

/**
* Max distance to overfling for edge effects
*/
private static final int OVERFLING_DISTANCE = 4;

...

private ViewConfiguration(Context context) {
final DisplayMetrics metrics = context.getResources().getDisplayMetrics();
final float density = metrics.density;

mEdgeSlop = (int) (density * EDGE_SLOP + 0.5f);
mFadingEdgeLength = (int) (density * FADING_EDGE_LENGTH + 0.5f);
mMinimumFlingVelocity = (int) (density * MINIMUM_FLING_VELOCITY + 0.5f);
mMaximumFlingVelocity = (int) (density * MAXIMUM_FLING_VELOCITY + 0.5f);
mScrollbarSize = (int) (density * SCROLL_BAR_SIZE + 0.5f);
mTouchSlop = (int) (density * TOUCH_SLOP + 0.5f);
mPagingTouchSlop = (int) (density * PAGING_TOUCH_SLOP + 0.5f);
mDoubleTapSlop = (int) (density * DOUBLE_TAP_SLOP + 0.5f);
mWindowTouchSlop = (int) (density * WINDOW_TOUCH_SLOP + 0.5f);

// Size of the screen in bytes, in ARGB_8888 format
mMaximumDrawingCacheSize = 4 * metrics.widthPixels * metrics.heightPixels;

mOverscrollDistance = (int) (density * OVERSCROLL_DISTANCE + 0.5f);
mOverflingDistance = (int) (density * OVERFLING_DISTANCE + 0.5f);
}

...

public int getScaledOverscrollDistance() {
return mOverscrollDistance;
}

...

}


なんと、OVERSCROLL_DISTANCE が 0 じゃないですか!
そりゃあオーバースクロールしないですよ。。。

AbsListView でもまったく同じことをしてるので、やっぱり mOverscrollDistance は 0 です。。。


はっ!ということは、単に overScrollBy メソッドを override して mOverscrollDistance 部分に 0 以外の値を入れればいいだけかも。

ということでやってみた。


package yanzm.example.overscroll;

import android.content.Context;
import android.util.AttributeSet;
import android.widget.ListView;

public class MyListView extends ListView {

private int mOverscrollDistance = 200;

public MyListView(Context context) {
super(context);
}

public MyListView(Context context, AttributeSet attrs) {
super(context, attrs);
}

public MyListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}


@Override
protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) {
return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY, maxOverScrollX, mOverscrollDistance, isTouchEvent);
}
}


overScrollBy メソッドを override して、第8引数に 0 以外の値(ここでは 200) を渡すだけ!


<?xml version="1.0" encoding="utf-8"?>
<yanzm.example.overscroll.MyListView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/list"
android:layout_width="fill_parent"
android:layout_height="fill_parent"

android:overScrollMode="always"
android:overScrollHeader="#000000"
android:overScrollFooter="#000000"
/>


この overScrollHeader と overScrollFooter がビローンってしたときの領域の色です。(リソースIDを指定すれば画像も可能 android:overScrollHeader="@drawable/icon" とか)


package yanzm.example.overscroll;

import android.app.ListActivity;
import android.os.Bundle;
import android.widget.ArrayAdapter;

public class MyActivity extends ListActivity {

String[] mData = {
"Apple",
"Banana",
"Grape",
"Lemon",
"Melon",
"Orange",
"Peach",
"Water Melon",
};

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.list);

ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, R.layout.list_item, mData);
setListAdapter(adapter);
}
}


調子にのりましたw



Android Bazaar and Conference 2011 で女子部セッションあります。
「DJモグタソのオールナイトヒャッハー」
# すっごい面白いよ。


 

1 件のコメント: