Launcher2 分析

編譯Launcher2

這是我已經編輯事後的代碼: https://github.com/sevenler/Android_Launcher_Custom.git

我作了一些修改,讓其能夠單獨進行編譯。 
修改包括:
 1.修改包名,修改包名後安裝纔不會和系統的衝突
 2.導入編譯依賴包。 
  1)framework_intermediates/classes.jar (android的框架類) 
  2)android-common_intermediates/classes.jar (包含com.android.common.Search這個類)
  3)core_intermediates/classes.jar (包含dalvik.system.VMRuntime這個類) 

這三個包放在根目錄的sys_lib文件夾中,編譯時將三個包,做爲user library引用到項目中來。
能夠經過配置工程的Build Path來加入, 右鍵工程名稱而後選擇
Build Path -> Configure Build Path...->Libraries->Add Library->User Library->User Libraries...>New... 
而後將上面3個依賴的包一個個的加入進來,分別命名爲android_framework,android_common,android_core.
進入Order and Export ,調節這3個包的排序,到 Android 包和 Android Dependices包 的前面。

到此,就能編譯Launcher進行安裝了。 html

注意:安裝最好找一個原生的系統調試,第三方改過的系統,會把launcher請求的一些原生信息改了,這樣會致使launcher報錯。好比我就遇到過這個問題: java

final Cursor c = contentResolver.query(LauncherSettings.Favorites.CONTENT_URI, null, null, null, null);//這個地飯去請求launcher的默認配置,在huawei的系統上面請求這個,獲得的c是null


1.壁紙顯示

壁紙的顯示有2個重要的地方

  1. activity的配置,並且這個配置又有2套方案

     1).AndroidManifest.xml文件裏面activity屬性裏面添加 android:theme="@android:style/Theme.Translucent",而且在Activity的onCreate中添加getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER); 這樣獲得的activity就有了桌面背景。
     2).activity配置 android:theme="@style/Theme" theme爲下面的
<resources>
   <style name="Theme">
<item name="android:windowNoTitle">true</item>
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:colorBackgroundCacheHint">@null</item>
<item name="android:windowShowWallpaper">true</item>
   </style>
</resources>
  這兩套方案實際是同樣的,都達到在Activity後面顯示桌面背景的效果。

  2.頁面滑動的同時,滑動桌面背景。

    主要是經過這2個方法來控制的:
    public void setWallpaperOffsetSteps (float xStep, float yStep)
     public void setWallpaperOffsets (IBinder windowToken, float xOffset, float yOffset)

具體調用看Workspace中的
private void updateWallpaperOffset(int scrollRange) {
    IBinder token = getWindowToken();
    if (token != null) {
        mWallpaperManager.setWallpaperOffsetSteps(1.0f / (getChildCount() - 1), 0 );
        mWallpaperManager.setWallpaperOffsets(getWindowToken(),
                Math.max(0.f, Math.min(mScrollX/(float)scrollRange, 1.f)), 0);
    }
}

  這個方法會在onTouchEvent事件中調用,也就是說,當手勢操做了事後,就調用這個方法來更新壁紙的偏移量,這樣就達到了壁紙滾動的效果。 android

  完了,Launcher的獲取壁紙的邏輯就清晰了,首先配置Activity爲透明,並配置Activity的後面顯示桌面背景(android:windowShowWallpaper = true的做用,這並非在後面顯示桌面哦,是配置activity的背景爲桌面背景),當Activity的控件滑動的時候,調用WallpaperManager平移壁紙,就完成壁紙的滾動。 git

有一哥們,感受Launcher的刷動效果很不錯,因而把這一塊摳了出來,得很漂亮,而且加上了桌面背景效果,下載戳這https://github.com/sevenler/ScrollLayout_From_Launcher.git github

這個哥們也寫了相關Launcher的幾篇博客:http://blog.csdn.net/yao_guet/article/details/6572739 app


2.桌面滑動分析

  桌面的滑動是桌面提供的一個重要操做,滑動是由Workspace提供,繼承於ViewGroup,和ViewPager很相識,內部填充的是CellLayout,5個桌面頁就有5個CellLayout。
  Workspace中幾個重要的代碼以下:

   //計算每一個子CellLayout的佈局大小  框架

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
	// .....
	// The children are given the same width and height as the scrollLayout
	final int count = getChildCount();
	for (int i = 0; i < count; i++) {
		getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec);
	}
	scrollTo(mCurScreen * width, 0);
}

//將CellLayout順序地佈局起來 佈局

protected void onLayout(boolean changed, int l, int t, int r, int b) {
	//.....
	int childLeft = 0;
	final int childCount = getChildCount();
	
	for (int i=0; i<childCount; i++) {
		final View childView = getChildAt(i);
		if (childView.getVisibility() != View.GONE) {
			final int childWidth = childView.getMeasuredWidth();
			childView.layout(childLeft, 0, 
					childLeft+childWidth, childView.getMeasuredHeight());
			childLeft += childWidth;
		}
	}
	updateWallpaperOffset();
}
    佈局完成以後,重點來了,監聽手勢來滑動。代碼集中在下面兩個方法中:
public boolean onInterceptTouchEvent(MotionEvent ev) {
		
		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:
			final int xDiff = (int)Math.abs(mLastMotionX-x);
			if (xDiff>mTouchSlop) {
				mTouchState = TOUCH_STATE_SCROLLING;
			}
			break;
		//判斷Scroller滾動狀態設置滾動狀態
		case MotionEvent.ACTION_DOWN:
			mLastMotionX = x;
			mLastMotionY = y;
			mTouchState = mScroller.isFinished()? TOUCH_STATE_REST : TOUCH_STATE_SCROLLING;
			break;
			
		case MotionEvent.ACTION_CANCEL:
		case MotionEvent.ACTION_UP:
			//依然置滾動狀態
			mTouchState = TOUCH_STATE_REST;
			break;
		}
		
		return mTouchState != TOUCH_STATE_REST;
	}

//接着是處理手勢
public boolean onTouchEvent(MotionEvent event) {
	// TODO Auto-generated method stub
	
	//用於計算手勢的滑動偏移量
	if (mVelocityTracker == null) {
		mVelocityTracker = VelocityTracker.obtain();
	}
	mVelocityTracker.addMovement(event);
	
	final int action = event.getAction();
	final float x = event.getX();
	final float y = event.getY();
	
	switch (action) {
	.....
	case MotionEvent.ACTION_MOVE://使用scroller來滾動
		int deltaX = (int)(mLastMotionX - x);
		mLastMotionX = x;
        	scrollBy(deltaX, 0);
        	updateWallpaperOffset();
		break;
		
	case MotionEvent.ACTION_UP://根據手勢滑動的偏移量來肯定是滾動到下一頁仍是上一頁,或者是滾動回當前頁
		Log.e(TAG, "event : up");   
		final VelocityTracker velocityTracker = mVelocityTracker;   
		velocityTracker.computeCurrentVelocity(1000);   
		int velocityX = (int) velocityTracker.getXVelocity();   
		Log.e(TAG, "velocityX:"+velocityX); 
		
		if (velocityX > SNAP_VELOCITY && mCurScreen > 0) {   
		    // Fling enough to move left   
		    Log.e(TAG, "snap left");
		    snapToScreen(mCurScreen - 1);   
		} else if (velocityX < -SNAP_VELOCITY   
		        && mCurScreen < getChildCount() - 1) {   
		    // Fling enough to move right   
		    Log.e(TAG, "snap right");
		    snapToScreen(mCurScreen + 1);   
		} else {   
		    snapToDestination();   
		}   
		if (mVelocityTracker != null) {
		    mVelocityTracker.recycle();
		    mVelocityTracker = null;   
		}   
        mTouchState = TOUCH_STATE_REST;   
		break;
	case MotionEvent.ACTION_CANCEL:
		mTouchState = TOUCH_STATE_REST;
		break;
	}
	
	return true;
}


3.應用列表

//使用下面這幾句話讀取應用列表
final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
final PackageManager packageManager = mContext.getPackageManager();
List<ResolveInfo> apps = packageManager.queryIntentActivities(mainIntent, 0);
//將應用列表排序
Collections.sort(apps, new ResolveInfo.DisplayNameComparator(packageManager));


Launcher分析相關的一些博客 ui

http://www.cnblogs.com/Hwangroid/archive/2011/12/08/2281286.html spa

http://blog.csdn.net/aomandeshangxiao/article/category/888680

http://blog.csdn.net/stonecao/article/details/6536083

相關文章
相關標籤/搜索