在android的源代码中,屏幕之间的跳转是如何实现的呢?在workspace.java中开始。在这个类中,为实现屏幕切换主要重写了以下几个方法:onMeasure()、onLayout()、onInterceptTouchEvent()、onTouchEvent()方法,另外还是用了CustomScroller mScroller来平滑过渡各个页面之间的切换。
首先,我们看一下onMeasure()方法,根据字面意思,就是测量,主要负责测量各个控件的高度和宽度:
-
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-
final int width = MeasureSpec.getSize(widthMeasureSpec);
-
final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
-
if (widthMode != MeasureSpec.EXACTLY) {
-
throw new IllegalStateException("Workspace can only be used in EXACTLY mode.");
-
}
-
final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
-
if (heightMode != MeasureSpec.EXACTLY) {
-
throw new IllegalStateException("Workspace can only be used in EXACTLY mode.");
-
}
-
-
// The children are given the same width and height as the workspace
-
final int count = getChildCount();
-
for (int i = 0; i < count; i++) {
-
getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec);
-
}
-
//ADW: measure wallpaper when using old rendering
-
if(!lwpSupport){
-
if (mWallpaperLoaded) {
-
mWallpaperLoaded = false;
-
mWallpaperWidth = mWallpaperDrawable.getIntrinsicWidth();
-
mWallpaperHeight = mWallpaperDrawable.getIntrinsicHeight();
-
}
-
-
final int wallpaperWidth = mWallpaperWidth;
-
mWallpaperOffset = wallpaperWidth > width ? (count * width - wallpaperWidth) /
-
((count - 1) * (float) width) : 1.0f;
-
}
-
if (mFirstLayout) {
-
scrollTo(mCurrentScreen * width, 0);
-
mScroller.startScroll(0, 0, mCurrentScreen * width, 0, 0);
-
if(lwpSupport)updateWallpaperOffset(width * (getChildCount() - 1));
-
mFirstLayout = false;
-
}
-
/*int max = 3;
-
int aW = getMeasuredWidth();
-
float w = aW / max;
-
maxPreviewWidth=(int) w;
-
maxPreviewHeight=(int) (getMeasuredHeight()*(w/getMeasuredWidth()));*/
-
}
在这里,得到屏幕的宽高,然后再枚举其中所有的子view,设置它们的布局(使他们的高和父控件一样),这样每一个子view就是充满屏幕可以滑动显示的其中一页。
下面是onLayout()方法:
-
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-
int childLeft = 0;
-
final int count = getChildCount();
-
for (int i = 0; i < count; i++) {
-
final View child = getChildAt(i);
-
if (child.getVisibility() != View.GONE) {
-
final int childWidth = child.getMeasuredWidth();
-
child.layout(childLeft, 0, childLeft + childWidth, child.getMeasuredHeight());
-
childLeft += childWidth;
-
}
-
}
-
//ADW:updateWallpaperoffset
-
if(lwpSupport){
-
if(mWallpaperScroll)
-
updateWallpaperOffset();
-
else
-
centerWallpaperOffset();
-
}
-
}
onLayout方法中,横向画出每一个子view,view的高与屏幕高一致,宽度为getChildCount()-1个屏幕宽度的view。
再看一下onInterceptTouchEvent()方法:
-
public boolean onInterceptTouchEvent(MotionEvent ev) {
-
if(mStatus==SENSE_OPEN){
-
if(ev.getAction()==MotionEvent.ACTION_DOWN){
-
findClickedPreview(ev.getX(),ev.getY());
-
}
-
return true;
-
}
-
-
//Wysie: If multitouch event is detected
-
if (multiTouchController.onTouchEvent(ev)) {
-
return false;
-
}
-
-
if (mLocked || mLauncher.isAllAppsVisible()) {
-
return true;
-
}
-
-
/*
-
* 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.
-
*/
-
-
/*
-
* Shortcut the most recurring case: the user is in the dragging
-
* state and he is moving his finger. We want to intercept this
-
* motion.
-
*/
-
final int action = ev.getAction();
-
if ((action == MotionEvent.ACTION_MOVE) && (mTouchState != TOUCH_STATE_REST)) {
-
return true;
-
}
-
-
final float x = ev.getX();
-
final float y = ev.getY();
-
-
switch (action) {
-
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.
-
*/
-
-
/*
-
* Locally do absolute value. mLastMotionX is set to the y value
-
* of the down event.
-
*/
-
final int xDiff = (int) Math.abs(x - mLastMotionX);
-
final int yDiff = (int) Math.abs(y - mLastMotionY);
-
-
final int touchSlop = mTouchSlop;
-
boolean xMoved = xDiff > touchSlop;
-
boolean yMoved = yDiff > touchSlop;
-
if (xMoved || yMoved) {
-
// If xDiff > yDiff means the finger path pitch is smaller than 45deg so we assume the user want to scroll X axis
-
if (xDiff > yDiff) {
-
// Scroll if the user moved far enough along the X axis
-
mTouchState = TOUCH_STATE_SCROLLING;
-
enableChildrenCache();
-
-
}
-
// If yDiff > xDiff means the finger path pitch is bigger than 45deg so we assume the user want to either scroll Y or Y-axis gesture
-
else if (getOpenFolder()==null)
-
{
-
// As x scrolling is left untouched (more or less untouched;)), every gesture should start by dragging in Y axis. In fact I only consider useful, swipe up and down.
-
// Guess if the first Pointer where the user click belongs to where a scrollable widget is.
-
mTouchedScrollableWidget = isWidgetAtLocationScrollable((int)mLastMotionX,(int)mLastMotionY);
-
if (!mTouchedScrollableWidget)
-
{
-
// Only y axis movement. So may be a Swipe down or up gesture
-
if ((y - mLastMotionY) > 0){
-
if(Math.abs(y-mLastMotionY)>(touchSlop*2))mTouchState = TOUCH_SWIPE_DOWN_GESTURE;
-
}else{
-
if(Math.abs(y-mLastMotionY)>(touchSlop*2))mTouchState = TOUCH_SWIPE_UP_GESTURE;
-
}
-
}
-
}
-
// Either way, cancel any pending longpress
-
if (mAllowLongPress) {
-
mAllowLongPress = false;
-
// Try canceling the long press. It could also have been scheduled
-
// by a distant descendant, so use the mAllowLongPress flag to block
-
// everything
-
final View currentScreen = getChildAt(mCurrentScreen);
-
currentScreen.cancelLongPress();
-
}
-
}
-
break;
-
-
case MotionEvent.ACTION_DOWN:
-
// Remember location of down touch
-
mLastMotionX = x;
-
mLastMotionY = y;
-
mAllowLongPress = true;
-
-
/*
-
* If being flinged and user touches the screen, initiate drag;
-
* otherwise don't. mScroller.isFinished should be false when
-
* being flinged.
-
*/
-
mTouchState = mScroller.isFinished() ? TOUCH_STATE_REST : TOUCH_STATE_SCROLLING;
-
break;
-
-
case MotionEvent.ACTION_CANCEL:
-
case MotionEvent.ACTION_UP:
-
-
if (mTouchState != TOUCH_STATE_SCROLLING && mTouchState != TOUCH_SWIPE_DOWN_GESTURE && mTouchState != TOUCH_SWIPE_UP_GESTURE) {
-
final CellLayout currentScreen = (CellLayout) getChildAt(mCurrentScreen);
-
if (!currentScreen.lastDownOnOccupiedCell()) {
-
getLocationOnScreen(mTempCell);
-
// Send a tap to the wallpaper if the last down was on empty space
-
if(lwpSupport)
-
mWallpaperManager.sendWallpaperCommand(getWindowToken(),
-
"android.wallpaper.tap",
-
mTempCell[0] + (int) ev.getX(),
-
mTempCell[1] + (int) ev.getY(), 0, null);
-
}
-
}
-
// Release the drag
-
clearChildrenCache();
-
mTouchState = TOUCH_STATE_REST;
-
mAllowLongPress = false;
-
break;
-
}
-
-
/*
-
* The only time we want to intercept motion events is if we are in the
-
* drag mode.
-
*/
-
return mTouchState != TOUCH_STATE_REST;
-
}
onInterceptTouchEvent()方法和下面的onTouchEvent()主要是来响应手指按下划动时所需要捕获的消息,例如划动的速度,划动的距离等。再配合使用scrollBy (int x, int y)方法得到慢速滑动小距离的时候,所需要显示的内容。最后当手指起来时,根据划动的速度与跨度来判断是向左滑动一页还是向右滑动一页,确保每次用户操作结束之后显示的都是整体的一个子view.
-
public boolean onTouchEvent(MotionEvent ev) {
-
//Wysie: If multitouch event is detected
-
/*if (multiTouchController.onTouchEvent(ev)) {
-
return false;
-
}*/
-
if (mLocked || mLauncher.isAllAppsVisible() || mSensemode) {
-
return true;
-
}
-
-
if (mVelocityTracker == null) {
-
mVelocityTracker = VelocityTracker.obtain();
-
}
-
mVelocityTracker.addMovement(ev);
-
-
final int action = ev.getAction();
-
final float x = ev.getX();
-
-
switch (action) {
-
case MotionEvent.ACTION_DOWN:
-
/*
-
* If being flinged and user touches, stop the fling. isFinished
-
* will be false if being flinged.
-
*/
-
if (!mScroller.isFinished()) {
-
mScroller.abortAnimation();
-
}
-
-
// Remember where the motion event started
-
mLastMotionX = x;
-
break;
-
case MotionEvent.ACTION_MOVE:
-
if (mTouchState == TOUCH_STATE_SCROLLING) {
-
// Scroll to follow the motion event
-
final int deltaX = (int) (mLastMotionX - x);
-
mLastMotionX = x;
-
-
if (deltaX < 0) {
-
if (mScrollX > -mScrollingBounce) {
-
scrollBy(Math.min(deltaX,mScrollingBounce), 0);
-
if(lwpSupport)updateWallpaperOffset();
-
if(mLauncher.getDesktopIndicator()!=null)mLauncher.getDesktopIndicator().indicate((float)getScrollX()/(float)(getChildCount()*getWidth()));
-
}
-
} else if (deltaX > 0) {
-
final int availableToScroll = getChildAt(getChildCount() - 1).getRight() -
-
mScrollX - getWidth()+mScrollingBounce;
-
if (availableToScroll > 0) {
-
scrollBy(deltaX, 0);
-
if(lwpSupport)updateWallpaperOffset();
-
if(mLauncher.getDesktopIndicator()!=null)mLauncher.getDesktopIndicator().indicate((float)getScrollX()/(float)(getChildCount()*getWidth()));
-
}
-
}
-
}
-
break;
-
case MotionEvent.ACTION_UP:
-
if (mTouchState == TOUCH_STATE_SCROLLING) {
-
final VelocityTracker velocityTracker = mVelocityTracker;
-
velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
-
int velocityX = (int) velocityTracker.getXVelocity();
-
-
if (velocityX > SNAP_VELOCITY && mCurrentScreen > 0) {
-
// Fling hard enough to move left
-
snapToScreen(mCurrentScreen - 1);
-
} else if (velocityX < -SNAP_VELOCITY && mCurrentScreen < getChildCount() - 1) {
-
// Fling hard enough to move right
-
snapToScreen(mCurrentScreen + 1);
-
} else {
-
snapToDestination();
-
}
-
-
if (mVelocityTracker != null) {
-
mVelocityTracker.recycle();
-
mVelocityTracker = null;
-
}
-
} else if (mTouchState == TOUCH_SWIPE_DOWN_GESTURE )
-
{
-
mLauncher.fireSwipeDownAction();
-
} else if (mTouchState == TOUCH_SWIPE_UP_GESTURE )
-
{
-
mLauncher.fireSwipeUpAction();
-
}
-
mTouchState = TOUCH_STATE_REST;
-
break;
-
case MotionEvent.ACTION_CANCEL:
-
mTouchState = TOUCH_STATE_REST;
-
}
-
-
return true;
-
}
以上就是launcher中左右滑动屏幕切换源码。
有需要代码的朋友,请到下载即可。
阅读(792) | 评论(0) | 转发(0) |