`
ishelf
  • 浏览: 106341 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

Launcher 桌面的3D转屏效果实现(1)-matrix setPolyToPoly

阅读更多

从现有方法来讲为了实现桌面的3D转屏效果主要是通过Launcher中的workspace实现(现有的有源码的方法),具体实现见:

     http://www.eoeandroid.com/viewthread.php?tid=27079&extra=&page=1 (写这篇文章也是为了“报答”该作者开源的贡献,共同学习)

 

     不过该方法存在以下几个问题:

  1. 不同机器的分辨率和内存大小不同,从而使用cache保持截图的方法很有可能会出现内存方面的错误
  2. 界面上面的变化,例如图标增加和删除,需要程序对应做出很多修改,用以保证整体效果的统一。其根本原因就是修改的模块位置在Launcher中过于考上
  3. 图标变形和覆盖(我在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实现的方法

4
2
分享到:
评论
14 楼 烧伤的火柴 2012-02-20  
ishelf 写道
setPara
烧伤的火柴 写道
setPara函数是做什么用的呢?

你查看一下1.6的Launcher源码,这个是在1.6上开发的,早了

谢谢大师指点
13 楼 ishelf 2012-02-13  
setPara
烧伤的火柴 写道
setPara函数是做什么用的呢?

你查看一下1.6的Launcher源码,这个是在1.6上开发的,早了
12 楼 烧伤的火柴 2012-02-09  
setPara函数是做什么用的呢?
11 楼 烧伤的火柴 2012-02-03  
手动拖才翻一下 根本不是真正
10 楼 jackshen118 2011-12-20  
setPara函数是做什么用的呢?
9 楼 cheng330301560 2011-12-19  
setPara方法没找到
8 楼 sadsnow19840713 2011-12-02  
对呀,有些变量没有定义和赋值,能贴得更全吗?
7 楼 ishelf 2011-11-30  
liu7se7en 写道
liu7se7en 写道
老大我有一个问题 就是在使用该代码实现了3D之后有个问题 就是在拖动的过程中 有时不一定出现3D效果 而且 整个屏幕发生位移

求老大指点啊



应该是你对canvas的操作不对,你写一个小demo测试一下,怎么平移和旋转。  尤其是旋转地中心点要确认一下在什么地方。   多打Log
6 楼 liu7se7en 2011-11-28  
liu7se7en 写道
老大我有一个问题 就是在使用该代码实现了3D之后有个问题 就是在拖动的过程中 有时不一定出现3D效果 而且 整个屏幕发生位移

求老大指点啊
5 楼 liu7se7en 2011-11-28  
老大我有一个问题 就是在使用该代码实现了3D之后有个问题 就是在拖动的过程中 有时不一定出现3D效果 而且 整个屏幕发生位移
4 楼 zgcypjhf 2011-11-24  
请问startRotate(canvas, currentX, canvas.getWidth(), canvas.getHeight());
if (isCurrentScrenn && xCor < 0) { 
            xCor = width + xCor; 
            flag = false; 
        }
变量currentX 和 isCurrentScrenn都没定义, 想请问一下是在哪定义并给它赋值的,谢谢了!
3 楼 anita315 2011-11-02  
很有幫助 ! 感謝您的提供 . 
2 楼 ishelf 2011-08-02  
zhmeup 写道
假的,手动拖才翻一下 根本不是真正的3D

这个哥们很给力,顶你一个。
的确要做真正的3D就需要用opengl来做(GLSurfaceView),市面上有一个叫SPB的Launcher,这个是真正的3D,代价是要完全实现一套widget机制,也就是重写Launcher,你可以试试。
1 楼 zhmeup 2011-08-01  
假的,手动拖才翻一下 根本不是真正的3D

相关推荐

Global site tag (gtag.js) - Google Analytics