android Launcher的滑动效果怎么实现?
3个回答
展开全部
滑动功能主要分两步:
1、在onInterceptTouchEvent中进行拦截。
2、在onTouchEvent中进行滑动。
1,onInterceptTouchEvent(MotionEvent en)
在这个方法中,决定了什么时候截获MotionEvent来实现滑动,避免了子View的其他事件的影响(如点击事件)。
[java] view plaincopy
public boolean onInterceptTouchEvent(MotionEvent ev) {
/**
* This method JUST determines whether we want to intercept the motion.
* If we return true, onTouchEvent will be called and we do the actual
* scrolling there.
**/
//获取速度跟踪器,记录各个时刻的速度。并且添加当前的MotionEvent以记录更行速度值。
acquireVelocityTrackerAndAddMovement(ev);
......
/**
* Shortcut the most recurring case: the user is in the dragging
* state and he is moving his finger. We want to intercept this
* motion.
* 最常见的需要拦截的情况:用户已经进入滑动状态,并且正在滑动手指。
* 对这种情况直接进行拦截,执行onTouchEvent()继续执行滑动操作。
**/
final int action = ev.getAction();
if ((action == MotionEvent.ACTION_MOVE) &&
(mTouchState == TOUCH_STATE_SCROLLING)) {
return true;
}
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_MOVE: {
/**
* mIsBeingDragged == false, otherwise the shortcut would have caught it. Check
* whether the user has moved far enough from his original down touch.
*/
/**
* 当在这里接受到ACTION_MOVE时,说明mTouchState!=TOUCH_STATE_SCROLLING并且mIsBeingDragged的值应该为false,
* 否则DragLayer就应该截获了MotionEvent用于实现拖拽。
* 此时还没有进入滑动状态,当mActivePointerId == INVALID_POINTER时,也就是在此之前没有接收到任何touch事件。
* 这种情况发生在Workspace变小时,也就是之前Workspace处于SPRING_LOADED状态。当出现这种情况时直接把当前的事件当作ACTION_DOWN进行处理。
* 反之,则通过determineScrollingStart()尝试能够进入滑动状态。
*/
if (mActivePointerId != INVALID_POINTER) {
determineScrollingStart(ev);
break;
}
// if mActivePointerId is INVALID_POINTER, then we must have missed an ACTION_DOWN
// event. in that case, treat the first occurence of a move event as a ACTION_DOWN
// i.e. fall through to the next case (don't break)
// (We sometimes miss ACTION_DOWN events in Workspace because it ignores all events
// while it's small- this was causing a crash before we checked for INVALID_POINTER)
}
case MotionEvent.ACTION_DOWN: {
final float x = ev.getX();
final float y = ev.getY();
// Remember location of down touch
//记录按下的x的坐标值
mDownMotionX = x;
//记录前次发生touch时的坐标
mLastMotionX = x;
mLastMotionY = y;
//因为在ScrollBy时只能使用int,而记录的x和y都是float,会产生误差,故这里用mLastMotionXRemainder记录余数
//用于消除误差
mLastMotionXRemainder = 0;
//x方向上的总位移
mTotalMotionX = 0;
mActivePointerId = ev.getPointerId(0);
//设置mAllowLongPress=true,允许LongClick事件发生。LongClick事件定义在Launcher中
//处理的内容包括启动对shortcut的拖拽或弹出壁纸选择的对话框,若mAllowLongPress=false,
//则不会响应以上事件。
mAllowLongPress = true;
/**
* If being flinged and user touches the screen, initiate drag;
* otherwise don't. mScroller.isFinished should be false when
* being flinged.
* 当屏幕处于flinged状态(快速滑动)时,若此时用户触摸了屏幕,需要使滑动停止。
* 并且初始化拖拽的条件
**/
final int xDist = Math.abs(mScroller.getFinalX() - mScroller.getCurrX());
final boolean finishedScrolling = (mScroller.isFinished() || xDist < mTouchSlop);
if (finishedScrolling) {
mTouchState = TOUCH_STATE_REST;
mScroller.abortAnimation();
} else {
mTouchState = TOUCH_STATE_SCROLLING;
}
// check if this can be the beginning of a tap on the side of the pages
// to scroll the current page
if (mTouchState != TOUCH_STATE_PREV_PAGE && mTouchState != TOUCH_STATE_NEXT_PAGE) {
if (getChildCount() > 0) {
if (hitsPreviousPage(x, y)) {
mTouchState = TOUCH_STATE_PREV_PAGE;
} else if (hitsNextPage(x, y)) {
mTouchState = TOUCH_STATE_NEXT_PAGE;
}
}
}
break;
}
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
mTouchState = TOUCH_STATE_REST;
mAllowLongPress = false;
mActivePointerId = INVALID_POINTER;
releaseVelocityTracker();
break;
case MotionEvent.ACTION_POINTER_UP:
onSecondaryPointerUp(ev);
releaseVelocityTracker();
break;
}
/**
* The only time we want to intercept motion events is if we are in the
* drag mode.
* 只有进入了滑动状态,才进行拦截,进入onTouchEvent执行滑动操作。当mTouchState != TOUCH_STATE_REST
* 时,就说明没有进入滑动状态。
**/
return mTouchState != TOUCH_STATE_REST;
}
2,onTouchEvent(MotionEvent en)
在这个方法中,执行各种关于滑动的工作的计算,界面的刷新等工作。
[java] view plaincopy
public boolean onTouchEvent(MotionEvent ev) {
......
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
/*
* If being flinged and user touches, stop the fling. isFinished
* will be false if being flinged.
*/
/**
* 如果Workspace此时已经被“掷出去”(靠惯性滑动)。
* 此时发生ACTION_DOWN则需要停止滑动。
*/
if (!mScroller.isFinished()) {
mScroller.abortAnimation();
}
// Remember where the motion event started
mDownMotionX = mLastMotionX = ev.getX();
mLastMotionXRemainder = 0;
mTotalMotionX = 0;
mActivePointerId = ev.getPointerId(0);
if (mTouchState == TOUCH_STATE_SCROLLING) {
pageBeginMoving();
}
break;
case MotionEvent.ACTION_MOVE:
if (mTouchState == TOUCH_STATE_SCROLLING) {
......
if (Math.abs(deltaX) >= 1.0f) {
......
if (!mDeferScrollUpdate) {
//调用scrollBy滑动桌面
scrollBy((int) deltaX, 0);
......
} else {
......
}
mLastMotionX = x;
mLastMotionXRemainder = deltaX - (int) deltaX;
} else {
awakenScrollBars();
}
} else {
/**
* 如果条件满足,则进入滑动状态,开始滑动。
*/
determineScrollingStart(ev);
}
break;
case MotionEvent.ACTION_UP:
if (mTouchState == TOUCH_STATE_SCROLLING) {
......
boolean isSignificantMove = Math.abs(deltaX) > MIN_LENGTH_FOR_MOVE;
boolean returnToOriginalPage = false;
final int pageWidth = getScaledMeasuredWidth(getPageAt(mCurrentPage));
if (Math.abs(deltaX) > pageWidth * RETURN_TO_ORIGINAL_PAGE_THRESHOLD &&
Math.signum(velocityX) != Math.signum(deltaX)) {
returnToOriginalPage = true;
}
boolean isFling = mTotalMotionX > MIN_LENGTH_FOR_FLING &&
Math.abs(velocityX) > snapVelocity;
int finalPage;
//判断拿起手指之后应该进入哪个分屏
if (((isSignificantMove && deltaX > 0 && !isFling) ||
(isFling && velocityX > 0)) && mCurrentPage > 0) {
finalPage = returnToOriginalPage ? mCurrentPage : mCurrentPage - 1;
snapToPageWithVelocity(finalPage, velocityX);
} else if (((isSignificantMove && deltaX < 0 && !isFling) ||
(isFling && velocityX < 0)) &&
mCurrentPage < getChildCount() - 1) {
finalPage = returnToOriginalPage ? mCurrentPage : mCurrentPage + 1;
snapToPageWithVelocity(finalPage, velocityX);
} else {
snapToDestination();
}
} else if (mTouchState == TOUCH_STATE_PREV_PAGE) {
//直接进入前一屏
int nextPage = Math.max(0, mCurrentPage - 1);
if (nextPage != mCurrentPage) {
snapToPage(nextPage);
} else {
snapToDestination();
}
} else if (mTouchState == TOUCH_STATE_NEXT_PAGE) {
//直接进入后一屏
int nextPage = Math.min(getChildCount() - 1, mCurrentPage + 1);
if (nextPage != mCurrentPage) {
snapToPage(nextPage);
} else {
snapToDestination();
}
} else {
onUnhandledTap(ev);
}
mTouchState = TOUCH_STATE_REST;
mActivePointerId = INVALID_POINTER;
releaseVelocityTracker();
break;
case MotionEvent.ACTION_CANCEL:
if (mTouchState == TOUCH_STATE_SCROLLING) {
snapToDestination();
}
mTouchState = TOUCH_STATE_REST;
mActivePointerId = INVALID_POINTER;
releaseVelocityTracker();
break;
case MotionEvent.ACTION_POINTER_UP:
onSecondaryPointerUp(ev);
break;
}
return true;
}
1、在onInterceptTouchEvent中进行拦截。
2、在onTouchEvent中进行滑动。
1,onInterceptTouchEvent(MotionEvent en)
在这个方法中,决定了什么时候截获MotionEvent来实现滑动,避免了子View的其他事件的影响(如点击事件)。
[java] view plaincopy
public boolean onInterceptTouchEvent(MotionEvent ev) {
/**
* This method JUST determines whether we want to intercept the motion.
* If we return true, onTouchEvent will be called and we do the actual
* scrolling there.
**/
//获取速度跟踪器,记录各个时刻的速度。并且添加当前的MotionEvent以记录更行速度值。
acquireVelocityTrackerAndAddMovement(ev);
......
/**
* Shortcut the most recurring case: the user is in the dragging
* state and he is moving his finger. We want to intercept this
* motion.
* 最常见的需要拦截的情况:用户已经进入滑动状态,并且正在滑动手指。
* 对这种情况直接进行拦截,执行onTouchEvent()继续执行滑动操作。
**/
final int action = ev.getAction();
if ((action == MotionEvent.ACTION_MOVE) &&
(mTouchState == TOUCH_STATE_SCROLLING)) {
return true;
}
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_MOVE: {
/**
* mIsBeingDragged == false, otherwise the shortcut would have caught it. Check
* whether the user has moved far enough from his original down touch.
*/
/**
* 当在这里接受到ACTION_MOVE时,说明mTouchState!=TOUCH_STATE_SCROLLING并且mIsBeingDragged的值应该为false,
* 否则DragLayer就应该截获了MotionEvent用于实现拖拽。
* 此时还没有进入滑动状态,当mActivePointerId == INVALID_POINTER时,也就是在此之前没有接收到任何touch事件。
* 这种情况发生在Workspace变小时,也就是之前Workspace处于SPRING_LOADED状态。当出现这种情况时直接把当前的事件当作ACTION_DOWN进行处理。
* 反之,则通过determineScrollingStart()尝试能够进入滑动状态。
*/
if (mActivePointerId != INVALID_POINTER) {
determineScrollingStart(ev);
break;
}
// if mActivePointerId is INVALID_POINTER, then we must have missed an ACTION_DOWN
// event. in that case, treat the first occurence of a move event as a ACTION_DOWN
// i.e. fall through to the next case (don't break)
// (We sometimes miss ACTION_DOWN events in Workspace because it ignores all events
// while it's small- this was causing a crash before we checked for INVALID_POINTER)
}
case MotionEvent.ACTION_DOWN: {
final float x = ev.getX();
final float y = ev.getY();
// Remember location of down touch
//记录按下的x的坐标值
mDownMotionX = x;
//记录前次发生touch时的坐标
mLastMotionX = x;
mLastMotionY = y;
//因为在ScrollBy时只能使用int,而记录的x和y都是float,会产生误差,故这里用mLastMotionXRemainder记录余数
//用于消除误差
mLastMotionXRemainder = 0;
//x方向上的总位移
mTotalMotionX = 0;
mActivePointerId = ev.getPointerId(0);
//设置mAllowLongPress=true,允许LongClick事件发生。LongClick事件定义在Launcher中
//处理的内容包括启动对shortcut的拖拽或弹出壁纸选择的对话框,若mAllowLongPress=false,
//则不会响应以上事件。
mAllowLongPress = true;
/**
* If being flinged and user touches the screen, initiate drag;
* otherwise don't. mScroller.isFinished should be false when
* being flinged.
* 当屏幕处于flinged状态(快速滑动)时,若此时用户触摸了屏幕,需要使滑动停止。
* 并且初始化拖拽的条件
**/
final int xDist = Math.abs(mScroller.getFinalX() - mScroller.getCurrX());
final boolean finishedScrolling = (mScroller.isFinished() || xDist < mTouchSlop);
if (finishedScrolling) {
mTouchState = TOUCH_STATE_REST;
mScroller.abortAnimation();
} else {
mTouchState = TOUCH_STATE_SCROLLING;
}
// check if this can be the beginning of a tap on the side of the pages
// to scroll the current page
if (mTouchState != TOUCH_STATE_PREV_PAGE && mTouchState != TOUCH_STATE_NEXT_PAGE) {
if (getChildCount() > 0) {
if (hitsPreviousPage(x, y)) {
mTouchState = TOUCH_STATE_PREV_PAGE;
} else if (hitsNextPage(x, y)) {
mTouchState = TOUCH_STATE_NEXT_PAGE;
}
}
}
break;
}
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
mTouchState = TOUCH_STATE_REST;
mAllowLongPress = false;
mActivePointerId = INVALID_POINTER;
releaseVelocityTracker();
break;
case MotionEvent.ACTION_POINTER_UP:
onSecondaryPointerUp(ev);
releaseVelocityTracker();
break;
}
/**
* The only time we want to intercept motion events is if we are in the
* drag mode.
* 只有进入了滑动状态,才进行拦截,进入onTouchEvent执行滑动操作。当mTouchState != TOUCH_STATE_REST
* 时,就说明没有进入滑动状态。
**/
return mTouchState != TOUCH_STATE_REST;
}
2,onTouchEvent(MotionEvent en)
在这个方法中,执行各种关于滑动的工作的计算,界面的刷新等工作。
[java] view plaincopy
public boolean onTouchEvent(MotionEvent ev) {
......
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
/*
* If being flinged and user touches, stop the fling. isFinished
* will be false if being flinged.
*/
/**
* 如果Workspace此时已经被“掷出去”(靠惯性滑动)。
* 此时发生ACTION_DOWN则需要停止滑动。
*/
if (!mScroller.isFinished()) {
mScroller.abortAnimation();
}
// Remember where the motion event started
mDownMotionX = mLastMotionX = ev.getX();
mLastMotionXRemainder = 0;
mTotalMotionX = 0;
mActivePointerId = ev.getPointerId(0);
if (mTouchState == TOUCH_STATE_SCROLLING) {
pageBeginMoving();
}
break;
case MotionEvent.ACTION_MOVE:
if (mTouchState == TOUCH_STATE_SCROLLING) {
......
if (Math.abs(deltaX) >= 1.0f) {
......
if (!mDeferScrollUpdate) {
//调用scrollBy滑动桌面
scrollBy((int) deltaX, 0);
......
} else {
......
}
mLastMotionX = x;
mLastMotionXRemainder = deltaX - (int) deltaX;
} else {
awakenScrollBars();
}
} else {
/**
* 如果条件满足,则进入滑动状态,开始滑动。
*/
determineScrollingStart(ev);
}
break;
case MotionEvent.ACTION_UP:
if (mTouchState == TOUCH_STATE_SCROLLING) {
......
boolean isSignificantMove = Math.abs(deltaX) > MIN_LENGTH_FOR_MOVE;
boolean returnToOriginalPage = false;
final int pageWidth = getScaledMeasuredWidth(getPageAt(mCurrentPage));
if (Math.abs(deltaX) > pageWidth * RETURN_TO_ORIGINAL_PAGE_THRESHOLD &&
Math.signum(velocityX) != Math.signum(deltaX)) {
returnToOriginalPage = true;
}
boolean isFling = mTotalMotionX > MIN_LENGTH_FOR_FLING &&
Math.abs(velocityX) > snapVelocity;
int finalPage;
//判断拿起手指之后应该进入哪个分屏
if (((isSignificantMove && deltaX > 0 && !isFling) ||
(isFling && velocityX > 0)) && mCurrentPage > 0) {
finalPage = returnToOriginalPage ? mCurrentPage : mCurrentPage - 1;
snapToPageWithVelocity(finalPage, velocityX);
} else if (((isSignificantMove && deltaX < 0 && !isFling) ||
(isFling && velocityX < 0)) &&
mCurrentPage < getChildCount() - 1) {
finalPage = returnToOriginalPage ? mCurrentPage : mCurrentPage + 1;
snapToPageWithVelocity(finalPage, velocityX);
} else {
snapToDestination();
}
} else if (mTouchState == TOUCH_STATE_PREV_PAGE) {
//直接进入前一屏
int nextPage = Math.max(0, mCurrentPage - 1);
if (nextPage != mCurrentPage) {
snapToPage(nextPage);
} else {
snapToDestination();
}
} else if (mTouchState == TOUCH_STATE_NEXT_PAGE) {
//直接进入后一屏
int nextPage = Math.min(getChildCount() - 1, mCurrentPage + 1);
if (nextPage != mCurrentPage) {
snapToPage(nextPage);
} else {
snapToDestination();
}
} else {
onUnhandledTap(ev);
}
mTouchState = TOUCH_STATE_REST;
mActivePointerId = INVALID_POINTER;
releaseVelocityTracker();
break;
case MotionEvent.ACTION_CANCEL:
if (mTouchState == TOUCH_STATE_SCROLLING) {
snapToDestination();
}
mTouchState = TOUCH_STATE_REST;
mActivePointerId = INVALID_POINTER;
releaseVelocityTracker();
break;
case MotionEvent.ACTION_POINTER_UP:
onSecondaryPointerUp(ev);
break;
}
return true;
}
2013-05-08
展开全部
这个很简单啊,你看过launcher源码吗?在workspace中重写getChildStaticTransformation(View child, Transformation t) 在这个方法里就可以对child做动画了 matrix camera 都能取到
本回答被网友采纳
已赞过
已踩过<
评论
收起
你对这个回答的评价是?
2013-05-08
展开全部
android.graphics包下的camera类可用于这类特效
已赞过
已踩过<
评论
收起
你对这个回答的评价是?
推荐律师服务:
若未解决您的问题,请您详细描述您的问题,通过百度律临进行免费专业咨询