2011年1月2日日曜日

Android Overscroll を使う簡単な方法

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

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

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

android.wdiget.ScrollView
  1. public boolean onTouchEvent(MotionEvent ev) {  
  2.   
  3.   ...  
  4.   
  5.   case MotionEvent.ACTION_MOVE:  
  6.     if (mIsBeingDragged) {  
  7.       // Scroll to follow the motion event  
  8.       final int activePointerIndex = ev.findPointerIndex(mActivePointerId);  
  9.       final float y = ev.getY(activePointerIndex);  
  10.       final int deltaY = (int) (mLastMotionY - y);  
  11.       mLastMotionY = y;  
  12.   
  13.       final int oldX = mScrollX;  
  14.       final int oldY = mScrollY;  
  15.       final int range = getScrollRange();  
  16.       if (overScrollBy(0, deltaY, 0, mScrollY, 0, range,  
  17.                        0, mOverscrollDistance, true)) {  
  18.         // Break our velocity if we hit a scroll barrier.  
  19.         mVelocityTracker.clear();  
  20.       }  
  21.       onScrollChanged(mScrollX, mScrollY, oldX, oldY);  
  22.   
  23.       final int overscrollMode = getOverScrollMode();  
  24.       if (overscrollMode == OVER_SCROLL_ALWAYS ||  
  25.          (overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && range > 0)) {  
  26.         final int pulledToY = oldY + deltaY;  
  27.         if (pulledToY < 0) {  
  28.           mEdgeGlowTop.onPull((float) deltaY / getHeight());  
  29.           if (!mEdgeGlowBottom.isFinished()) {  
  30.             mEdgeGlowBottom.onRelease();  
  31.           }  
  32.         } else if (pulledToY > range) {  
  33.           mEdgeGlowBottom.onPull((float) deltaY / getHeight());  
  34.           if (!mEdgeGlowTop.isFinished()) {  
  35.             mEdgeGlowTop.onRelease();  
  36.           }  
  37.         }  
  38.         if (mEdgeGlowTop != null  
  39.             && (!mEdgeGlowTop.isFinished() ||   
  40.                 !mEdgeGlowBottom.isFinished())) {  
  41.                 invalidate();  
  42.         }  
  43.       }  
  44.     }  
  45.     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
  1. private void initScrollView() {  
  2.     mScroller = new OverScroller(getContext());  
  3.     setFocusable(true);  
  4.     setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);  
  5.     setWillNotDraw(false);  
  6.     final ViewConfiguration configuration = ViewConfiguration.get(mContext);  
  7.     mTouchSlop = configuration.getScaledTouchSlop();  
  8.     mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();  
  9.     mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();  
  10.     mOverscrollDistance = configuration.getScaledOverscrollDistance();  
  11.     mOverflingDistance = configuration.getScaledOverflingDistance();  
  12. }  


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

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

android.view.ViewConfiguration
  1. package android.view;  
  2.   
  3. import android.content.Context;  
  4. import android.util.DisplayMetrics;  
  5. import android.util.SparseArray;  
  6.   
  7. /** 
  8.  * Contains methods to standard constants used in the UI for timeouts, sizes, and distances. 
  9.  */  
  10. public class ViewConfiguration {  
  11.   
  12.   ...  
  13.   
  14.     /** 
  15.      * Max distance to overscroll for edge effects 
  16.      */  
  17.     private static final int OVERSCROLL_DISTANCE = 0;  
  18.   
  19.     /** 
  20.      * Max distance to overfling for edge effects 
  21.      */  
  22.     private static final int OVERFLING_DISTANCE = 4;  
  23.   
  24.     ...  
  25.   
  26.     private ViewConfiguration(Context context) {  
  27.         final DisplayMetrics metrics = context.getResources().getDisplayMetrics();  
  28.         final float density = metrics.density;  
  29.   
  30.         mEdgeSlop = (int) (density * EDGE_SLOP + 0.5f);  
  31.         mFadingEdgeLength = (int) (density * FADING_EDGE_LENGTH + 0.5f);  
  32.         mMinimumFlingVelocity = (int) (density * MINIMUM_FLING_VELOCITY + 0.5f);  
  33.         mMaximumFlingVelocity = (int) (density * MAXIMUM_FLING_VELOCITY + 0.5f);  
  34.         mScrollbarSize = (int) (density * SCROLL_BAR_SIZE + 0.5f);  
  35.         mTouchSlop = (int) (density * TOUCH_SLOP + 0.5f);  
  36.         mPagingTouchSlop = (int) (density * PAGING_TOUCH_SLOP + 0.5f);  
  37.         mDoubleTapSlop = (int) (density * DOUBLE_TAP_SLOP + 0.5f);  
  38.         mWindowTouchSlop = (int) (density * WINDOW_TOUCH_SLOP + 0.5f);  
  39.   
  40.         // Size of the screen in bytes, in ARGB_8888 format  
  41.         mMaximumDrawingCacheSize = 4 * metrics.widthPixels * metrics.heightPixels;  
  42.   
  43.         mOverscrollDistance = (int) (density * OVERSCROLL_DISTANCE + 0.5f);  
  44.         mOverflingDistance = (int) (density * OVERFLING_DISTANCE + 0.5f);  
  45.     }  
  46.   
  47.     ...  
  48.   
  49.     public int getScaledOverscrollDistance() {  
  50.         return mOverscrollDistance;  
  51.     }  
  52.   
  53.     ...  
  54.   
  55. }  


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

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


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

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

  1. package yanzm.example.overscroll;  
  2.   
  3. import android.content.Context;  
  4. import android.util.AttributeSet;  
  5. import android.widget.ListView;  
  6.   
  7. public class MyListView extends ListView {  
  8.   
  9.  private int mOverscrollDistance = 200;  
  10.    
  11.     public MyListView(Context context) {  
  12.         super(context);  
  13.     }  
  14.   
  15.     public MyListView(Context context, AttributeSet attrs) {  
  16.      super(context, attrs);  
  17.     }  
  18.   
  19.     public MyListView(Context context, AttributeSet attrs, int defStyle) {  
  20.         super(context, attrs, defStyle);  
  21.     }  
  22.   
  23.    
  24.  @Override  
  25.  protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) {  
  26.   return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY, maxOverScrollX, mOverscrollDistance, isTouchEvent);  
  27.  }  
  28. }  


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

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <yanzm.example.overscroll.MyListView xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:id="@android:id/list"  
  4.     android:layout_width="fill_parent"  
  5.     android:layout_height="fill_parent"  
  6.       
  7.     android:overScrollMode="always"  
  8.     android:overScrollHeader="#000000"  
  9.     android:overScrollFooter="#000000"  
  10.     />  


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

  1. package yanzm.example.overscroll;  
  2.   
  3. import android.app.ListActivity;  
  4. import android.os.Bundle;  
  5. import android.widget.ArrayAdapter;  
  6.   
  7. public class MyActivity extends ListActivity {  
  8.   
  9.     String[] mData = {  
  10.             "Apple",  
  11.             "Banana",  
  12.             "Grape",  
  13.             "Lemon",  
  14.             "Melon",  
  15.             "Orange",  
  16.             "Peach",  
  17.             "Water Melon",  
  18.         };  
  19.   
  20.     @Override  
  21.     public void onCreate(Bundle savedInstanceState) {  
  22.         super.onCreate(savedInstanceState);  
  23.         setContentView(R.layout.list);  
  24.           
  25.         ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, R.layout.list_item, mData);  
  26.         setListAdapter(adapter);          
  27.     }  
  28. }  


調子にのりましたw



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


 

1 件のコメント: