滑动

platform\frameworks\base\core\java\android\view\ViewConfiguration.java

ViewConfiguration类中有判断是否滑动的若干配置项默认值设置。

 

getScaledTouchSlop() 手指的移动要大于这个距离才算滑动,返回的mTouchSlop来自TOUCH_SLOP的取值。

getScaledDoubleTapSlop() 第一个触点和后面触点的距离小于这个数值算双击,返回的mDoubleTapTouchSlop来自mTouchSlop

getScaledPagingTouchSlop()手指的移动要大于这个距离才算翻页,返回的mPagingTouchSlop来自mTouchSlop * 2

getScaledEdgeSlop()获得一个触摸移动的最小像素值。也就是说,只有超过了这个值,才代表我们该滑屏处理;

getScaledMaximumFlingVelocity() 用户滑动时的最大速度,单位是每秒多少像素

getScaledMinimumFlingVelocity() 用户滑动时的最小速度

 

/**

* @return Distance in pixels a touch can wander before we think the user is scrolling

*/

public int getScaledTouchSlop() {

return mTouchSlop;

}

 

/**

* @deprecated Use {@link android.view.ViewConfiguration#get(android.content.Context)}.

*/

@Deprecated

public ViewConfiguration() {

mEdgeSlop = EDGE_SLOP;

mTouchSlop = TOUCH_SLOP;

/** * @deprecated Use {@link #getScaledEdgeSlop()} instead.

* Inset in dips to look for touchable content when the user touches the edge of the screen

*/

private static final int EDGE_SLOP = 12;

/**

* Distance a touch can wander before we think the user is scrolling in dips.

* Note that this value defined here is only used as a fallback by legacy/misbehaving

* applications that do not provide a Context for determining density/configuration-dependent

* values.

*

* To alter this value, see the configuration resource config_viewConfigurationTouchSlop

* in frameworks/base/core/res/res/values/config.xml or the appropriate device resource overlay.

* It may be appropriate to tweak this on a device-specific basis in an overlay based on

* the characteristics of the touch panel and firmware.

*/

private static final int TOUCH_SLOP = 8;

/**

* Distance the first touch can wander before we stop considering this event a double tap

* (in dips)

*/

private static final int DOUBLE_TAP_TOUCH_SLOP = TOUCH_SLOP;

 

/**

* Distance a touch can wander before we think the user is attempting a paged scroll

* (in dips)

*

* Note that this value defined here is only used as a fallback by legacy/misbehaving

* applications that do not provide a Context for determining density/configuration-dependent

* values.

*

* See the note above on {@link #TOUCH_SLOP} regarding the dimen resource

* config_viewConfigurationTouchSlop. ViewConfiguration will report a paging touch slop of

* config_viewConfigurationTouchSlop * 2 when provided with a Context.

*/

private static final int PAGING_TOUCH_SLOP = TOUCH_SLOP * 2;

 

/**

@deprecated Use {@link #getScaledDoubleTapSlop()} instead.

* Distance in dips between the first touch and second touch to still be considered a double tap

*/

private static final int DOUBLE_TAP_SLOP = 100;

/**

* @return Distance in pixels between the first touch and second touch to still be

* considered a double tap

*/

public int getScaledDoubleTapSlop() {

return mDoubleTapSlop;

}

/**

* @return Inset in pixels to look for touchable content when the user touches the edge of the

* screen

*/

public int getScaledEdgeSlop() {

return mEdgeSlop;

}

<!-- Minimum velocity to initiate a fling, as measured in dips per second. -->

<dimen name="config_viewMinFlingVelocity">50dp</dimen>

 

<!-- Maximum velocity to initiate a fling, as measured in dips per second. -->

<dimen name="config_viewMaxFlingVelocity">8000dp</dimen>

 

/**

* Creates a new configuration for the specified context. The configuration depends on

* various parameters of the context, like the dimension of the display or the density

* of the display.

*

* @param context The application context used to initialize this view configuration.

*

* @see #get(android.content.Context)

* @see android.util.DisplayMetrics

*/

private ViewConfiguration(Context context) {

final Resources res = context.getResources();

final DisplayMetrics metrics = res.getDisplayMetrics();

final Configuration config = res.getConfiguration();

final float density = metrics.density;

final float sizeAndDensity;

if (config.isLayoutSizeAtLeast(Configuration.SCREENLAYOUT_SIZE_XLARGE)) {

sizeAndDensity = density * 1.5f;

} else {

sizeAndDensity = density;

}

 

mEdgeSlop = (int) (sizeAndDensity * EDGE_SLOP + 0.5f);

mFadingEdgeLength = (int) (sizeAndDensity * FADING_EDGE_LENGTH + 0.5f);

mScrollbarSize = (int) (density * SCROLL_BAR_SIZE + 0.5f);

mDoubleTapSlop = (int) (sizeAndDensity * DOUBLE_TAP_SLOP + 0.5f);

mWindowTouchSlop = (int) (sizeAndDensity * WINDOW_TOUCH_SLOP + 0.5f);

 

// Size of the screen in bytes, in ARGB_8888 format

final WindowManager win = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);

final Display display = win.getDefaultDisplay();

final Point size = new Point();

display.getRealSize(size);

mMaximumDrawingCacheSize = 4 * size.x * size.y;

 

mOverscrollDistance = (int) (sizeAndDensity * OVERSCROLL_DISTANCE + 0.5f);

mOverflingDistance = (int) (sizeAndDensity * OVERFLING_DISTANCE + 0.5f);

 

if (!sHasPermanentMenuKeySet) {

final int configVal = res.getInteger(

com.android.internal.R.integer.config_overrideHasPermanentMenuKey);

 

switch (configVal) {

default:

case HAS_PERMANENT_MENU_KEY_AUTODETECT: {

IWindowManager wm = WindowManagerGlobal.getWindowManagerService();

try {

sHasPermanentMenuKey = !wm.hasNavigationBar();

sHasPermanentMenuKeySet = true;

} catch (RemoteException ex) {

sHasPermanentMenuKey = false;

}

}

break;

 

case HAS_PERMANENT_MENU_KEY_TRUE:

sHasPermanentMenuKey = true;

sHasPermanentMenuKeySet = true;

break;

 

case HAS_PERMANENT_MENU_KEY_FALSE:

sHasPermanentMenuKey = false;

sHasPermanentMenuKeySet = true;

break;

}

}

 

mFadingMarqueeEnabled = res.getBoolean(

com.android.internal.R.bool.config_ui_enableFadingMarquee);

mTouchSlop = res.getDimensionPixelSize(

com.android.internal.R.dimen.config_viewConfigurationTouchSlop);

mPagingTouchSlop = mTouchSlop * 2;

 

mDoubleTapTouchSlop = mTouchSlop;

 

mMinimumFlingVelocity = res.getDimensionPixelSize(

com.android.internal.R.dimen.config_viewMinFlingVelocity);

mMaximumFlingVelocity = res.getDimensionPixelSize(

com.android.internal.R.dimen.config_viewMaxFlingVelocity);

mGlobalActionsKeyTimeout = res.getInteger(

com.android.internal.R.integer.config_globalActionsKeyTimeout);

}

 

platform\frameworks\base\core\res\res\values\symbols.xml

<java-symbol type="dimen" name="config_viewConfigurationTouchSlop" />

<java-symbol type="dimen" name="config_viewMinFlingVelocity" />

<java-symbol type="dimen" name="config_viewMaxFlingVelocity" />

 

frameworks/base/core/res/res/values/config.xml

<!-- Base "touch slop" value used by ViewConfiguration as a

movement threshold where scrolling should begin. -->

<dimen name="config_viewConfigurationTouchSlop">8dp</dimen>

 

<!-- Minimum velocity to initiate a fling, as measured in dips per second. -->

<dimen name="config_viewMinFlingVelocity">50dp</dimen>

 

<!-- Maximum velocity to initiate a fling, as measured in dips per second. -->

<dimen name="config_viewMaxFlingVelocity">8000dp</dimen>

 

private GestureDetector mGestureDetector;

 

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

mGestureDetector = new GestureDetector(this, new MyGestureListener());

}

 

@Override

public boolean onTouchEvent(MotionEvent event) {

return mGestureDetector.onTouchEvent(event);

}

 

 

class MyGestureListener extends GestureDetector.OnGestureListener{ //共有6个方法:

 

// 用户轻触触屏:Touch down(仅一次)时触发, e为down时的MotionEvent:

boolean onDown(MotionEvent e){return true;}

 

// 用户轻触触屏,且尚未松开或拖动:

// 在Touch down(仅一次)之后一定时间(115ms)触发,e为down时的MotionEvent:

void onShowPress(MotionEvent e){return true;}

 

// 用户(轻触触屏后)松开:Touch up(仅一次)时触发,e为up时的MotionEvent:

boolean onSingleTapUp(MotionEvent e){return true;}

 

// 用户轻触触屏,并拖动:

// 按下并滑动时触发,e1为down(仅一次)时的MotionEvent,e2为move(多个)时的MotionEvent:

boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY){return true;}

 

// 用户长按触屏(此View必须是可长按的: myView->setLongClickable(true);):

// 在Touch down之后一定时间(500ms)后,由多个down事件触发,e为down时的MotionEvent:

void onLongPress(MotionEvent e){return true;}

 

// 用户按下触屏、快速移动后松开:

// 按下并快速滑动一小段距离(多个move),up时触发,e1为down(仅一次)时的MotionEvent,

//e2为up(仅一次)时的MotionEvent:

boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY){return true;}

}

// -------------------------------------------------------------------------------

 

至于doubletap的检测,可以通过设置mGestureDetector的OnDoubleTapListener来实现:

 

GestureDetector.OnDoubleTapListener doubleTapListener = new GestureDetector.OnDoubleTapListener() {

 

// 完成一次单击,并确定(300ms内)没有发生第二次单击事件后触发,e为down时的MotionEvent:

boolean onSingleTapConfirmed(MotionEvent e){return true;}

 

// 第二次单击down时触发,e为第一次down时的MotionEvent:

boolean onDoubleTap(MotionEvent e){return true;}

 

// 第二次单击down、move和up时都触发,e为不同时机下的MotionEvent:

boolean onDoubleTapEvent(MotionEvent e){return true;}

}

mGestureDetector.setDoubleTapListener(doubleTapListener);

// -------------------------------------------------------------------------------

 

 

具体地说,典型的触屏事件及其listener执行的流程见下:

 

1). 单击事件的执行流程:

有两种情况,一种是时间很短,一种时间稍长。

时间很短:onDown ----> onSingleTapUp ----> onSingleTapConfirmed

时间稍长:onDown ----> onShowPress ----> onSingleTapUp ----> onSingleTapConfirmed

2). 长按事件

onDown ----> onShowPress ----> onLongPress

3.抛(fling):手指触动屏幕后,稍微滑动后立即松开:

onDown ----> onScroll ----> onScroll ----> onScroll ----> ……… ----> onFling

4.拖动(drag)

onDown ----> onScroll ----> onScroll ----> onFiling

注意:有的时候会触发onFiling,但是有的时候不会触发,z这是因为人的动作不标准所致。

 

ViewConfiguration滑动参数设置类:

/**   

  * 包含了方法和标准的常量用来设置UI的超时、大小和距离   

  */

 public class ViewConfiguration {    

     // 设定水平滚动条的宽度和垂直滚动条的高度,单位是像素px    

     private static final int SCROLL_BAR_SIZE = 10;    

             

     //定义滚动条逐渐消失的时间,单位是毫秒    

     private static final int SCROLL_BAR_FADE_DURATION = 250;    

             

     // 默认的滚动条多少秒之后消失,单位是毫秒    

     private static final int SCROLL_BAR_DEFAULT_DELAY = 300;    

             

     // 定义边缘地方褪色的长度    

     private static final int FADING_EDGE_LENGTH = 12;    

             

     //定义子控件按下状态的持续事件    

     private static final int PRESSED_STATE_DURATION = 125;    

                 

     //定义一个按下状态转变成长按状态的转变时间    

     private static final int LONG_PRESS_TIMEOUT = 500;    

                 

     //定义用户在按住适当按钮,弹出全局的对话框的持续时间    

     private static final int GLOBAL_ACTIONS_KEY_TIMEOUT = 500;    

                 

     //定义一个touch事件中是点击事件还是一个滑动事件所需的时间,如果用户在这个时间之内滑动,那么就认为是一个点击事件    

     private static final int TAP_TIMEOUT = 115;    

                 

     /**   

      * Defines the duration in milliseconds we will wait to see if a touch event    

      * is a jump tap. If the user does not complete the jump tap within this interval, it is   

      * considered to be a tap.    

      */

     //定义一个touch事件时候是一个点击事件。如果用户在这个时间内没有完成这个点击,那么就认为是一个点击事件    

     private static final int JUMP_TAP_TIMEOUT = 500;    

             

     //定义双击事件的间隔时间    

     private static final int DOUBLE_TAP_TIMEOUT = 300;    

                 

     //定义一个缩放控制反馈到用户界面的时间    

     private static final int ZOOM_CONTROLS_TIMEOUT = 3000;    

             

     /**   

      * Inset in pixels to look for touchable content when the user touches the edge of the screen   

      */

     private static final int EDGE_SLOP = 12;    

                 

     /**   

      * Distance a touch can wander before we think the user is scrolling in pixels   

      */

     private static final int TOUCH_SLOP = 16;    

                 

     /**   

      * Distance a touch can wander before we think the user is attempting a paged scroll   

      * (in dips)   

      */

     private static final int PAGING_TOUCH_SLOP = TOUCH_SLOP * 2;    

                 

     /**   

      * Distance between the first touch and second touch to still be considered a double tap   

      */

     private static final int DOUBLE_TAP_SLOP = 100;    

                 

     /**   

      * Distance a touch needs to be outside of a window's bounds for it to   

      * count as outside for purposes of dismissing the window.   

      */

     private static final int WINDOW_TOUCH_SLOP = 16;    

             

    //用来初始化fling的最小速度,单位是每秒多少像素    

     private static final int MINIMUM_FLING_VELOCITY = 50;    

                 

     //用来初始化fling的最大速度,单位是每秒多少像素    

     private static final int MAXIMUM_FLING_VELOCITY = 4000;    

             

     //视图绘图缓存的最大尺寸,以字节表示。在ARGB888格式下,这个尺寸应至少等于屏幕的大小    

     @Deprecated    

     private static final int MAXIMUM_DRAWING_CACHE_SIZE = 320 * 480 * 4; // HVGA screen, ARGB8888    

             

     //flingsscrolls摩擦力度大小的系数    

     private static float SCROLL_FRICTION = 0.015f;    

             

     /**   

      * Max distance to over scroll for edge effects   

      */

     private static final int OVERSCROLL_DISTANCE = 0;    

             

     /**   

      * Max distance to over fling for edge effects   

      */

     private static final int OVERFLING_DISTANCE = 4;    

             

 }

 

public class ViewConfiguration {

......

//不推荐使用,推荐ViewConfiguration.get(Context)获取实例

public ViewConfiguration() {}

public static ViewConfiguration get(Context context) {}

//不推荐使用,推荐getScaledScrollBarSize()代替;获取水平滚动条的宽或垂直滚动条的高

public static int getScrollBarSize() {}

public int getScaledScrollBarSize() {}

//滚动条褪去消失的持续时间

public static int getScrollBarFadeDuration() {}

//滚动条消失的延迟时间

public static int getScrollDefaultDelay() {}

//不推荐使用,推荐getScaledFadingEdgeLength()代替;褪去边缘的长度

public static int getFadingEdgeLength() {}

public int getScaledFadingEdgeLength() {}

//按下的持续时间长度

public static int getPressedStateDuration() {}

//按住状态转变为长按状态需要的时间

public static int getLongPressTimeout() {}

//重新按键判断时间

public static int getKeyRepeatTimeout() {}

//重复按键延迟的时间

public static int getKeyRepeatDelay() {}

//判断是单击还是滚动的时间,在这个时间内没有移动则是单击,否则是滚动

public static int getTapTimeout() {}

//在这个时间内没有完成这个点击,那么就认为是一个点击事件

public static int getJumpTapTimeout() {}

//得到双击间隔时间,在这个时间内是双击,否则是单击

public static int getDoubleTapTimeout() {}

//不推荐使用,推荐getScaledEdgeSlop()代替;判断是否滑动事件

public static int getEdgeSlop() {}

public int getScaledEdgeSlop() {}

//不推荐使用,推荐getScaledTouchSlop()代替;滑动的时候,手的移动要大于这个距离才算移动

public static int getTouchSlop() {}

public int getScaledTouchSlop() {}

//触摸边沿padding区域的判断

public int getScaledPagingTouchSlop() {}

//不推荐使用,推荐getScaledDoubleTapSlop()代替;判断是否双击的阈值

public static int getDoubleTapSlop() {}

public int getScaledDoubleTapSlop() {}

//不推荐使用,推荐getScaledWindowTouchSlop()代替;触摸窗体边沿区域判断

public static int getWindowTouchSlop() {}

public int getScaledWindowTouchSlop() {}

//不推荐使用,推荐getScaledMinimumFlingVelocity()代替;得到滑动的最小速度, 以像素/每秒来进行计算

public static int getMinimumFlingVelocity() {}

public int getScaledMinimumFlingVelocity() {}

//不推荐使用,推荐getScaledMaximumFlingVelocity()代替;得到滑动的最大速度, 以像素/每秒来进行计算

public static int getMaximumFlingVelocity() {}

public int getScaledMaximumFlingVelocity() {}

//不推荐使用,推荐getScaledMaximumDrawingCacheSize()代替;获取最大的图形可缓存大小,单位bytes

public static int getMaximumDrawingCacheSize() {}

public int getScaledMaximumDrawingCacheSize() {}

......

}

 

 

1. dip: device independent pixels(设备独立像素). 不同设备有不同的显示效果,这个和设备硬件有关,一般我们为了支持WVGAHVGAQVGA 推荐使用这    这个,不依赖像素。 
   
这里要特别注意dip与屏幕密度有关,而屏幕密度又与具体的硬件有关,硬件设置不正确,有可能导致dip不能正常显示。在屏幕密度为160的显示屏上,1dip=1px,有时候可能你的屏幕分辨率很大如480*800,但是屏幕密度没有正确设置比如说还是160,那么这个时候凡是使用dip的都会显示异常,基本都是显示过小。 
     dip
的换算: 
           dip
value=(int) (pxvalue/1.5 + 0.5) 
2. dp:
很简单,和dip是一样的。 
3. px: pixels(
像素),不同的设备不同的显示屏显示效果是相同的,这是绝对像素,是多少就永远是多少不会改变。 
4.  sp: scaled pixels(
放大像素). 主要用于字体显示best for textsize 

备注: 根据google的推荐,像素统一使用dip,字体统一使用sp  

举个例子区别pxdip

px就是像素,如果用px,就会用实际像素画,比个如吧,用画一条长度为240px的横线,在480宽的模拟器上看就是一半的屏宽,而在320宽的模拟器上看就是23的屏宽了。

dip,就是把屏幕的高分成480分,宽分成320分。比如你做一条160dip的横线,无论你在320480的模拟器上,都是一半屏的长度。


public static int dip2px(Context context, float dipValue){ 
                final float scale = context.getResources().getDisplayMetrics().density; 
                return (int)(dipValue * scale + 0.5f); 
        } 
        
    public static int px2dip(Context context, float pxValue){ 
                final float scale = context.getResources().getDisplayMetrics().density; 
                return (int)(pxValue / scale + 0.5f); 
        } 

 

class MyScrollListener implements OnScrollListener {  
  
        @Override  
        public void onScroll(AbsListView view, int firstVisibleItem,  
                int visibleItemCount, int totalItemCount) {  
            /** 
            * firstVisibleItem
表示在当前屏幕显示的第一个listItem在整个listView里面的位置(下标从0开始) 
            * visibleItemCount
表示在现时屏幕可以见到的ListItem(部分显示的ListItem也算)总数 
            * totalItemCount
表示ListViewListItem总数  
            * listView.getLastVisiblePosition()
表示在现时屏幕最后一个ListItem 
            * (
最后ListItem要完全显示出来才算)在整个ListView的位置(下标从0开始)  
            */  
          
        }  
            
                                                                                                                                        
        @Override  
        public void onScrollStateChanged(AbsListView view, int scrollState) { 
    /**
    *scrollState
有三种状态,分别是SCROLL_STATE_IDLESCROLL_STATE_TOUCH_SCROLLSCROLL_STATE_FLING
    *SCROLL_STATE_IDLE
是当屏幕停止滚动时
    *SCROLL_STATE_TOUCH_SCROLL
是当用户在以触屏方式滚动屏幕并且手指仍然还在屏幕上时(The user is scrolling using touch, and their finger is still on the screen
    *SCROLL_STATE_FLING
是当用户由于之前划动屏幕并抬起手指,屏幕产生惯性滑动时(The user had previously been scrolling using touch and had performed a fling
    */  
            Log.d("load", "onScrollStateChanged");  
        }  
    } 

posted on 2016-06-10 15:50  农夫山药  阅读(858)  评论(0编辑  收藏  举报