一、增加墙纸
图片放入/packages/apps/Launcher2/res/drawable-mdpi
/packages/apps/Launcher2/res/values-mdpi/wallpapers.xml
- <resources>
- <string-array name="wallpapers" translatable="false">
- <item>wallpaper_lake</item>
- <item>wallpaper_sunset</item>
- <item>wallpaper_beach</item>
- <item>wallpaper_snow_leopard</item>
- <item>wallpaper_path</item>
- <item>wallpaper_sunrise</item>
- <item>wallpaper_mountain</item>
- <item>wallpaper_road</item>
- <item>wallpaper_jellyfish</item>
- <item>wallpaper_zanzibar</item>
- <item>wallpaper_blue</item>
- <item>wallpaper_grey</item>
- <item>wallpaper_green</item>
- <item>wallpaper_pink</item>
- </string-array>
- </resources>
设置默认墙纸
frameworks\base\core\res\res\drawable中default_wallpaper.jpg这个是默认的墙纸
二、源代码中设置wallpaper
在launcher.java中
- public boolean onCreateOptionsMenu(Menu menu) {
- if (isWorkspaceLocked()) {
- return false;
- }
- super.onCreateOptionsMenu(menu);
- menu.add(MENU_GROUP_ADD, MENU_ADD, 0, R.string.menu_add)
- .setIcon(android.R.drawable.ic_menu_add)
- .setAlphabeticShortcut('A');
- menu.add(0, MENU_MANAGE_APPS, 0, R.string.menu_manage_apps)
- .setIcon(android.R.drawable.ic_menu_manage)
- .setAlphabeticShortcut('M');
- menu.add(MENU_GROUP_WALLPAPER, MENU_WALLPAPER_SETTINGS, 0, R.string.menu_wallpaper)
- .setIcon(android.R.drawable.ic_menu_gallery)
- .setAlphabeticShortcut('W');
- … …
点击wallpaper后会调用
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case MENU_ADD:
- addItems();
- return true;
- case MENU_MANAGE_APPS:
- manageApps();
- return true;
- case MENU_WALLPAPER_SETTINGS:
- startWallpaper();
- return true;
- case MENU_SEARCH:
- onSearchRequested();
- return true;
- case MENU_NOTIFICATIONS:
- showNotifications();
- return true;
- }
- return super.onOptionsItemSelected(item);
- }
将进入
- private void startWallpaper() {
- closeAllApps(true);
- final Intent pickWallpaper = new Intent(Intent.ACTION_SET_WALLPAPER);
- Intent chooser = Intent.createChooser(pickWallpaper,
- getText(R.string.chooser_wallpaper));
- startActivityForResult(chooser, REQUEST_PICK_WALLPAPER);
- }
三、launcher中launcher的搜索框和ProtipWidget(widget修改)
Launcher2/res/xml/default_workspace.xml
- <!-- Middle screen [2] <search>为添加google搜索框;-->
- <search
- launcher:screen="2"
- launcher:x="0"
- launcher:y="0" />
- !-- <appwidget>为添加相应的widget; -->
- <appwidget
- launcher:packageName="com.android.protips"
- launcher:className="com.android.protips.ProtipWidget"
- launcher:screen="2"
- launcher:x="0"
- launcher:y="1"
- launcher:spanX="4"
- launcher:spanY="1" />
此xml解析在Launcherprovider.java文件loadFavorites方法中
四、launcher屏数和默认屏
在launcher.java的Launcher类中有
static final int SCREEN_COUNT = 5; //屏数
static final int DEFAULT_SCREEN = 2; //默认显示的第几屏(从0屏开始)。
修改屏数还需要修改
Launcher2/res/layout-port/Launcher.xml
- <!-- The workspace contains 3 screens of cells -->
- <com.android.launcher2.Workspace
- android:id="@+id/workspace"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:scrollbars="horizontal"
- android:fadeScrollbars="true"
- launcher:defaultScreen="2">
- <include android:id="@+id/cell1" layout="@layout/workspace_screen" />
- <include android:id="@+id/cell2" layout="@layout/workspace_screen" />
- <include android:id="@+id/cell3" layout="@layout/workspace_screen" />
- <include android:id="@+id/cell4" layout="@layout/workspace_screen" />
- <include android:id="@+id/cell5" layout="@layout/workspace_screen" />
- </com.android.launcher2.Workspace>
修改默认屏同时需要修改workspace.java中
- public Workspace(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- … …
- mDefaultScreen = a.getInt(R.styleable.Workspace_defaultScreen, 1);
- a.recycle();
- setHapticFeedbackEnabled(false);
- initWorkspace();
五、页面标记实现原理
Launcher2/res/drawable/ home_arrows_left.xml
- <selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_pressed="true" android:drawable="@drawable/all_apps_button_pressed" />
- <item android:state_focused="true" android:state_window_focused="true" android:drawable="@drawable/all_apps_button_focused" />
- <item android:state_focused="true" android:state_window_focused="false" android:drawable="@drawable/all_apps_button_normal" />
- <item android:drawable="@drawable/all_apps_button_normal" />
- </selector>
Launcher2/res/layout-port/Launcher.xml
- <ImageView
- android:id="@+id/previous_screen"
- android:layout_width="93dip"
- android:layout_height="@dimen/button_bar_height"
- android:layout_gravity="bottom|left"
- android:layout_marginLeft="6dip"
- android:scaleType="center"
- android:src="@drawable/home_arrows_left"
- android:onClick="previousScreen" <!—点击事件处理-->
- android:focusable="true"
- android:clickable="true" />
在launcher.java中实现
- private void setupViews() {
- … …
- mPreviousView = (ImageView) dragLayer.findViewById(R.id.previous_screen);
- mNextView = (ImageView) dragLayer.findViewById(R.id.next_screen);
- Drawable previous = mPreviousView.getDrawable();
- Drawable next = mNextView.getDrawable();
- mWorkspace.setIndicators(previous, next);
- … …
- }
获取这个两旁的图片设置到mCurrentScreen屏幕上
Workspace.java
- void setIndicators(Drawable previous, Drawable next) {
- mPreviousIndicator = previous;
- mNextIndicator = next;
- previous.setLevel(mCurrentScreen);
- next.setLevel(mCurrentScreen);
在launcher.xml中定义
android:onClick="previousScreen" <!—点击事件处理-->
在launcher.java中的处理为
- @SuppressWarnings({"UnusedDeclaration"})
- public void previousScreen(View v) {
- if (!isAllAppsVisible()) {
- mWorkspace.scrollLeft();
- }
- }
- @SuppressWarnings({"UnusedDeclaration"})
- public void nextScreen(View v) {
- if (!isAllAppsVisible()) {
- mWorkspace.scrollRight();
- }
- }
进入到workspace中
- public void scrollLeft() {
- clearVacantCache();
- if (mScroller.isFinished()) {
- if (mCurrentScreen > 0) snapToScreen(mCurrentScreen - 1);
- } else {
- if (mNextScreen > 0) snapToScreen(mNextScreen - 1);
- }
- }
- public void scrollRight() {
- clearVacantCache();
- if (mScroller.isFinished()) {
- if (mCurrentScreen < getChildCount() -1) snapToScreen(mCurrentScreen + 1);
- } else {
- if (mNextScreen < getChildCount() -1) snapToScreen(mNextScreen + 1);
- }
- }
先判断滑动是否完成,然后跳转到目标屏。
- void snapToScreen(int whichScreen) {
- snapToScreen(whichScreen, 0, false);
- }
- private void snapToScreen(int whichScreen, int velocity, boolean settle) {
- //if (!mScroller.isFinished()) return;
- whichScreen = Math.max(0, Math.min(whichScreen, getChildCount() - 1));
- clearVacantCache();
- enableChildrenCache(mCurrentScreen, whichScreen);
- mNextScreen = whichScreen;
- mPreviousIndicator.setLevel(mNextScreen);
- mNextIndicator.setLevel(mNextScreen);
- View focusedChild = getFocusedChild();
- if (focusedChild != null && whichScreen != mCurrentScreen &&
- focusedChild == getChildAt(mCurrentScreen)) {
- focusedChild.clearFocus();
- }
- final int screenDelta = Math.max(1, Math.abs(whichScreen - mCurrentScreen));
- final int newX = whichScreen * getWidth();
- final int delta = newX - mScrollX;
- int duration = (screenDelta + 1) * 100;
- if (!mScroller.isFinished()) {
- mScroller.abortAnimation();
- }
- if (settle) {
- mScrollInterpolator.setDistance(screenDelta);
- } else {
- mScrollInterpolator.disableSettle();
- }
- velocity = Math.abs(velocity);
- if (velocity > 0) {
- duration += (duration / (velocity / BASELINE_FLING_VELOCITY))
- * FLING_VELOCITY_INFLUENCE;
- } else {
- duration += 100;
- }
- awakenScrollBars(duration);
- mScroller.startScroll(mScrollX, 0, delta, 0, duration);
- invalidate();
- }
六、左右滑动切换屏幕
在workspace中实现屏幕切换主要重写了以下几个方法:onMeasure()、onLayout()、onInterceptTouchEvent()、onTouchEvent()方法。
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- final int width = MeasureSpec.getSize(widthMeasureSpec);
- final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
- if (widthMode != MeasureSpec.EXACTLY) {
- throw new IllegalStateException("Workspace can only be used in EXACTLY mode.");
- }
- final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
- if (heightMode != MeasureSpec.EXACTLY) {
- throw new IllegalStateException("Workspace can only be used in EXACTLY mode.");
- }
- // The children are given the same width and height as the workspace
- final int count = getChildCount();
- for (int i = 0; i < count; i++) {
- getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec);
- }
- if (mFirstLayout) {
- setHorizontalScrollBarEnabled(false);
- scrollTo(mCurrentScreen * width, 0);
- setHorizontalScrollBarEnabled(true);
- updateWallpaperOffset(width * (getChildCount() - 1));
- mFirstLayout = false;
- }
- }
onInterceptTouchEvent()方法和onTouchEvent()主要是来响应手指按下划动时所需要捕获的消息,例如划动的速度,划动的距离等。当手指起来时,根据划动的速度与跨度来判断是向左滑动一页还是向右滑动一页,确保每次用户操作结束之后显示的都是整体的一个子view.
在View.java中framework\base\core\java\android\view
将当前视图内容偏移至(x , y)坐标处,即显示(可视)区域位于(x , y)坐标
- public void scrollTo(int x, int y) {
- if (mScrollX != x || mScrollY != y) {
- int oldX = mScrollX;
- int oldY = mScrollY;
- mScrollX = x;
- mScrollY = y;
- onScrollChanged(mScrollX, mScrollY, oldX, oldY);
- if (!awakenScrollBars()) {
- invalidate();
- }
- }
- }
在当前视图内容继续偏移(x , y)个单位,显示(可视)区域也跟着偏移(x,y)个单位
- public void scrollBy(int x, int y) {
- scrollTo(mScrollX + x, mScrollY + y);
- }
七、获取应用列表(相当于mainmenu)
在launcher.java中
Launcher 布局文件初始化方法中
- private void setupViews() {
- … …
- mHandleView = (HandleView) findViewById(R.id.all_apps_button);
- mHandleView.setLauncher(this);
- mHandleView.setOnClickListener(this);
- mHandleView.setOnLongClickListener(this);
- … …
- }
点击中间button
- public void onClick(View v) {
- Object tag = v.getTag();
- if (tag instanceof ShortcutInfo) {
- …
- } else if (tag instanceof FolderInfo) {
- handleFolderClick((FolderInfo) tag);
- } else if (v == mHandleView) {
- if (isAllAppsVisible()) {
- closeAllApps(true);
- } else {
- showAllApps(true);
- }
- }
进入到showAllApps()方法
- void showAllApps(boolean animated) {
- mAllAppsGrid.zoom(1.0f, animated);
- ((View) mAllAppsGrid).setFocusable(true);
- ((View) mAllAppsGrid).requestFocus();
- // TODO: fade these two too
- mDeleteZone.setVisibility(View.GONE);
- }
在上面使用到了mAllAppsGrid
privateAllAppsView mAllAppsGrid; //声明
在setupViews()方法中
- private void setupViews() {
- … …
- mAllAppsGrid = (AllAppsView)dragLayer.findViewById(R.id.all_apps_view);
- mAllAppsGrid.setLauncher(this);
- mAllAppsGrid.setDragController(dragController);
- ((View) mAllAppsGrid).setWillNotDraw(false); // We don't want a hole punched in our window.
- // Manage focusability manually since this thing is always visible
- ((View) mAllAppsGrid).setFocusable(false);
- … …
- }
all_apps_view是在all_app_2d.xml中定义
位于res/layout-port中
故找到AllApps2D.java可以找到addApps,removeApps,zoom这几个方法。
- public void zoom(float zoom, boolean animate) {
- Log.d(TAG, "zooming " + ((zoom == 1.0) ? "open" : "closed"));
- cancelLongPress();
- mZoom = zoom;
- if (isVisible()) {
- getParent().bringChildToFront(this);
- setVisibility(View.VISIBLE);
- mGrid.setAdapter(mAppsAdapter);
- if (animate) {
- startAnimation(AnimationUtils.loadAnimation(getContext(), R.anim.all_apps_2d_fade_in));
- } else {
- onAnimationEnd();
- }
- } else {
- if (animate) {
- startAnimation(AnimationUtils.loadAnimation(getContext(), R.anim.all_apps_2d_fade_out));
- } else {
- onAnimationEnd();
- }
- }
- }
在zoom()方法里面有mGrid.setAdapter(mAppsAdapter),在构造方法中,给adapter已经赋值。
- public AllApps2D(Context context, AttributeSet attrs) {
- super(context, attrs);
- setVisibility(View.GONE);
- setSoundEffectsEnabled(false);
- mAppsAdapter = new AppsAdapter(getContext(), mAllAppsList);
- mAppsAdapter.setNotifyOnChange(false);
- }
在launcher.java函数中
- private void loadHotseats() {
- … …
- PackageManager pm = getPackageManager();
- … …
- ResolveInfo bestMatch = pm.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY);
- List<ResolveInfo> allMatches = pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
- … …
- }
如此获取了应用列表