滑动
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
//flings和scrolls摩擦力度大小的系数
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(设备独立像素). 不同设备有不同的显示效果,这个和设备硬件有关,一般我们为了支持WVGA、HVGA和QVGA 推荐使用这 这个,不依赖像素。
这里要特别注意dip与屏幕密度有关,而屏幕密度又与具体的硬件有关,硬件设置不正确,有可能导致dip不能正常显示。在屏幕密度为160的显示屏上,1dip=1px,有时候可能你的屏幕分辨率很大如480*800,但是屏幕密度没有正确设置比如说还是160,那么这个时候凡是使用dip的都会显示异常,基本都是显示过小。
dip的换算:
dip(value)=(int) (px(value)/1.5 + 0.5)
2. dp: 很简单,和dip是一样的。
3. px: pixels(像素),不同的设备不同的显示屏显示效果是相同的,这是绝对像素,是多少就永远是多少不会改变。
4. sp: scaled pixels(放大像素). 主要用于字体显示best for textsize。
备注: 根据google的推荐,像素统一使用dip,字体统一使用sp
举个例子区别px和dip:
px就是像素,如果用px,就会用实际像素画,比个如吧,用画一条长度为240px的横线,在480宽的模拟器上看就是一半的屏宽,而在320宽的模拟器上看就是2/3的屏宽了。
而dip,就是把屏幕的高分成480分,宽分成320分。比如你做一条160dip的横线,无论你在320还480的模拟器上,都是一半屏的长度。
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表示ListView的ListItem总数
* listView.getLastVisiblePosition()表示在现时屏幕最后一个ListItem
* (最后ListItem要完全显示出来才算)在整个ListView的位置(下标从0开始)
*/
}
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
/**
*scrollState有三种状态,分别是SCROLL_STATE_IDLE、SCROLL_STATE_TOUCH_SCROLL、SCROLL_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");
}
}