编译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进行安装了。
注意:安装最好找一个原生的系统调试,第三方改过的系统,会把launcher请求的一些原生信息改了,这样会导致launcher报错。比如我就遇到过这个问题:
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事件中调用,也就是说,当手势操作了过后,就调用这个方法来更新壁纸的偏移量,这样就达到了壁纸滚动的效果。
完了,Launcher的获取壁纸的逻辑就清晰了,首先配置Activity为透明,并配置Activity的后面显示桌面背景(android:windowShowWallpaper = true的作用,这并不是在后面显示桌面哦,是配置activity的背景为桌面背景),当Activity的控件滑动的时候,调用WallpaperManager平移壁纸,就完成壁纸的滚动。
有一哥们,感觉Launcher的刷动效果很不错,于是把这一块抠了出来,抠得很漂亮,并且加上了桌面背景效果,下载戳这https://github.com/sevenler/ScrollLayout_From_Launcher.git
这个哥们也写了相关Launcher的几篇博客:http://blog.csdn.net/yao_guet/article/details/6572739
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分析相关的一些博客
http://www.cnblogs.com/Hwangroid/archive/2011/12/08/2281286.html
http://blog.csdn.net/aomandeshangxiao/article/category/888680
http://blog.csdn.net/stonecao/article/details/6536083