Launcher知識點整理(三)

一、增加牆紙

圖片放入/packages/apps/Launcher2/res/drawable-mdpi

/packages/apps/Launcher2/res/values-mdpi/wallpapers.xml 

  1. <resources>  
  2.     <string-array name="wallpapers" translatable="false">  
  3.         <item>wallpaper_lake</item>  
  4.         <item>wallpaper_sunset</item>  
  5.         <item>wallpaper_beach</item>  
  6.         <item>wallpaper_snow_leopard</item>  
  7.         <item>wallpaper_path</item>  
  8.         <item>wallpaper_sunrise</item>  
  9.         <item>wallpaper_mountain</item>  
  10.         <item>wallpaper_road</item>  
  11.         <item>wallpaper_jellyfish</item>  
  12.         <item>wallpaper_zanzibar</item>  
  13.         <item>wallpaper_blue</item>  
  14.         <item>wallpaper_grey</item>  
  15.         <item>wallpaper_green</item>  
  16.         <item>wallpaper_pink</item>  
  17.     </string-array>  
  18. </resources>  

設置默認牆紙

frameworks\base\core\res\res\drawable中default_wallpaper.jpg這個是默認的牆紙

二、源代碼中設置wallpaper

在launcher.java中

  1. public boolean onCreateOptionsMenu(Menu menu) {  
  2.     if (isWorkspaceLocked()) {  
  3.         return false;  
  4.     }  
  5.     super.onCreateOptionsMenu(menu);  
  6.   
  7.     menu.add(MENU_GROUP_ADD, MENU_ADD, 0, R.string.menu_add)  
  8.             .setIcon(android.R.drawable.ic_menu_add)  
  9.             .setAlphabeticShortcut('A');  
  10.     menu.add(0, MENU_MANAGE_APPS, 0, R.string.menu_manage_apps)  
  11.             .setIcon(android.R.drawable.ic_menu_manage)  
  12.             .setAlphabeticShortcut('M');  
  13.     menu.add(MENU_GROUP_WALLPAPER, MENU_WALLPAPER_SETTINGS, 0, R.string.menu_wallpaper)  
  14.              .setIcon(android.R.drawable.ic_menu_gallery)  
  15.              .setAlphabeticShortcut('W');  
  16.  … …  

點擊wallpaper後會調用

  1. public boolean onOptionsItemSelected(MenuItem item) {  
  2.        switch (item.getItemId()) {  
  3.            case MENU_ADD:  
  4.                addItems();  
  5.                return true;  
  6.            case MENU_MANAGE_APPS:  
  7.                manageApps();  
  8.                return true;  
  9.            case MENU_WALLPAPER_SETTINGS:  
  10.                startWallpaper();  
  11.                return true;  
  12.            case MENU_SEARCH:  
  13.                onSearchRequested();  
  14.                return true;  
  15.            case MENU_NOTIFICATIONS:  
  16.                showNotifications();  
  17.                return true;  
  18.        }  
  19.   
  20.        return super.onOptionsItemSelected(item);  
  21.    }  

將進入

  1. private void startWallpaper() {  
  2.     closeAllApps(true);  
  3.     final Intent pickWallpaper = new Intent(Intent.ACTION_SET_WALLPAPER);  
  4.     Intent chooser = Intent.createChooser(pickWallpaper,  
  5.             getText(R.string.chooser_wallpaper));  
  6.     startActivityForResult(chooser, REQUEST_PICK_WALLPAPER);  
  7. }  


三、launcher中launcher的搜索框和ProtipWidget(widget修改)

 Launcher2/res/xml/default_workspace.xml

  1. <!--  Middle screen [2] <search>爲添加google搜索框;-->  
  2. <search  
  3.     launcher:screen="2"  
  4.     launcher:x="0"  
  5.     launcher:y="0" />  
  6. !--  <appwidget>爲添加相應的widget; -->  
  7. <appwidget  
  8.     launcher:packageName="com.android.protips"  
  9.     launcher:className="com.android.protips.ProtipWidget"  
  10.     launcher:screen="2"  
  11.     launcher:x="0"  
  12.     launcher:y="1"  
  13.     launcher:spanX="4"  
  14.     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

  1. <!-- The workspace contains 3 screens of cells -->  
  2. <com.android.launcher2.Workspace  
  3.     android:id="@+id/workspace"  
  4.     android:layout_width="match_parent"  
  5.     android:layout_height="match_parent"  
  6.     android:scrollbars="horizontal"  
  7.     android:fadeScrollbars="true"  
  8.     launcher:defaultScreen="2">  
  9.   
  10.     <include android:id="@+id/cell1" layout="@layout/workspace_screen" />  
  11.     <include android:id="@+id/cell2" layout="@layout/workspace_screen" />  
  12.     <include android:id="@+id/cell3" layout="@layout/workspace_screen" />  
  13.     <include android:id="@+id/cell4" layout="@layout/workspace_screen" />  
  14.     <include android:id="@+id/cell5" layout="@layout/workspace_screen" />  
  15.   
  16. </com.android.launcher2.Workspace>  

修改默認屏同時需要修改workspace.java中

  1. public Workspace(Context context, AttributeSet attrs, int defStyle) {  
  2.     super(context, attrs, defStyle);  
  3.   
  4.     … …  
  5.     mDefaultScreen = a.getInt(R.styleable.Workspace_defaultScreen, 1);  
  6.     a.recycle();  
  7.   
  8.     setHapticFeedbackEnabled(false);  
  9.     initWorkspace();  

五、頁面標記實現原理

Launcher2/res/drawable/ home_arrows_left.xml

  1. <selector xmlns:android="http://schemas.android.com/apk/res/android">  
  2.     <item android:state_pressed="true" android:drawable="@drawable/all_apps_button_pressed" />  
  3.     <item android:state_focused="true" android:state_window_focused="true" android:drawable="@drawable/all_apps_button_focused" />  
  4.     <item android:state_focused="true" android:state_window_focused="false" android:drawable="@drawable/all_apps_button_normal" />  
  5.     <item android:drawable="@drawable/all_apps_button_normal" />  
  6. </selector>  

Launcher2/res/layout-port/Launcher.xml

  1. <ImageView  
  2.     android:id="@+id/previous_screen"  
  3.     android:layout_width="93dip"  
  4.     android:layout_height="@dimen/button_bar_height"  
  5.     android:layout_gravity="bottom|left"  
  6.     android:layout_marginLeft="6dip"  
  7.   
  8.     android:scaleType="center"  
  9.     android:src="@drawable/home_arrows_left"  
  10.       
  11.     android:onClick="previousScreen"    <!—點擊事件處理-->  
  12.   
  13.     android:focusable="true"  
  14.     android:clickable="true" />  

在launcher.java中實現

  1. private void setupViews() {  
  2.   
  3. … …  
  4.         mPreviousView = (ImageView) dragLayer.findViewById(R.id.previous_screen);  
  5.         mNextView = (ImageView) dragLayer.findViewById(R.id.next_screen);  
  6.   
  7.         Drawable previous = mPreviousView.getDrawable();  
  8.         Drawable next = mNextView.getDrawable();  
  9.         mWorkspace.setIndicators(previous, next);  
  10.     … …  
  11. }  

獲取這個兩旁的圖片設置到mCurrentScreen屏幕上

Workspace.java

  1. void setIndicators(Drawable previous, Drawable next) {  
  2.     mPreviousIndicator = previous;  
  3.     mNextIndicator = next;  
  4.     previous.setLevel(mCurrentScreen);  
  5.     next.setLevel(mCurrentScreen);  

在launcher.xml中定義

android:onClick="previousScreen"    <!—點擊事件處理-->

在launcher.java中的處理爲

  1. @SuppressWarnings({"UnusedDeclaration"})  
  2. public void previousScreen(View v) {  
  3.     if (!isAllAppsVisible()) {  
  4.         mWorkspace.scrollLeft();  
  5.     }  
  6. }  
  7.   
  8. @SuppressWarnings({"UnusedDeclaration"})  
  9. public void nextScreen(View v) {  
  10.     if (!isAllAppsVisible()) {  
  11.         mWorkspace.scrollRight();  
  12.     }  
  13. }  

進入到workspace中

  1. public void scrollLeft() {  
  2.         clearVacantCache();  
  3.         if (mScroller.isFinished()) {  
  4.             if (mCurrentScreen > 0) snapToScreen(mCurrentScreen - 1);  
  5.         } else {  
  6.             if (mNextScreen > 0) snapToScreen(mNextScreen - 1);              
  7.         }  
  8.     }  
  9.   
  10.     public void scrollRight() {  
  11.         clearVacantCache();  
  12.         if (mScroller.isFinished()) {  
  13.             if (mCurrentScreen < getChildCount() -1) snapToScreen(mCurrentScreen + 1);  
  14.         } else {  
  15.             if (mNextScreen < getChildCount() -1) snapToScreen(mNextScreen + 1);              
  16.         }  
  17.     }  

先判斷滑動是否完成,然後跳轉到目標屏。

  1. void snapToScreen(int whichScreen) {  
  2.         snapToScreen(whichScreen, 0false);  
  3.     }  
  4.   
  5.     private void snapToScreen(int whichScreen, int velocity, boolean settle) {  
  6.         //if (!mScroller.isFinished()) return;  
  7.   
  8.         whichScreen = Math.max(0, Math.min(whichScreen, getChildCount() - 1));  
  9.           
  10.         clearVacantCache();  
  11.         enableChildrenCache(mCurrentScreen, whichScreen);  
  12.   
  13.         mNextScreen = whichScreen;  
  14.   
  15.         mPreviousIndicator.setLevel(mNextScreen);  
  16.         mNextIndicator.setLevel(mNextScreen);  
  17.   
  18.         View focusedChild = getFocusedChild();  
  19.         if (focusedChild != null && whichScreen != mCurrentScreen &&  
  20.                 focusedChild == getChildAt(mCurrentScreen)) {  
  21.             focusedChild.clearFocus();  
  22.         }  
  23.           
  24.         final int screenDelta = Math.max(1, Math.abs(whichScreen - mCurrentScreen));  
  25.         final int newX = whichScreen * getWidth();  
  26.         final int delta = newX - mScrollX;  
  27.         int duration = (screenDelta + 1) * 100;  
  28.   
  29.         if (!mScroller.isFinished()) {  
  30.             mScroller.abortAnimation();  
  31.         }  
  32.           
  33.         if (settle) {  
  34.             mScrollInterpolator.setDistance(screenDelta);  
  35.         } else {  
  36.             mScrollInterpolator.disableSettle();  
  37.         }  
  38.           
  39.         velocity = Math.abs(velocity);  
  40.         if (velocity > 0) {  
  41.             duration += (duration / (velocity / BASELINE_FLING_VELOCITY))  
  42.                     * FLING_VELOCITY_INFLUENCE;  
  43.         } else {  
  44.             duration += 100;  
  45.         }  
  46.   
  47.         awakenScrollBars(duration);  
  48.         mScroller.startScroll(mScrollX, 0, delta, 0, duration);  
  49.         invalidate();    
  50.     }  

六、左右滑動切換屏幕

在workspace中實現屏幕切換主要重寫了以下幾個方法:onMeasure()、onLayout()、onInterceptTouchEvent()、onTouchEvent()方法。

  1. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
  2.     super.onMeasure(widthMeasureSpec, heightMeasureSpec);  
  3.   
  4.     final int width = MeasureSpec.getSize(widthMeasureSpec);  
  5.     final int widthMode = MeasureSpec.getMode(widthMeasureSpec);  
  6.     if (widthMode != MeasureSpec.EXACTLY) {  
  7.         throw new IllegalStateException("Workspace can only be used in EXACTLY mode.");  
  8.     }  
  9.   
  10.     final int heightMode = MeasureSpec.getMode(heightMeasureSpec);  
  11.     if (heightMode != MeasureSpec.EXACTLY) {  
  12.         throw new IllegalStateException("Workspace can only be used in EXACTLY mode.");  
  13.     }  
  14.   
  15.     // The children are given the same width and height as the workspace  
  16.     final int count = getChildCount();  
  17.     for (int i = 0; i < count; i++) {  
  18.         getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec);  
  19.     }  
  20.   
  21.   
  22.     if (mFirstLayout) {  
  23.         setHorizontalScrollBarEnabled(false);  
  24.         scrollTo(mCurrentScreen * width, 0);  
  25.         setHorizontalScrollBarEnabled(true);  
  26.         updateWallpaperOffset(width * (getChildCount() - 1));  
  27.         mFirstLayout = false;  
  28.     }  
  29. }  


onInterceptTouchEvent()方法和onTouchEvent()主要是來響應手指按下划動時所需要捕獲的消息,例如划動的速度,划動的距離等。當手指起來時,根據划動的速度與跨度來判斷是向左滑動一頁還是向右滑動一頁,確保每次用戶操作結束之後顯示的都是整體的一個子view.

 

在View.java中framework\base\core\java\android\view

將當前視圖內容偏移至(x , y)座標處,即顯示(可視)區域位於(x , y)座標

  1. public void scrollTo(int x, int y) {  
  2.     if (mScrollX != x || mScrollY != y) {  
  3.         int oldX = mScrollX;  
  4.         int oldY = mScrollY;  
  5.         mScrollX = x;  
  6.         mScrollY = y;  
  7.         onScrollChanged(mScrollX, mScrollY, oldX, oldY);  
  8.         if (!awakenScrollBars()) {  
  9.             invalidate();  
  10.         }  
  11.     }  
  12. }  

在當前視圖內容繼續偏移(x , y)個單位,顯示(可視)區域也跟着偏移(x,y)個單位

  1. public void scrollBy(int x, int y) {  
  2.     scrollTo(mScrollX + x, mScrollY + y);  
  3. }  
mScrollX 與 mScrollY 代表我們當前偏移的位置 , 在當前位置繼續偏移(x ,y)個單位 


七、獲取應用列表(相當於mainmenu)

在launcher.java中

Launcher 佈局文件初始化方法中

  1. private void setupViews() {  
  2. … …   
  3. mHandleView = (HandleView) findViewById(R.id.all_apps_button);  
  4. mHandleView.setLauncher(this);  
  5. mHandleView.setOnClickListener(this);  
  6. mHandleView.setOnLongClickListener(this);  
  7. … …  
  8. }  

點擊中間button

  1. public void onClick(View v) {  
  2.     Object tag = v.getTag();  
  3.     if (tag instanceof ShortcutInfo) {  
  4. …  
  5.     } else if (tag instanceof FolderInfo) {  
  6.         handleFolderClick((FolderInfo) tag);  
  7.     } else if (v == mHandleView) {  
  8.         if (isAllAppsVisible()) {  
  9.             closeAllApps(true);  
  10.         } else {  
  11.             showAllApps(true);  
  12.         }  
  13.     }  

進入到showAllApps()方法

  1. void showAllApps(boolean animated) {  
  2.     mAllAppsGrid.zoom(1.0f, animated);  
  3.   
  4.     ((View) mAllAppsGrid).setFocusable(true);  
  5.     ((View) mAllAppsGrid).requestFocus();  
  6.       
  7.     // TODO: fade these two too  
  8.     mDeleteZone.setVisibility(View.GONE);  
  9. }  

在上面使用到了mAllAppsGrid

 privateAllAppsView mAllAppsGrid; //聲明

在setupViews()方法中

  1. private void setupViews() {  
  2. … …  
  3.         mAllAppsGrid = (AllAppsView)dragLayer.findViewById(R.id.all_apps_view);  
  4.         mAllAppsGrid.setLauncher(this);  
  5.         mAllAppsGrid.setDragController(dragController);  
  6.         ((View) mAllAppsGrid).setWillNotDraw(false); // We don't want a hole punched in our window.  
  7.         // Manage focusability manually since this thing is always visible  
  8.         ((View) mAllAppsGrid).setFocusable(false);  
  9.         … …  
  10. }  

all_apps_view是在all_app_2d.xml中定義

位於res/layout-port中

故找到AllApps2D.java可以找到addApps,removeApps,zoom這幾個方法。

  1. public void zoom(float zoom, boolean animate) {  
  2.       Log.d(TAG, "zooming " + ((zoom == 1.0) ? "open" : "closed"));  
  3.     cancelLongPress();  
  4.   
  5.     mZoom = zoom;  
  6.   
  7.     if (isVisible()) {  
  8.         getParent().bringChildToFront(this);  
  9.         setVisibility(View.VISIBLE);  
  10.         mGrid.setAdapter(mAppsAdapter);  
  11.         if (animate) {  
  12.             startAnimation(AnimationUtils.loadAnimation(getContext(), R.anim.all_apps_2d_fade_in));  
  13.         } else {  
  14.             onAnimationEnd();  
  15.         }  
  16.     } else {  
  17.         if (animate) {  
  18.             startAnimation(AnimationUtils.loadAnimation(getContext(), R.anim.all_apps_2d_fade_out));  
  19.         } else {  
  20.             onAnimationEnd();  
  21.         }  
  22.     }  
  23. }  

在zoom()方法裏面有mGrid.setAdapter(mAppsAdapter),在構造方法中,給adapter已經賦值。

  1. public AllApps2D(Context context, AttributeSet attrs) {  
  2.     super(context, attrs);  
  3.     setVisibility(View.GONE);  
  4.     setSoundEffectsEnabled(false);  
  5.   
  6.     mAppsAdapter = new AppsAdapter(getContext(), mAllAppsList);  
  7.     mAppsAdapter.setNotifyOnChange(false);  
  8. }  

在launcher.java函數中

  1. private void loadHotseats() {  
  2. … …   
  3. PackageManager pm = getPackageManager();  
  4. … …  
  5. ResolveInfo bestMatch = pm.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY);  
  6. List<ResolveInfo> allMatches = pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);  
  7. … …  
  8. }  

如此獲取了應用列表

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章