通过Launcher里的WorkSpace完成桌面的3D转屏效果(有源码)

 
本帖最后由 ryan2010 于 2010-11-25 14:42 编辑
http://www.eoeandroid.com/thread-27079-1-1.html
大家复制起来研究太麻烦,要我上传附件,已经上传了,附件里面的改动比较大而且注释不那么详细了,
出于多效率的考虑,没有用多线程了。下面提到的问题也都已解决,有什么好的建议大家再提出来一起讨论。



这个workspace可以看做一个整体,X坐标上的范围是0-960
它共有3页,每个页即为一个child,每个child里面都有很多单元格,单元格中可以放置shortcut,
active folder, widget,他们分别会横向纵向占用不同的单元格数。
通过使用ScrollTo(int,int)这个方法可以把workspace这个整体移动,来显示不同的页面
比如:
ScrollTo(0,0) 为第一页
ScrollTo(320,0) 为第二页
ScrollTo(640,0)  为第三页
如果有更多页
ScrollTo(640,0) 为第四页
ScrollTo(960,0)  为第五页
.
.
.
其实页面上的被添加了组件后,这些组件我们看不见,
但是你点击某些看不见的“空位置”也是可以进入。
比如我们在中间页的左上角添加了一个短信的shortcut,如果不调用
drawChild(canvas, getChildAt(1), drawingTime)或者绘制出来我们是
看不见这个icon的,但是点击屏幕左上角却可以进入短信。
流程:
1.得到3个页的bitmap,页面上一有变化就重新得到bitmap
2.拖动屏幕时让屏幕不滑动也不按原始方式换页,而进行相应
的两个bitmap的变换操作来达到3D效果(不用GL,这样可以更有效率)
3.通过拖动的起始和结束坐标判断是否换页,应该如何换页
4.由于使用drawChild()把icon画到页上的时候,在进行2个bitmap的变换达到3D效果时
图像会出现从叠,故没有使用它,而是使用Canvas.drawBitmap()把相应bitmap张贴到
相应的页上,这样可以我们就可以知道屏幕的何处有何应用图标了
背景还没做好,准备搞个全黑,转的时候的3个Bitmap的背景搞成桌面背景,这样用户
还可以更换,3D效果有了,但需要改善

希望大家把自己的研究成果都分享出来,大家共同学习,共同进步
下面修改过的代码基都加以注释
*/
package com.android.launcher;
import java.util.ArrayList;
import android.app.WallpaperManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Region;
import android.graphics.drawable.Drawable;
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 android.widget.TextView;
public class Workspace extends ViewGroup implements DropTarget, DragSource, DragScroller,Runnable {
    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 final WallpaperManager mWallpaperManager;
    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 int[] mTargetCell = null;
    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 OnLongClickListener mLongClickListener;
    private Launcher mLauncher;
    private DragController mDragger;
   
    /**
     * Cache of vacant cells, used during drag events and invalidated as needed.
     */
    private CellLayout.CellInfo mVacantCache = null;
   
    private int[] mTempCell = new int[2];
    private int[] mTempEstimate = new int[2];
    private boolean mAllowLongPress;
    private boolean mLocked;
    private int mTouchSlop;
    private int mMaximumVelocity;
    final Rect mDrawerBounds = new Rect();
    final Rect mClipBounds = new Rect();
    int mDrawerContentHeight;
    int mDrawerContentWidth;
    //有3页,因此定义三个Bitmap用作3次截屏
private Bitmap[] s=new Bitmap[3];
//定义图片的大小
private float width=280;
private float height=380;
//定义矩阵和矩阵被绘制的方式,矩阵被用来做变换来让2张图片达到3D效果
private Paint   mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    private Matrix  mMatrix = new Matrix();
    //用作绘制完成3D的参数
private float key=0.0f;
//当前手在屏幕上的X坐标值,通过它判断2张图片要如何绘制
private float currentX=0;
//判断拖动屏幕时的拖动方向,大于0为向右拖,小于0为向左拖
private float mdirection=0;
//当拖动屏幕时,手刚接触到屏幕时的坐标,可以用来计算方向和跨度
private float startDragCoor=0;
//用来得到startDragCoor时做判定
boolean flag=true;
    public Workspace(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
  //设为true,因为下面要截图操作
  setDrawingCacheEnabled(true);
  setDrawingCacheQuality(DRAWING_CACHE_QUALITY_HIGH);
    }
    public Workspace(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        mWallpaperManager = WallpaperManager.getInstance(context);
        
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Workspace, defStyle, 0);
      
        //刚开机时 mDefaultScreen==1
  mDefaultScreen = a.getInt(R.styleable.Workspace_defaultScreen, 1);
        a.recycle();
  
  //设为true,因为下面要截图操作
  setDrawingCacheEnabled(true);
        
  initWorkspace();
  
  //新开一个线程,在拖动屏幕时更新 currentX 坐标并刷新屏幕,让2张图片变换达到3D效果
  new Thread(this).start();
    }
    private void initWorkspace() {
        mScroller = new Scroller(getContext());
        mCurrentScreen = mDefaultScreen;
  Launcher.setScreen(mCurrentScreen);
        final ViewConfiguration configuration = ViewConfiguration.get(getContext());
        mTouchSlop = configuration.getScaledTouchSlop();
        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) {
        super.addView(child);
    }
    @Override
    public void addView(View child, int index) {
        super.addView(child, index);
    }
    @Override
    public void addView(View child, int width, int height) {
        super.addView(child, width, height);
    }
    @Override
    public void addView(View child, LayoutParams params) {
        super.addView(child, params);
    }
    Folder getOpenFolder() {
        CellLayout currentScreen = (CellLayout) getChildAt(mCurrentScreen);
        int count = currentScreen.getChildCount();
        for (int i = 0; i < count; i++) {
            View child = currentScreen.getChildAt(i);
            CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
            if (lp.cellHSpan == 4 && lp.cellVSpan == 4 && child instanceof Folder) {
                return (Folder) child;
            }
        }
        return null;
    }
    ArrayList<Folder> getOpenFolders() {
        final int screens = getChildCount();
        ArrayList<Folder> folders = new ArrayList<Folder>(screens);
        for (int screen = 0; screen < screens; screen++) {
            CellLayout currentScreen = (CellLayout) getChildAt(screen);
            int count = currentScreen.getChildCount();
            for (int i = 0; i < count; i++) {
                View child = currentScreen.getChildAt(i);
                CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
                if (lp.cellHSpan == 4 && lp.cellVSpan == 4 && child instanceof Folder) {
                    folders.add((Folder) child);
                    break;
                }
            }
        }
        return folders;
    }
    //查看当前的页面是否为默认页面,及为中间的那个(共三个页面)
    boolean isDefaultScreenShowing() {
        return mCurrentScreen == mDefaultScreen;
    }
    //可查看当前是哪个页,0,1, 2
    int getCurrentScreen() {
        return mCurrentScreen;
    }
    //通过scrollTo(int,int)这个方法来移动动屏幕,让它到达某个页面
//第一个参数为X坐标,第二个参数为Y坐标,由于有3页,故X范围是0 - 320*3
//如果把参数设置成(0,0)则当前为第一页,(320,0)为第二页,(640,0)为第三页
    void setCurrentScreen(int currentScreen) {
        clearVacantCache();
        mCurrentScreen = Math.max(0, Math.min(currentScreen, getChildCount() - 1));
        scrollTo(mCurrentScreen * getWidth(), 0);
        Log.d("Launcer D","Here we go");
        invalidate();
    }
    void addInCurrentScreen(View child, int x, int y, int spanX, int spanY) {
        addInScreen(child, mCurrentScreen, x, y, spanX, spanY, false);
    }
    void addInCurrentScreen(View child, int x, int y, int spanX, int spanY, boolean insert) {
        addInScreen(child, mCurrentScreen, x, y, spanX, spanY, insert);
    }
    void addInScreen(View child, int screen, int x, int y, int spanX, int spanY) {
        addInScreen(child, screen, x, y, spanX, spanY, false);
    }
    void addInScreen(View child, int screen, int x, int y, int spanX, int spanY, boolean insert) {
        if (screen < 0 || screen >= getChildCount()) {
            throw new IllegalStateException("The screen must be >= 0 and < " + getChildCount());
        }
        clearVacantCache();
        final CellLayout group = (CellLayout) getChildAt(screen);
        CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
        if (lp == null) {
            lp = new CellLayout.LayoutParams(x, y, spanX, spanY);
        } else {
            lp.cellX = x;
            lp.cellY = y;
            lp.cellHSpan = spanX;
            lp.cellVSpan = spanY;
        }
        group.addView(child, insert ? 0 : -1, lp);
        if (!(child instanceof Folder)) {
            child.setOnLongClickListener(mLongClickListener);
        }
    }
   
    void addWidget(View view, Widget widget, boolean insert) {
        addInScreen(view, widget.screen, widget.cellX, widget.cellY, widget.spanX,
                widget.spanY, insert);
    }
    CellLayout.CellInfo findAllVacantCells(boolean[] occupied) {
        CellLayout group = (CellLayout) getChildAt(mCurrentScreen);
        if (group != null) {
            return group.findAllVacantCells(occupied, null);
        }
        return null;
    }
   
    CellLayout.CellInfo findAllVacantCellsFromModel() {
        CellLayout group = (CellLayout) getChildAt(mCurrentScreen);
        if (group != null) {
            int countX = group.getCountX();
            int countY = group.getCountY();
            boolean occupied[][] = new boolean[countX][countY];
            Launcher.getModel().findAllOccupiedCells(occupied, countX, countY, mCurrentScreen);
            return group.findAllVacantCellsFromOccupied(occupied, countX, countY);
        }
        return null;
    }
   
    private void clearVacantCache() {
        if (mVacantCache != null) {
            mVacantCache.clearVacantCells();
            mVacantCache = null;
        }
    }
    @Override
    public void setOnLongClickListener(OnLongClickListener l) {
        mLongClickListener = l;
        final int count = getChildCount();
        for (int i = 0; i < count; i++) {
            getChildAt(i).setOnLongClickListener(l);
        }
    }
    private void updateWallpaperOffset() {
        updateWallpaperOffset(getChildAt(getChildCount() - 1).getRight() - (mRight - mLeft));
    }
    //当我们移动屏幕时,壁纸也会移动,但前提是壁纸一定要比页面大小大
    private void updateWallpaperOffset(int scrollRange) {
        mWallpaperManager.setWallpaperOffsetSteps(1.0f / (getChildCount() - 1), 0 );
        
  //下面这个方法带两参数X Y, X should be 0.0-1.0,if there are 3 screens
        // X can be 0.0 0.5 1.0, to represents the 3 screens
        mWallpaperManager.setWallpaperOffsets(getWindowToken(), mScrollX / (float) scrollRange, 0);
    }

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值