从现有方法来讲为了实现桌面的3D转屏效果主要是通过Launcher中的workspace实现(现有的有源码的方法),具体实现见:
http://www.eoeandroid.com/viewthread.php?tid=27079&extra=&page=1 (写这篇文章也是为了“报答”该作者开源的贡献,共同学习)
不过该方法存在以下几个问题:
- 不同机器的分辨率和内存大小不同,从而使用cache保持截图的方法很有可能会出现内存方面的错误
- 界面上面的变化,例如图标增加和删除,需要程序对应做出很多修改,用以保证整体效果的统一。其根本原因就是修改的模块位置在Launcher中过于考上
- 图标变形和覆盖(我在2.2源码上总是搞不出来,╮(╯▽╰)╭)
转载请注明http://ishelf.iteye.com/admin/blogs/836929
依据以上问题本文从每个屏的dispatchDraw入手,修改CellLayout的dispatchDraw方法,这篇文章先给出2D的实现方式(利用Matrix实现):
由于代码过多,本文只给出做过修改的代码
- ///CellLayout.java
- @Override
- public void dispatchDraw(Canvas canvas) {
- long start_time = System.currentTimeMillis();
- startRotate(canvas, currentX, canvas.getWidth(), canvas.getHeight());
- super.dispatchDraw(canvas);
- canvas.restore();
- long end_time = System.currentTimeMillis();
- Log.d("CellLayout" + currentScrenn, (end_time - start_time) + " ms");
- }
- // 上面的Log信息是用来对比用opengl实现两者效率
- //startRotate使用来计算该屏显示的位置以及显示的大小,xCor是手指移动的位置大小
- public void startRotate(Canvas mCanvas, float xCor, int width, int height) {
- boolean flag = true;
- if (isCurrentScrenn && xCor < 0) {
- xCor = width + xCor;
- flag = false;
- } else if (isCurrentScrenn && xCor >= 0) {
- // xCor = width - xCor;
- } else if (!isCurrentScrenn && xCor < 0) {
- xCor = width + xCor;
- } else if (!isCurrentScrenn && xCor >= 0) {
- flag = false;
- }
- final float SPAN = 0.000424f;
- float f = xCor - 10;
- if (f <= 0) {
- f = 0;
- xCor = 10;
- }// the maximum left
- float value = f * SPAN;
- if (f > width) {
- xCor = width - 10;
- value = 0.127225f;
- }// the maximum right
- if (isBorder) {
- doDraw(mCanvas, new float[] {
- 0, 0, width, 0, width, height, 0, height
- }, new float[] {
- 0, 0, width, 0, width, height, 0, height
- });
- } else if (!flag) {
- doDraw(mCanvas, new float[] {
- 0, 0, width, 0, width, height, 0, height
- }, new float[] {
- 0, 0, xCor, height * (1 / 7.0f - value), xCor, height * (6 / 7.0f + value), 0,
- height
- });
- } else {
- doDraw(mCanvas, new float[] {
- 0, 0, width, 0, width, height, 0, height
- }, new float[] {
- xCor, height * (1 / 30.0f + value), width, 0, width, height, xCor,
- height * (29 / 30.0f - value)
- });
- }
- }
- private Matrix mMatrix = new Matrix();
- private int currentScrenn;
- private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- private boolean isBorder;
- //doDraw使用计算该屏如何变形,这里使用matrix的polyToPoly来实现,具体描述见APIDemo
- private void doDraw(Canvas canvas, float src[], float dst[]) {
- canvas.save();
- mMatrix.setPolyToPoly(src, 0, dst, 0, src.length >> 1);
- canvas.concat(mMatrix);
- switch (currentScrenn) {
- case 0:
- mPaint.setColor(Color.RED);
- break;
- case 1:
- mPaint.setColor(Color.BLUE);
- break;
- case 2:
- mPaint.setColor(Color.YELLOW);
- break;
- case 3:
- mPaint.setColor(Color.CYAN);
- break;
- case 4:
- mPaint.setColor(Color.GREEN);
- break;
- }
- mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
- canvas.drawRect(0, 0, src[4], src[5], mPaint);
- }
以下是workspace,该类主要是要传给cellLayout移动的参数
- // 该方法用来画屏
- protected void dispatchDraw(Canvas canvas) {
- boolean restore = false;
- int restoreCount = 0;
- // ViewGroup.dispatchDraw() supports many features we don't need:
- // clip to padding, layout animation, animation listener, disappearing
- // children, etc. The following implementation attempts to fast-track
- // the drawing dispatch by drawing only what we know needs to be drawn.
- boolean fastDraw = mTouchState != TOUCH_STATE_SCROLLING && mNextScreen == INVALID_SCREEN;
- Log.d("Scroller","dispatchDraw"+mScrollX);
- // If we are not scrolling or flinging, draw only the current screen
- if (fastDraw) {
- ((CellLayout) getChildAt(mCurrentScreen)).setPara(mCurrentScreen,
- (mCurrentScreen - mCurrentScreen) >= 0 ? true : false, true,
- mChangeMotionX - mLastMotionX);
- drawChild(canvas, getChildAt(mCurrentScreen), getDrawingTime());
- } else {
- final long drawingTime = getDrawingTime();
- final float scrollPos = (float) mScrollX / getWidth();
- final int leftScreen = (int) scrollPos;
- final int rightScreen = leftScreen + 1;
- if (leftScreen >= 0) {
- ((CellLayout) getChildAt(leftScreen)).setPara(leftScreen,
- (leftScreen - mCurrentScreen) >= 0 ? true : false, scrollPos == leftScreen,
- mChangeMotionX - mLastMotionX);
- drawChild(canvas, getChildAt(leftScreen), drawingTime);
- }
- if (scrollPos != leftScreen && rightScreen < getChildCount()) {
- ((CellLayout) getChildAt(rightScreen)).setPara(rightScreen, mCurrentScreen
- - rightScreen >= 0 ? true : false, scrollPos == leftScreen, mChangeMotionX
- - mLastMotionX);
- drawChild(canvas, getChildAt(rightScreen), drawingTime);
- }
- }
- if (restore) {
- canvas.restoreToCount(restoreCount);
- }
- }
- @Override
- public boolean onTouchEvent(MotionEvent ev) {
- if (mLauncher.isWorkspaceLocked()) {
- return false; // We don't want the events. Let them fall through to
- // the all apps view.
- }
- if (mLauncher.isAllAppsVisible()) {
- // Cancel any scrolling that is in progress.
- if (!mScroller.isFinished()) {
- mScroller.abortAnimation();
- }
- snapToScreen(mCurrentScreen);
- return false; // We don't want the events. Let them fall through to
- // the all apps view.
- }
- if (mVelocityTracker == null) {
- mVelocityTracker = VelocityTracker.obtain();
- }
- mVelocityTracker.addMovement(ev);
- final int action = ev.getAction();
- 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.
- */
- if (!mScroller.isFinished()) {
- mScroller.abortAnimation();
- }
- // Remember where the motion event started
- mLastMotionX = ev.getX();
- mChangeMotionX = mLastMotionX;
- mActivePointerId = ev.getPointerId(0);
- if (mTouchState == TOUCH_STATE_SCROLLING) {
- enableChildrenCache(mCurrentScreen - 1, mCurrentScreen + 1);
- }
- break;
- case MotionEvent.ACTION_MOVE:
- if (mTouchState == TOUCH_STATE_SCROLLING) {
- // Scroll to follow the motion event
- final int pointerIndex = ev.findPointerIndex(mActivePointerId);
- final float x = ev.getX(pointerIndex);
- final float deltaX = mLastMotionX - x;
- mLastMotionX = x;
- if (deltaX < 0) {
- if (mTouchX > 0) {
- mTouchX += Math.max(-mTouchX, deltaX);
- mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
- invalidate();
- }
- } else if (deltaX > 0) {
- final float availableToScroll = getChildAt(getChildCount() - 1).getRight()
- - mTouchX - getWidth();
- if (availableToScroll > 0) {
- mTouchX += Math.min(availableToScroll, deltaX);
- mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
- invalidate();
- }
- } else {
- awakenScrollBars();
- }
- }
- break;
- case MotionEvent.ACTION_UP:
- if (mTouchState == TOUCH_STATE_SCROLLING) {
- final VelocityTracker velocityTracker = mVelocityTracker;
- velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
- final int velocityX = (int) velocityTracker.getXVelocity(mActivePointerId);
- final int screenWidth = getWidth();
- final int whichScreen = (mScrollX + (screenWidth / 2)) / screenWidth;
- final float scrolledPos = (float) mScrollX / screenWidth;
- mChangeMotionX = mLastMotionX;
- if (velocityX > SNAP_VELOCITY && mCurrentScreen > 0) {
- // Fling hard enough to move left.
- // Don't fling across more than one screen at a time.
- final int bound = scrolledPos < whichScreen ? mCurrentScreen - 1
- : mCurrentScreen;
- snapToScreen(Math.min(whichScreen, bound), velocityX, true);
- } else if (velocityX < -SNAP_VELOCITY && mCurrentScreen < getChildCount() - 1) {
- // Fling hard enough to move right
- // Don't fling across more than one screen at a time.
- final int bound = scrolledPos > whichScreen ? mCurrentScreen + 1
- : mCurrentScreen;
- snapToScreen(Math.max(whichScreen, bound), velocityX, true);
- } else {
- snapToScreen(whichScreen, 0, true);
- }
- if (mVelocityTracker != null) {
- mVelocityTracker.recycle();
- mVelocityTracker = null;
- }
- }
- mTouchState = TOUCH_STATE_REST;
- mActivePointerId = INVALID_POINTER;
- break;
- case MotionEvent.ACTION_CANCEL:
- mTouchState = TOUCH_STATE_REST;
- mActivePointerId = INVALID_POINTER;
- break;
- case MotionEvent.ACTION_POINTER_UP:
- onSecondaryPointerUp(ev);
- break;
- }
- return true;
- }
- //修改该方法主要目的是记录滑动的距离
- @Override
- public boolean onInterceptTouchEvent(MotionEvent ev) {
- final boolean workspaceLocked = mLauncher.isWorkspaceLocked();
- final boolean allAppsVisible = mLauncher.isAllAppsVisible();
- if (workspaceLocked || allAppsVisible) {
- return false; // We don't want the events. Let them fall through to
- // the all apps view.
- }
- /*
- * 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;
- }
- if (mVelocityTracker == null) {
- mVelocityTracker = VelocityTracker.obtain();
- }
- mVelocityTracker.addMovement(ev);
- 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.
- */
- /*
- * Locally do absolute value. mLastMotionX is set to the y value
- * of the down event.
- */
- final int pointerIndex = ev.findPointerIndex(mActivePointerId);
- final float x = ev.getX(pointerIndex);
- final float y = ev.getY(pointerIndex);
- 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;
- mLastMotionX = x;
- mTouchX = mScrollX;
- mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
- enableChildrenCache(mCurrentScreen - 1, mCurrentScreen + 1);
- }
- // 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: {
- final float x = ev.getX();
- final float y = ev.getY();
- // Remember location of down touch
- mLastMotionX = x;
- mChangeMotionX = x;
- mLastMotionY = y;
- mActivePointerId = ev.getPointerId(0);
- 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) {
- 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
- final int pointerIndex = ev.findPointerIndex(mActivePointerId);
- mWallpaperManager.sendWallpaperCommand(getWindowToken(),
- "android.wallpaper.tap",
- mTempCell[0] + (int) ev.getX(pointerIndex), mTempCell[1]
- + (int) ev.getY(pointerIndex), 0, null);
- }
- }
- // Release the drag
- clearChildrenCache();
- mTouchState = TOUCH_STATE_REST;
- mActivePointerId = INVALID_POINTER;
- mAllowLongPress = false;
- if (mVelocityTracker != null) {
- mVelocityTracker.recycle();
- mVelocityTracker = null;
- }
- break;
- case MotionEvent.ACTION_POINTER_UP:
- onSecondaryPointerUp(ev);
- break;
- }
- /*
- * The only time we want to intercept motion events is if we are in the
- * drag mode.
- */
- return mTouchState != TOUCH_STATE_REST;
- }
这些类的修改特别是变换时一定要注意canvas的save和restore方法,不清楚的先百度一下,不然很容易就变形了。下篇讨论使用openGL实现的方法