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モグタソのオールナイトヒャッハー」
# すっごい面白いよ。


 

2 件のコメント:

  1. HaHa omosiroidesune. Yoku mimasita!

    返信削除
  2. If you want your ex-girlfriend or ex-boyfriend to come crawling back to you on their knees (no matter why you broke up) you need to watch this video
    right away...

    (VIDEO) Why your ex will NEVER get back...

    返信削除