android滑动切换屏幕(扒的是launcher2/workspace的源码)

一直对像是墨迹天气呀,还有android系统默认的桌面的切换效果很感兴趣,上网查找了不少资料,秉承这不要重复发明轮子的原则,决定直接去扒android桌面的源码,后来发现扒扒源码也是不错的,于是乎就找来launcher2/workspace的源码剃去不要的,竟然真让我扒成了,效果跟android的桌面是一样的,只不过没有背景没有图标

其实,workspace本身就是一个自定义的ViewGroup(UI的水也挺深啊~~T.T),用法和那些基本布局类似,只不过需要添加一些小的修改就可以当作布局来用了,没怎么在做过测试,反正目前自我感觉良好~~

 

话说为啥米木有滑屏的效果图?我能告诉你我一边用鼠标在模拟器上瞎划啦,另一只手没办法截图吗??(这个以后会好的……会好的……)

废话少说,上代码

Workspace.java 懒死我算了,名字我都没改

 

/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.lynn.practice.view;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.widget.Scroller;

import com.lynn.practice.R;

/**
* The workspace is a wide area with a wallpaper and a finite number of screens. Each
* screen contains a number of icons, folders or widgets the user can interact with.
* A workspace is meant to be used with a fixed width only.
*/
public class Workspace extends ViewGroup {
private static final int INVALID_SCREEN = -1;

/**
* The velocity at which a fling gesture will cause us to snap to the next screen
*/
private static final int SNAP_VELOCITY = 1000;

private int mDefaultScreen;

private boolean mFirstLayout = true;

private int mCurrentScreen;
private int mNextScreen = INVALID_SCREEN;
private Scroller mScroller;
private VelocityTracker mVelocityTracker;

/**
* CellInfo for the cell that is currently being dragged
*/
// private CellLayout.CellInfo mDragInfo;

/**
* Target drop area calculated during last acceptDrop call.
*/


private float mLastMotionX;
private float mLastMotionY;

private final static int TOUCH_STATE_REST = 0;
private final static int TOUCH_STATE_SCROLLING = 1;

private int mTouchState = TOUCH_STATE_REST;


/* private Launcher mLauncher;
private DragController mDragger;
*/

/**
* Cache of vacant cells, used during drag events and invalidated as needed.
*/
/* private CellLayout.CellInfo mVacantCache = null;*/



private boolean mAllowLongPress;


private int mTouchSlop;
private int mMaximumVelocity;

final Rect mDrawerBounds = new Rect();
final Rect mClipBounds = new Rect();
int mDrawerContentHeight;
int mDrawerContentWidth;

/**
* Used to inflate the Workspace from XML.
*
*
@param context The application's context.
*
@param attrs The attribtues set containing the Workspace's customization values.
*/
public Workspace(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}

/**
* Used to inflate the Workspace from XML.
*
*
@param context The application's context.
*
@param attrs The attribtues set containing the Workspace's customization values.
*
@param defStyle Unused.
*/
public Workspace(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);

TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Workspace, defStyle, 0);
mDefaultScreen = a.getInt(R.styleable.Workspace_defaultScreen, 1);
a.recycle();

initWorkspace();
}

/**
* Initializes various states for this workspace.
*/
private void initWorkspace() {
mScroller = new Scroller(getContext());
mCurrentScreen = mDefaultScreen;
/// Launcher.setScreen(mCurrentScreen);
Log.i("144",mScroller.isFinished()+"");
final ViewConfiguration configuration = ViewConfiguration.get(getContext());
mTouchSlop = configuration.getScaledTouchSlop();
System.out.println("mTouchSlop:"+mTouchSlop);
mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
}

@Override
public void addView(View child, int index, LayoutParams params) {
/* if (!(child instanceof CellLayout)) {
throw new IllegalArgumentException("A Workspace can only have CellLayout children.");
}
*/
super.addView(child, index, params);
}

@Override
public void addView(View child) {
/*if (!(child instanceof CellLayout)) {
throw new IllegalArgumentException("A Workspace can only have CellLayout children.");
}
*/
super.addView(child);
}

@Override
public void addView(View child, int index) {
/* if (!(child instanceof CellLayout)) {
throw new IllegalArgumentException("A Workspace can only have CellLayout children.");
}
*/
super.addView(child, index);
}

@Override
public void addView(View child, int width, int height) {
/* if (!(child instanceof CellLayout)) {
throw new IllegalArgumentException("A Workspace can only have CellLayout children.");
}
*/
super.addView(child, width, height);
}

@Override
public void addView(View child, LayoutParams params) {
/* if (!(child instanceof CellLayout)) {
throw new IllegalArgumentException("A Workspace can only have CellLayout children.");
}
*/
super.addView(child, params);
}

/**
*
@return The open folder on the current screen, or null if there is none
*/


boolean isDefaultScreenShowing() {
return mCurrentScreen == mDefaultScreen;
}

/**
* Returns the index of the currently displayed screen.
*
*
@return The index of the currently displayed screen.
*/
int getCurrentScreen() {
return mCurrentScreen;
}

/**
* Sets the current screen.
*
*
@param currentScreen
*/
void setCurrentScreen(int currentScreen) {

mCurrentScreen = Math.max(0, Math.min(currentScreen, getChildCount() - 1));
scrollTo(mCurrentScreen * getWidth(), 0);

invalidate();
}

/**



/**
* Registers the specified listener on each screen contained in this workspace.
*
*
@param l The listener used to respond to long clicks.
*/



@Override
public void computeScroll() {
if (mScroller.computeScrollOffset()) {
scrollTo(mScroller.getCurrX(),mScroller.getCurrY());

postInvalidate();
} else if (mNextScreen != INVALID_SCREEN) {
mCurrentScreen = Math.max(0, Math.min(mNextScreen, getChildCount() - 1));

mNextScreen = INVALID_SCREEN;

}
}

@Override
public boolean isOpaque() {

return false;
}
@Override
public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) {
int screen = indexOfChild(child);
if (screen != mCurrentScreen || !mScroller.isFinished()) {

snapToScreen(screen);

return true;
}
return false;
}


@Override
protected void dispatchDraw(Canvas canvas) {
boolean restore = false;



boolean fastDraw = mTouchState != TOUCH_STATE_SCROLLING && mNextScreen == INVALID_SCREEN;
// If we are not scrolling or flinging, draw only the current screen
if (fastDraw) {
drawChild(canvas, getChildAt(mCurrentScreen), getDrawingTime());
} else {
final long drawingTime = getDrawingTime();
// If we are flinging, draw only the current screen and the target screen
if (mNextScreen >= 0 && mNextScreen < getChildCount() &&
Math.abs(mCurrentScreen - mNextScreen) == 1) {
drawChild(canvas, getChildAt(mCurrentScreen), drawingTime);
drawChild(canvas, getChildAt(mNextScreen), drawingTime);
} else {
// If we are scrolling, draw all of our children
final int count = getChildCount();
for (int i = 0; i < count; i++) {
drawChild(canvas, getChildAt(i), drawingTime);
}
}
}

if (restore) {
canvas.restore();
}
}

@Override
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.UNSPECIFIED) {
throw new IllegalStateException("Workspace can only be used in EXACTLY mode.");
}
*/
final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
/* if (heightMode != MeasureSpec.AT_MOST) {
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);
}

if (mFirstLayout) {
scrollTo(mCurrentScreen * width, 0);

mFirstLayout = false;
}
}

@Override
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;
}
}
}




@Override
public boolean dispatchUnhandledMove(View focused, int direction) {
if (direction == View.FOCUS_LEFT) {
if (getCurrentScreen() > 0) {
snapToScreen(getCurrentScreen() - 1);
return true;
}
} else if (direction == View.FOCUS_RIGHT) {
if (getCurrentScreen() < getChildCount() - 1) {
snapToScreen(getCurrentScreen() + 1);
return true;
}
}
return super.dispatchUnhandledMove(focused, direction);
}



@Override
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.
*/

/*
* 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 (xMoved) {
// Scroll if the user moved far enough along the X axis
mTouchState = TOUCH_STATE_SCROLLING;
System.out.println("422 mTouchState"+mTouchState);

}
// 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.
*/

Log.i("453",mScroller.isFinished()+"");
mTouchState = mScroller.isFinished() ? TOUCH_STATE_SCROLLING: TOUCH_STATE_REST;
 

System.out.println("449 mTouchState"+mTouchState);
break;

case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:


// Release the drag

// 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;
}


@Override
public boolean onTouchEvent(MotionEvent ev) {

final int action = ev.getAction();
final float x = ev.getX();
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}
mVelocityTracker.addMovement(ev);
switch (action) {
case MotionEvent.ACTION_DOWN:
/*
* If being flinged and user touches, stop the fling. isFinished
* will be false if being flinged.
*/
System.out.println("MotionEvent.ACTION_DOWN");
if (!mScroller.isFinished()) {

mScroller.abortAnimation();
Log.i("495",mScroller.isFinished()+"");
}

// Remember where the motion event started
mLastMotionX = x;
break;
case MotionEvent.ACTION_MOVE:
System.out.println("493 mTouchState"+mTouchState);
if (mTouchState == TOUCH_STATE_SCROLLING) {
// Scroll to follow the motion event
System.out.println("MotionEvent.ACTION_MOVE");
final int deltaX = (int) (mLastMotionX - x);
mLastMotionX = x;

if (deltaX < 0) {
if (this.getScrollX() > 0) {
scrollBy(Math.max(-this.getScrollX(), deltaX), 0);

}
} else if (deltaX > 0) {
final int availableToScroll = getChildAt(getChildCount() - 1).getRight() -
this.getScrollX() - getWidth();
if (availableToScroll > 0) {
scrollBy(Math.min(availableToScroll, deltaX), 0);

}
}
}
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;
}
}
System.out.println("536 mTouchState"+mTouchState);
mTouchState = TOUCH_STATE_REST;
break;
case MotionEvent.ACTION_CANCEL:
System.out.println("540 mTouchState"+mTouchState);
mTouchState = TOUCH_STATE_REST;
}

return true;
}

private void snapToDestination() {
final int screenWidth = getWidth();
final int whichScreen = (this.getScrollX() + (screenWidth / 2)) / screenWidth;

snapToScreen(whichScreen);
}

void snapToScreen(int whichScreen) {
if (!mScroller.isFinished()) return;


whichScreen = Math.max(0, Math.min(whichScreen, getChildCount() - 1));
boolean changingScreens = whichScreen != mCurrentScreen;

mNextScreen = whichScreen;

View focusedChild = getFocusedChild();
if (focusedChild != null && changingScreens && focusedChild == getChildAt(mCurrentScreen)) {
focusedChild.clearFocus();
}

final int newX = whichScreen * getWidth();
final int delta = newX - this.getScrollX();
mScroller.startScroll(this.getScrollX(), 0, delta, 0, Math.abs(delta) * 2);
invalidate();
}


@Override
protected Parcelable onSaveInstanceState() {
final SavedState state = new SavedState(super.onSaveInstanceState());
state.currentScreen = mCurrentScreen;
return state;
}

@Override
protected void onRestoreInstanceState(Parcelable state) {
SavedState savedState = (SavedState) state;
super.onRestoreInstanceState(savedState.getSuperState());
if (savedState.currentScreen != -1) {
mCurrentScreen = savedState.currentScreen;

}
}

public void scrollLeft() {

if (mNextScreen == INVALID_SCREEN && mCurrentScreen > 0 && mScroller.isFinished()) {
snapToScreen(mCurrentScreen - 1);
}
}

public void scrollRight() {

if (mNextScreen == INVALID_SCREEN && mCurrentScreen < getChildCount() -1 &&
mScroller.isFinished()) {
snapToScreen(mCurrentScreen + 1);
}
}

public int getScreenForView(View v) {
int result = -1;
if (v != null) {
ViewParent vp = v.getParent();
int count = getChildCount();
for (int i = 0; i < count; i++) {
if (vp == getChildAt(i)) {
return i;
}
}
}
return result;
}




void moveToDefaultScreen() {
snapToScreen(mDefaultScreen);
getChildAt(mDefaultScreen).requestFocus();
}

public static class SavedState extends BaseSavedState {
int currentScreen = -1;

SavedState(Parcelable superState) {
super(superState);
}

private SavedState(Parcel in) {
super(in);
currentScreen = in.readInt();
}

@Override
public void writeToParcel(Parcel out, int flags) {
super.writeToParcel(out, flags);
out.writeInt(currentScreen);
}

public static final Parcelable.Creator<SavedState> CREATOR =
new Parcelable.Creator<SavedState>() {
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}

public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
}
}


基本上就是删代码,看到XX的就删掉,8过有个地方倒是让我很郁闷,就是上面黄色框框里的那行代码,和原来的源码比表达式后面的条件是完全相反的--!,不知倒为啥会介个样子,按照源码那样来的话,画面是完全不动滴,还望如果有人知道的话,请指教一二~

 

res/value/attr.xml 继续元封不动的扒

 1 <?xml version="1.0" encoding="utf-8"?>
2 <resources>
3 <declare-styleable name="Workspace"><!-- The first screen the workspace should display. -->
4 <attr name="defaultScreen" format="integer"/>
5 <!-- The number of horizontal cells in the CellLayout -->
6 <attr name="cellCountX" format="integer"/>
7 <!-- The number of vertical cells in the CellLayout -->
8 <attr name="cellCountY" format="integer"/></declare-styleable>
9
10 </resources>

这样的话,这个效果基本上就会实现了

下一步就是怎么用了,恩恩,很简单啦,就是自定义view里面使用include标签就好了,不过不要忘记了,在root布局里面加上

   xmlns:launcher="http://schemas.android.com/apk/res/com.lynn.practice"要不然~嘿嘿,,,,,

 

gallry_view.xml

 1 <?xml version="1.0" encoding="utf-8"?>
2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
3 xmlns:launcher="http://schemas.android.com/apk/res/com.lynn.practice"
4 android:layout_width="match_parent"
5 android:layout_height="match_parent"
6 android:orientation="vertical" >
7 <com.lynn.practice.view.Workspace android:layout_width="match_parent"
8 android:layout_height="match_parent"
9 android:id="@+id/workspace"
10 launcher:defaultScreen="0"
11 >
12 <include layout="@layout/main" />
13 <include layout="@layout/main2" />
14
15 </com.lynn.practice.view.Workspace>
16
17 </LinearLayout>

 

扒这个代码用了我一下午的时间,好吧,我承认我就是个蜗牛~~总是在立志,但是立志总是不长,希望通过博客激励我,在android的道路上越行越远吧~~

 

 

 

posted on 2012-03-11 12:17  曼妙琳珑心  阅读(7754)  评论(2编辑  收藏  举报

导航