Launcher of Android

Analyze the Launcher in Android

It’s based on the android 4.0.3.

You can find the configuration ofthe Launcher in /LINUX/android/packages/apps/Launcher

In /res/xml/default_workspace.xml,you can find the file.

1.       <favorite  //程序快捷鍵屬性標籤

2.          launcher:className="com.apical.radio.radioMainActivity"   //該應用的類,點擊圖標時,需要啓動的類

3.          launcher:packageName="com.apical.radio"          //該應用的包名

4.          launcher:screen="1"           //1,0-4屏共5

5.           launcher:x="0"                 //圖標X位置,左上角第一個爲0,向左遞增,0-45

6.           l0auncher:y="0"           //圖標Y位置,左上角第一個爲0,向下遞增,0-23

7.       />

In default, android has 5 screen.If you want to change it, you can modify the property target.

1.       //桌面Widget的標籤

2.       <appwidget //插件

3.           launcher:className="de.dnsproject.clock_widget_main.Clock1AppWidgetProvider"  //該應用的類

4.          launcher:packageName="de.dnsproject.clock_widget_main"       //該應用的包名

5.          launcher:screen="1"           //1,0-4屏共5

6.           launcher:x="2"                   //圖標X位置,左上角第一個爲0,向左遞增,0-45

7.           launcher:y="1"                                              //圖標Y位置,左上角第一個爲0,向下遞增,0-23

8.          launcher:spanX="3"                                           //x方向上所佔格數

9.         launcher:spanY="2" />                                        //y方向上所佔格數

Widget is the same property of shortcut.Launcher.spanX and Launcher.spanY means the size of Widget. The formula is :minWidth = 72 * grids – 2. The height is the same way to account.

1.       <search              //搜索欄

2.       launcher:screen="1"             //2

3.       launcher:x="0"                   //圖標X位置

4.      launcher:y="1"/>                 //圖標Y位置

This is the search bar partconfiguration. If you don’t need it, you can just delete it.

There are some configuration :

1.       //default_workspace.xml中,支持的標籤有:

2.       favorite:應用程序快捷方式。

3.       shortcut:鏈接,如網址,本地磁盤路徑等。

4.       search:搜索框。

5.       clock:桌面上的鐘表Widget

6.       //支持的屬性有:

7.       launcher:title:圖標下面的文字,目前只支持引用,不能直接書寫字符串;

8.       launcher:icon:圖標引用;

9.       launcher:uri:鏈接地址,鏈接網址用的,使用shortcut標籤就可以定義一個超鏈接,打開某個網址。

10.    launcher:packageName:應用程序的包名;

11.    launcher:className:應用程序的啓動類名;

12.    launcher:screen:圖標所在的屏幕編號;

13.    launcher:x:圖標在橫向排列上的序號;

14.    launcher:y:圖標在縱向排列上的序號;

 

The analyze java file to Launcheris LauncherProvider.java. In this file, the loadFavorites method will analyzeit. This is the code:

1.       //pass the source ID and database to default_workspace. Analyze thedata in xml and store to Launcher database. Return the targets number toanalyze.

2.       private int loadFavorites(SQLiteDatabase db,int workspaceResourceId) {

3.       //.........

4.       int type;

5.                      while (((type = parser.next()) != XmlPullParser.END_TAG ||

6.                              parser.getDepth()> depth) && type != XmlPullParser.END_DOCUMENT)

7.                          if (type !=XmlPullParser.START_TAG) {

8.                              continue;

9.                          boolean added = false;

10.                       final String name =parser.getName();

11.                       TypedArray a =mContext.obtainStyledAttributes(attrs, R.styleable.Favorite);

12.                       long container =LauncherSettings.Favorites.CONTAINER_DESKTOP;

13.                       if (a.hasValue(R.styleable.Favorite_container)){

14.                           container =Long.valueOf(a.getString(R.styleable.Favorite_container));

15.                       String screen =a.getString(R.styleable.Favorite_screen);

16.                       String x =a.getString(R.styleable.Favorite_x);

17.                       String y =a.getString(R.styleable.Favorite_y);

18.                       // If we are adding to the hotseat,the screen is used as the position in the

19.                       // hotseat. This screen can't be atposition 0 because AllApps is in the

20.                       // zeroth position.

21.                       if (container ==LauncherSettings.Favorites.CONTAINER_HOTSEAT

22.                              && Integer.valueOf(screen) == allAppsButtonRank) {

23.                           throw newRuntimeException("Invalid screen position for hotseat item");

24.                       values.clear();

25.                      values.put(LauncherSettings.Favorites.CONTAINER, container);

26.                      values.put(LauncherSettings.Favorites.SCREEN, screen);

27.                      values.put(LauncherSettings.Favorites.CELLX, x);

28.                      values.put(LauncherSettings.Favorites.CELLY, y);

29.    //解析xml裏面的標籤,從這裏可以找到支持的標籤類型和相關屬性參數。

30.                       if (TAG_FAVORITE.equals(name)) {

31.                           long id =addAppShortcut(db, values, a, packageManager, intent);

32.                           added = id >= 0;

33.                       } else if (TAG_SEARCH.equals(name)){

34.                           added =addSearchWidget(db, values);

35.                       } else if (TAG_CLOCK.equals(name)){

36.                           added =addClockWidget(db, values);

37.                       } else if(TAG_APPWIDGET.equals(name)) {

38.                           added =addAppWidget(parser, attrs, type, db, values, a, packageManager);

39.                       } else if(TAG_SHORTCUT.equals(name)) {

40.                           long id =addUriShortcut(db, values, a);

41.                           added = id >= 0;

42.                       } else if (TAG_FOLDER.equals(name))

43.       //.........

44.    //folder屬性裏面的參數要多於2個,才能形成文件夾。

45.                           if(folderItems.size() < 2 && folderId >= 0) {

46.                               // Wejust delete the folder and any items that made it

47.                              deleteId(db, folderId);

48.                               if(folderItems.size() > 0) {

49.                                  deleteId(db, folderItems.get(0));

50.                               added =false;

51.                       if (added) i++;

52.                       a.recycle();

53.    //.........

54.    return i;

It's justanalyze XML and write data to database.

The size of icon and title

The configuration file is in:

/res/values/dimens.xml

The values support kinds of languages. The change shouldconsider about it. If the system support different resolution, the icon shouldchanges as well.

The code below is the modify of cell of values-land:

1.    <font color="rgb(0, 0, 0)"><fontface="Verdana,"><!-- Workspace cell size -->

2.        <dimenname="workspace_cell_width_land">88dp</dimen>

3.        <dimenname="workspace_cell_width_port">96dp</dimen>

4.        <dimenname="workspace_cell_height_land">88dp</dimen>

5.        <dimenname="workspace_cell_height_port">96dp</dimen>

6.        <dimenname="workspace_width_gap_land">32dp</dimen>

7.        <dimenname="workspace_width_gap_port">0dp</dimen>

8.        <dimen name="workspace_height_gap_land">0dp</dimen>

9.        <dimenname="workspace_height_gap_port">24dp</dimen>

10.  <!--Folders -->

11.     <dimen name="folder_preview_size">68dp</dimen>

12.     <dimen name="folder_cell_width">86dp</dimen>

13.     <dimen name="folder_cell_height">90dp</dimen>

14.     <dimen name="folder_width_gap">3dp</dimen>

15.     <dimen name="folder_height_gap">3dp</dimen>

16.     <dimenname="folder_padding">6dp</dimen></font></font>

Add the Launcher icons into default-background

The size of the icons are different, so the system should addone background of each icon, so the look would be consistent. The file is inclass--Utilities.java. In Bitmap createIconBitmap(Drawable icon, Contextcontext) solves the problems.

1.    static Bitmap createIconBitmap(Drawable icon, Context context) {

2.               //...............

3.                final intleft = (textureWidth-width) / 2;

4.                final inttop = (textureHeight-height) / 2;

5.                //For testing, add colorful back frame.

6.                if (false)

7.                   // draw a big box for the icon for debugging

8.                   canvas.drawColor(sColors[sColorIndex]);

9.                   if (++sColorIndex >= sColors.length) sColorIndex = 0;

10.                 Paint debugPaint =new Paint();

11.                debugPaint.setColor(0xffcccc00);

12.                canvas.drawRect(left, top, left+width, top+height, debugPaint);

13.             //add icon background picture OWL

14.             if (true)

15.                 Bitmap backBitmap =BitmapFactory.decodeResource(context.getResources(),

16.                        R.drawable.apical_icon_bg);

17.                 int backWidth =backBitmap.getWidth();

18.                 int backHeight =backBitmap.getHeight();

19.                 if(backWidth !=sIconWidth || backHeight != sIconHeight)

20.                    Matrix matrix = new Matrix();

21.                    matrix.postScale((float)sIconWidth/backWidth,(float)sIconHeight/backHeight);

22.                    canvas.drawBitmap(Bitmap.createBitmap(backBitmap, 0, 0,backWidth, backHeight, matrix, true),

23.  .0f,0.0f, null);

24.                 }else

25.                    canvas.drawBitmap(backBitmap, 0.0f, 0.0f, null);

26.             //................

27.             return bitmap;

In the code, the picture, R.drawable.apical_icon_bg, is addedas the default background, so the icon is the same size.

Change Launcher’s default_background.

The default_background is in the /framework/base/core/res/,and the picture is also in the directory.  There is one way to set up thedefault_background in Launcher.  Thepicture should be put in the class – Launcher.java -> onCreate() ->showFirstRunWorkspaceCling(). The function can only be called for the firststart up or default-set.

The code :

                1.private void setDefaultWallPaper()

                2.         //修改默認背景 OWL test,可以在Framework替換默認靜態圖default_wallpaper

                3.         WallpaperManager mwallpaerManager;

                4.         mwallpaerManager =WallpaperManager.getInstance(this);

                5.            mwallpaerManager.setResource(R.drawable.launcher_default_bg);

                6.         catch (IOException e)

                7.                 Log.e(TAG, "set defaultwallpaper error");

                8.                 e.printStackTrace();

 

The background pictures is called by WallpaperManager, andthey are in /framework/base/core/res/res/drawable-nodpi

If the default-dynamic-picture needs to change the/framework/base/core/res/res/values/config.xml

1.    <stringname="default_wallpaper_component">@null</string>

2.    //change the null to theprogram’s name  as below:

3.     <stringname="default_wallpaper_component" translatable="false">包名/動態壁紙服務名</string>

The set of the  wallpaperis in /values/wallpaper.xml

1.    <resources>

2.        <string-array name="wallpapers"translatable="false">

3.           <item>wallpaper_01</item>

4.           <item>wallpaper_02</item>

5.           <item>wallpaper_03</item>

6.           <item>wallpaper_04</item>

7.            <item>wallpaper_05</item>

8.           <item>wallpaper_06</item>

9.           <item>wallpaper_07</item>

10.         <item>wallpaper_08</item>

11.         <item>wallpaper_09</item>

12.         <item>wallpaper_10</item>

13.         <item>wallpaper_11</item>

14.         <item>wallpaper_12</item>

15.     </string-array>

16.  </resources>

The R.array.wallpaper is the file to configuration ofdefault-wallpaper.

This is the code to set up the wallpaper.

1.    private void selectWallpaper(int position) {

2.               WallpaperManager wpm = (WallpaperManager) getActivity().getSystemService(

3.                       Context.WALLPAPER_SERVICE);

4.               wpm.setResource(mImages.get(position)); //設置壁紙

5.                Activityactivity = getActivity();

6.               activity.setResult(Activity.RESULT_OK);

7.               activity.finish();

8.            } catch (IOException e) {

9.                Log.e(TAG, "Failed to setwallpaper: " + e);

Init of Launcher

The picture show the process to initial the Launcher.

 

 

Activity Manager send the intent to start Launcher.

1.    Intent intent = new Intent(mTopAction, mTopData != null ?

2.      Uri.parse(mTopData) : null);

3.    intent.setComponent(mTopComponent);

4.    if (mFactoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL)

5.        intent.addCategory(Intent.CATEGORY_HOME);

6.    startActivityLocked(null, intent, null, null, 0, aInfo,

7.       null,null, 0, 0, 0, false, false);

If system exchange the Launcher, itshould add the action.MAIN, category.HOME, category.DEFAULT to <intent-filter>.If there are multiple Launchers added to the program, the system will pop onewindow to let you choose the initial.

 

This is the target ofApplication:

1.    <application

2.            android:name="com.android.launcher2.LauncherApplication"

3.           android:label="@string/application_name"  

4.           android:icon="@drawable/ic_launcher_home"

5.           android:hardwareAccelerated="@bool/config_hardwareAccelerated"

6.           android:largeHeap="@bool/config_largeHeap"

7.           android:configChanges="locale">

8.       </application>

Android has four classes’ defines should be put intoapplication target, the default uses the system’s application class. If youreload it, you should write the new application’s class name to the nameproperty. Application will be load when start.

1.     LauncherApplicationapp = ((LauncherApplication)getApplication());

The initialization to the Launcher.

  1. @Override
  2.     public void onCreate()
  3.         super.onCreate();
  4.         //get the size of the screen to tell the type of the devices
  5.         final int screenSize = getResources().getConfiguration().screenLayout &
  6.                 Configuration.SCREENLAYOUT_SIZE_MASK;
  7.         sIsScreenLarge = screenSize == Configuration.SCREENLAYOUT_SIZE_LARGE ||
  8.             screenSize == Configuration.SCREENLAYOUT_SIZE_XLARGE;
  9.         //screen dense
  10.         sScreenDensity = getResources().getDisplayMetrics().density;
  11.      //IconCahe stores all the data about icon pictures.
  12.         //to improve the effecting to picture the screen.
  13.         mIconCache = new IconCache(this);
  14.         //data load class. LauncherModel and
  15.      //Model function, manage the data and initialize the data.
  16.         mModel = new LauncherModel(this, mIconCache);
  17.         //below  registers the listener, the notify about APK update and delete.
  18.           //After get the notify, update the data of Launcher. It will only load once.
  19.         IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
  20.         filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
  21.         filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
  22.         filter.addDataScheme("package");
  23.         registerReceiver(mModel, filter);
  24.         filter = new IntentFilter();
  25.         filter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
  26.         filter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
  27.         filter.addAction(Intent.ACTION_LOCALE_CHANGED);
  28.         filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
  29.         registerReceiver(mModel, filter);
  30.         filter = new IntentFilter();
  31.         filter.addAction(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED);
  32.         registerReceiver(mModel, filter);
  33.         filter = new IntentFilter();
  34.         filter.addAction(SearchManager.INTENT_ACTION_SEARCHABLES_CHANGED);
  35.         registerReceiver(mModel, filter);
  36.      //contentresolver manage all the program’s contentprovider instances.
  37.         ContentResolver resolver = getContentResolver();
  38.         //register the content obvious, listene the data change , and recall
  39.         resolver.registerContentObserver(LauncherSettings.Favorites.CONTENT_URI, true, mFavoritesObserver);

The code above is just for initial the key class ofLauncher, register some listeners. Listen the update about the install, delete andupdate the apps leads to change the data about the Launcher. All the data aboutLauncher is provided by contentprovider. The register listener interface is :

1.    private final ContentObserver mFavoritesObserver = newContentObserver(new Handler())

2.            @Override

3.            public voidonChange(boolean selfChange)

4.     //重新加載界面數據

5.               mModel.startLoader(LauncherApplication.this, false);

LauncherSettings.Favorites.CONTENT_URI calls the mModel.startLoader()when changes and reload the data of Launchers. The rest code is getting thescreen dense, the size of screen etc.

 

Initial of the Launcher.java

Launcher.java is the main class, and it is Activity.

The code :

1.    @Override

2.        protected void onCreate(Bundle savedInstanceState)

3.           super.onCreate(savedInstanceState);

4.            //獲取Application實例

5.            LauncherApplication app =((LauncherApplication)getApplication());

6.     //LauncherModel類裏面獲取Launcher的對象引用

7.            mModel =app.setLauncher(this);

8.            //獲取IconCacheIconCacheApplication裏面初始化    

9.            mIconCache =app.getIconCache();

10.         mDragController = new DragController(this);

11.         mInflater = getLayoutInflater();

12.         mAppWidgetManager =AppWidgetManager.getInstance(this);

13.         //監聽widget改變,以後在Model裏面回調處理的結果

14.         mAppWidgetHost = new LauncherAppWidgetHost(this,APPWIDGET_HOST_ID);

15.         mAppWidgetHost.startListening();

16.  //這個是設置Launcher的跟蹤調試文件,下面很多信息會寫到這個文件裏面。

17.         if (PROFILE_STARTUP)

18.            android.os.Debug.startMethodTracing(

19.                    Environment.getExternalStorageDirectory() +"/launcher");

20.         //讀取本地配置,保存更新配置,清空IconCache

21.         checkForLocaleChange();

22.         setContentView(R.layout.launcher);

23.         //對所有的UI控件進行加載和配置

24.         setupViews();

25.         //顯示操作提示,第一次啓動的時候纔會顯示

26.         showFirstRunWorkspaceCling();

27.         //註冊監控Launcher數據庫變化

28.         registerContentObservers();

29.  //鎖住APP,初始化不能操作。

30.         lockAllApps();

31.         mSavedState = savedInstanceState;

32.         restoreState(mSavedState);

33.         // Update customization drawer _after_ restoringthe states

34.         if (mAppsCustomizeContent != null)

35.            mAppsCustomizeContent.onPackagesUpdated();

36.         if (PROFILE_STARTUP)

37.            android.os.Debug.stopMethodTracing();

38.         //加載啓動數據,所有界面數據(快捷方式、folderwidgetallApp)等在loader裏面加載,這部分後面我會詳細分析。

39.         if (!mRestoring) {

40.             mModel.startLoader(this, true);

41.         if (!mModel.isAllAppsLoaded())

42.             ViewGroupappsCustomizeContentParent = (ViewGroup) mAppsCustomizeContent.getParent();

43.            mInflater.inflate(R.layout.apps_customize_progressbar,appsCustomizeContentParent);

44.         // For handling default keys

45.         mDefaultKeySsb = new SpannableStringBuilder();

46.         Selection.setSelection(mDefaultKeySsb, 0);

47.         IntentFilter filter = newIntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);

48.         registerReceiver(mCloseSystemDialogsReceiver,filter);

49.  //下面這幾個就是Android原生界面上的Market、搜索、聲音輸入按鈕的默認圖標顯示。

50.         boolean searchVisible = false;

51.         boolean voiceVisible = false;

52.         // If we have a saved version of these externalicons, we load them up immediately

53.         int coi =getCurrentOrientationIndexForGlobalIcons();

54.         if (sGlobalSearchIcon[coi] == null ||sVoiceSearchIcon[coi] == null ||

55.                 sAppMarketIcon[coi]== null) {

56.             updateAppMarketIcon();

57.             searchVisible =updateGlobalSearchIcon();

58.             voiceVisible =updateVoiceSearchIcon(searchVisible);

59.         if (sGlobalSearchIcon[coi] != null) {

60.             updateGlobalSearchIcon(sGlobalSearchIcon[coi]);

61.              searchVisible = true;

62.         if (sVoiceSearchIcon[coi] != null)

63.            updateVoiceSearchIcon(sVoiceSearchIcon[coi]);

64.             voiceVisible = true;

65.         if (sAppMarketIcon[coi] != null)

66.            updateAppMarketIcon(sAppMarketIcon[coi]);

67.        mSearchDropTargetBar.onSearchPackagesChanged(searchVisible,voiceVisible);

68.         // On large interfaces, we want the screen toauto-rotate based on the current orientation

69.         if (LauncherApplication.isScreenLarge() ||Build.TYPE.contentEquals("eng"))

70.            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);

71.        Log.i(TAG,"------------------------>Launcher initover") ;

 

 

All above is reloading the datafor Launcher. mModel.startLoader(this, true);

In Launcher.java, onCreate() calls the load data interfaces:

1.     //加載啓動數據

2.     if(!mRestoring)

3.    mModel.startLoader(this, true);

The load data is in LauncherModel class.

Callbacks interface

1.     publicinterface Callbacks {

2.     publicboolean setLoadOnResume();

3.     publicint getCurrentWorkspaceScreen();

4.     publicvoid startBinding();

5.     publicvoid bindItems(ArrayList<ItemInfo> shortcuts, int start, int end);

6.     publicvoid bindFolders(HashMap<Long,FolderInfo> folders);

7.     publicvoid finishBindingItems();

8.     publicvoid bindAppWidget(LauncherAppWidgetInfo info);

9.     publicvoid bindAllApplications(ArrayList<ApplicationInfo> apps);

10.  publicvoid bindAppsAdded(ArrayList<ApplicationInfo> apps);

11.  publicvoid bindAppsUpdated(ArrayList<ApplicationInfo> apps);

12.  publicvoid bindAppsRemoved(ArrayList<ApplicationInfo> apps, boolean permanent);

13.  publicvoid bindPackagesUpdated();

14.  publicboolean isAllAppsVisible();

15. public void bindSearchablesChanged();

 

Callbacks have a lot of interfaces.

setLoadOnResume() When Launcher callonPause, it needs the onResume to recover.

getCurrentWorkspace()get the screens number(0-4)

startBinding()notify the Launcher toload data. Clean the data and reload it.

bindItems(ArrayList<ItemInfo> shortcuts, int start, int end)load App shortcut, LiveFolder, widget toLauncher’s .

bindFolders(HashMap<Long, FolderInfo> folders)load the content of folder.

finishBindingItems()數據加載完成。

bindAppWidget(LauncherAppWidgetInfo item)workspace load the app’ shortcut.

bindAllApplications(final ArrayList<ApplicationInfo> apps)All the app’s list connect to APP icon data.

bindAppsAdded(ArrayList<ApplicationInfo> apps)notify Launcher to install new APP, and updatethe data.

bindAppsUpdated(ArrayList<ApplicationInfo> apps)notify Launcher there is one new APP isupdate.

bindAppsRemoved(ArrayList<ApplicationInfo> apps, booleanpermanent)notify the app there is one app deleted.

bindPackagesUpdated()multiple apps update.

isAllAppsVisible()return all the app’listthat whether it can show the states.

bindSearchablesChanged()Google search bar or delete area changed to notifythe Launcher.

Data load process

Launcher.java inherits Callbacks’interface and realized it. LauncherModel will call this interface and returnthe data and states to Launcher. The data load part divides into two parts:load the data of workspace, load the all app’s screen data.

startLoader()

startLoader initial one thread to load data.

1.     public void startLoader(Context context,boolean isLaunching) {

2.     synchronized (mLock)

3.     //...............

4.     if (mCallbacks != null &&mCallbacks.get() != null) {

5.     isLaunching = isLaunching ||stopLoaderLocked();

6.     mLoaderTask = new LoaderTask(context,isLaunching);

7.     sWorkerThread.setPriority(Thread.NORM_PRIORITY);

8.    sWorker.post(mLoaderTask);

startLoader starts the run in LoaderTask’s thread. sWorkeris one Handles’ object to start run for thread.

LoaderTask’s run() method

 

1.     public void run() {

2.     //............

3.     keep_running: {

4.     //...............

5.     //加載當前頁面的數據,先把一頁的數據加載完成,

6.     //主要是爲了增加程序流暢性,提高用戶體驗

7.     if (loadWorkspaceFirst) {

8.     if (DEBUG_LOADERS) Log.d(TAG, "step 1:loading workspace");

9.     loadAndBindWorkspace();

10.  } else {

11.  if (DEBUG_LOADERS) Log.d(TAG, "step 1:special: loading all apps");

12.  loadAndBindAllApps();

13.  if (mStopped) {

14.  break keep_running;

15.  // THREAD_PRIORITY_BACKGROUND設置線程優先級爲後臺,

16.  //這樣當多個線程併發後很多無關緊要的線程分配的CPU時間將會減少,有利於主線程的處理

17.  synchronized (mLock) {

18.  if (mIsLaunching) {

19.  if (DEBUG_LOADERS) Log.d(TAG, "Settingthread priority to BACKGROUND");

20.  android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);

21.  //等待線程空閒的時候,繼續加載其他頁面數據

22.  waitForIdle();

23.  //加載剩餘頁面的數據,包含workspaceall app頁面

24.  if (loadWorkspaceFirst) {

25.  if (DEBUG_LOADERS) Log.d(TAG, "step 2:loading all apps");

26.  loadAndBindAllApps();

27.  } else {

28.  if (DEBUG_LOADERS) Log.d(TAG, "step 2:special: loading workspace");

29.  loadAndBindWorkspace();

30.  // Restore the default thread priority afterwe are done loading items

31.  synchronized (mLock) {

32. android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);

This code divides to two parts:

Load the page’s data and wait the free time of thread toload the rest data .

This method’s goal is to increase the speed to start. Showthe screen to user and load the data.

Workspace loads the data.

LoadAndBindWorkspace() runs the loadWorkspace() and bindWorkspace()

1.     private void loadWorkspace() {

2.     //..........

3.     //清空容器,存放界面不同的元素,App快捷方式、widgetfolder

4.                synchronized (sBgLock) {

5.                    sBgWorkspaceItems.clear();

6.                    sBgAppWidgets.clear();

7.                    sBgFolders.clear();

8.                    sBgItemsIdMap.clear();

9.                    sBgDbIconCache.clear();

10.                 final ArrayList<Long> itemsToRemove = newArrayList<Long>();

11.                 final Cursor c = contentResolver.query(

12.                         LauncherSettings.Favorites.CONTENT_URI,null, null, null, null);

13.                 // +1 for the hotseat (it can be larger than the workspace)

14.                 // Load workspace in reverse order to ensure that latestitems are loaded first (and

15.                 // before any earlier duplicates)

16.  //表示屏幕上的位置,

17.  //第一維表示分屏的序號,其中最後一個代表Hotseat            

18.  //第二維表示x方向方格的序號         

19.  //第三維表示y方向方格的序號

20.                 final ItemInfo occupied[][][] =

21.                         newItemInfo[Launcher.SCREEN_COUNT + 1][mCellCountX + 1][mCellCountY + 1];

22.  //讀取數據庫響應鍵值列序號

23.                     final int idIndex =c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);

24.                     final int intentIndex =c.getColumnIndexOrThrow

25.                            (LauncherSettings.Favorites.INTENT);

26.                     final int titleIndex =c.getColumnIndexOrThrow

27.                            (LauncherSettings.Favorites.TITLE);

28.                     final int iconTypeIndex =c.getColumnIndexOrThrow(

29.                            LauncherSettings.Favorites.ICON_TYPE);

30.  //...........

31.     while (!mStopped && c.moveToNext()) {

32.                             intitemType = c.getInt(itemTypeIndex);

33.                             switch(itemType) {

34.  //item類型爲ITEM_TYPE_APPLICATION或者ITEM_TYPE_SHORTCUT  

35.  //containerCONTAINER_DESKTOP或者CONTAINER_HOTSEAT

36.  //把當前的item添加到sWorkspaceItems

37.  caseLauncherSettings.Favorites.ITEM_TYPE_APPLICATION:

38.                             caseLauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:

39.  emType ==LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {

40.                                    info = getShortcutInfo(manager, intent,context, c, iconIndex,

41.                                           titleIndex, mLabelCache);

42.                                } else {

43.                                    info = getShortcutInfo(c, context,iconTypeIndex,

44.                                           iconPackageIndex, iconResourceIndex, iconIndex,

45.                                           titleIndex);

46.                                    switch (container) {

47.                                    caseLauncherSettings.Favorites.CONTAINER_DESKTOP:

48.                                    caseLauncherSettings.Favorites.CONTAINER_HOTSEAT:

49.  //添加數據

50.                                        sBgWorkspaceItems.add(info);

51.                                        break;

52.                                    default:

53.                                        //如果item的屬性是folder,添加到folder,創建forder

54.                                        FolderInfo folderInfo =

55.                                               findOrMakeFolder(sBgFolders, container);

56.                                        folderInfo.add(info);

57.                                        break;

58.                                    sBgItemsIdMap.put(info.id, info);

59.                                } else {

60.                                break;

61.  //item類型爲文件夾,添加

62.                             caseLauncherSettings.Favorites.ITEM_TYPE_FOLDER:

63.                                id = c.getLong(idIndex);

64.                                FolderInfo folderInfo = findOrMakeFolder(sBgFolders, id);

65.  //.........

66.                                sBgItemsIdMap.put(folderInfo.id, folderInfo);

67.                                sBgFolders.put(folderInfo.id, folderInfo);

68.                                break;

69.  //Widget添加

70.                             caseLauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:

71.                                // Read all Launcher-specific widget details

72.                                int appWidgetId = c.getInt(appWidgetIdIndex);

73.                                id = c.getLong(idIndex);

74.  //...........

75.                                sBgItemsIdMap.put(appWidgetInfo.id, appWidgetInfo);

76.                                sBgAppWidgets.add(appWidgetInfo);

77.                                break;

78.                         } catch (Exception e){

79.                            Log.w(TAG, "Desktop items loading interrupted:", e);

80.                 } finally {

81.                     c.close();

the loading data of workspace is laoding 

/*****************************************************/

 

Analyze the HotSeat

Hotseat is the shortcut guide bar. The default settings hasno Hotseat.  So we need to add it.

The configure profile of Hotseat.

Hotseat is belonging to the workspace, the configure is inthe configure file of workspace. The Hotseat is in  the launcher.xml. Not all the launcher.xmlhas the hotseat performance. E.g: layout-sw600 has no hotseat configuration inits launcher.xml.

1.      Add the hotseat into the launcher.xml of layout_sw-600dp.

1.     <!-- WorkSpace最下面的五個快捷位置  mythou-->

2.         <includelayout="@layout/hotseat"

3.            android:id="@+id/hotseat"

4.            android:layout_width="match_parent"

5.            android:layout_height="@dimen/button_bar_height_plus_padding"

6.           android:layout_gravity="bottom" />

2.     The bottoms of the hotseat are 5, the middleone is for entering All the app list.

The setting is below:

1.     <!-- Hotseat (We use the screen as theposition of the item in the hotseat) -->

2.     <!-- 使用screen作爲按鈕位置標識-->

3.         <favorite

4.            launcher:packageName="com.example.naviback"

5.            launcher:className="com.example.naviback.MainActivity"

6.            launcher:container="-101"

7.            launcher:screen="0"

8.            launcher:x="0"

9.            launcher:y="0" />

10.      <favorite

11.         launcher:packageName="com.csr.dvd"

12.         launcher:className="com.csr.dvd.LoadDVD"

13.         launcher:container="-101"

14.         launcher:screen="1"

15.         launcher:x="1"

16.         launcher:y="0" />

17.      <favorite

18.         launcher:packageName="com.apical.apicalradio"

19.         launcher:className="com.apical.apicalradio.RadioMainActivity"

20.         launcher:container="-101"

21.         launcher:screen="3"

22.         launcher:x="3"

23.         launcher:y="0" />

24.      <favorite

25.         launcher:packageName="com.csr.BTApp"

26.         launcher:className="com.csr.BTApp.CSRBluetoothDemoActivity"

27.         launcher:container="-101"

28.         launcher:screen="4"

29.         launcher:x="4"

30.        launcher:y="0" />

The configuration ofthe hotseat is different. The instruction of it shows below:

Launcher:container  : target with -101, namely the defaultsbottom of hotseat.

Launcher:screen:stands for the position of the bottom. 0 means the the first one ,and allapp isthe 2. So it has not the number 2 screen.

The horizon screen:

1.     <com.android.launcher2.Hotseat

2.         xmlns:android="http://schemas.android.com/apk/res/android"

3.        xmlns:launcher="http://schemas.android.com/apk/res/com.android.launcher"

4.        android:background="@drawable/hotseat_bg_panel"  <!--設置hotseat的背景 -->

5.        launcher:cellCountX="5"   <!--代表hotseat橫向有多少個方格(圖標)-->

6.        launcher:cellCountY="1">  <!--代碼hotseat豎向有多少個方格-->

7.        <com.android.launcher2.CellLayout     <!--實際容納按鈕的容器是CellLayout -->

8.            android:id="@+id/layout"

9.            android:layout_width="match_parent"

10.         android:layout_height="wrap_content"

11.         android:layout_gravity="center"

12.  <!--下面幾個屬性就是控制5個按鈕的顯示位置調整,跟我們一般使用控件屬性一樣 -->

13.         android:paddingTop="3dp"

14.         android:paddingBottom="3dp"

15.         android:paddingLeft="150dp"

16.         android:paddingRight="@dimen/button_bar_height_bottom_padding"

17.  <!-- hotseat裏面按鈕的大小以及兩個按鈕之間的間距 -->

18.         launcher:cellWidth="106dip"

19.         launcher:cellHeight="106dip"

20.         launcher:widthGap="25dp"

21.         launcher:heightGap="@dimen/hotseat_height_gap"

22.         launcher:maxGap="@dimen/workspace_max_gap" />

23. </com.android.launcher2.Hotseat>

We need noticesome  configuration: laucher:cellCountXand launcher:cellCountY. The hotseat contains one CellLayout. Because thehotseat needs some room, so the workspace should share some room to hotseat.

Hotseat’s constructedfunction. Since the performance of the Hotseat is not perfect. So we need toconfigure the class:Hotseat.java.

1.     public Hotseat(Context context, AttributeSetattrs, int defStyle)

2.            super(context, attrs, defStyle);

3.             TypedArraya = context.obtainStyledAttributes(attrs,

4.                    R.styleable.Hotseat, defStyle, 0);

5.            mCellCountX = a.getInt(R.styleable.Hotseat_cellCountX, -1);

6.            mCellCountY = a.getInt(R.styleable.Hotseat_cellCountY, -1);

7.            mIsLandscape =context.getResources().getConfiguration().orientation ==

8.                Configuration.ORIENTATION_LANDSCAPE;

9.             //設置成豎屏,使用豎屏時候的hotseat

10.        mIsLandscape = false;

There is one judgeabout the size of the screen.

The load of the Hotseat.

 

The load of the Hotseatdivided into two parts. Allapp bottom and other default bottom.

Other app bottoms:

 

1.     private void loadWorkspace()

2.     //........

3.          switch (container)

4.             caseLauncherSettings.Favorites.CONTAINER_DESKTOP:  

5.             caseLauncherSettings.Favorites.CONTAINER_HOTSEAT:  

6.                 sWorkspaceItems.add(info);  

7.                 break;  

8.         //........

 

Hotseat bind the data.

The flow of the binding data for hotseat is the same asworkspace.  The beginning of bindingdata.

1.     public void startBinding()

2.        //..........

3.        // 清空Hotseat的數據

4.         if (mHotseat != null)

5.            mHotseat.resetLayout();  

 

Inside of the Hotseatis just one CellLayout to response the interior elements.

The way to bind thedata.

1.     void addInScreen(View child, long container,int screen, int x, int y, int spanX, int spanY,

2.                boolean insert) {

3.     //...........

4.     //創建CellLayout,用於添加Hotseat的對象。

5.             finalCellLayout layout;

6.             if(container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {

7.                layout = mLauncher.getHotseat().getLayout();

8.                child.setOnKeyListener(null);

9.                // Hide folder title in the hotseat

10.             if (child instanceof FolderIcon) {

11.                 ((FolderIcon) child).setTextVisible(false);

12.             if (screen < 0) {

13.                 screen = mLauncher.getHotseat().getOrderInHotseat(x, y);

14.             } else {

15.                 // Note: We do this to ensure that the hotseat is alwayslaid out in the orientation

16.                 // of the hotseat in order regardless of which orientationthey were added

17.                 //獲取child的位置,返回true添加成功,false失敗  

18.                 x = mLauncher.getHotseat().getCellXFromOrder(screen);

19.                 y = mLauncher.getHotseat().getCellYFromOrder(screen);

20.          } else {

21.             //如果Hotseat裏面有Folder,隱藏文件夾名字

22.             if (child instanceof FolderIcon) {

23.                 ((FolderIcon) child).setTextVisible(true);

24.             layout = (CellLayout) getChildAt(screen);

25.             child.setOnKeyListener(new IconKeyEventListener());

26.     //.........

Hotseat class

The class of Hotseat is just constructed function.

1.     void resetLayout() {

2.     //清空原來的內容

3.            mContent.removeAllViewsInLayout();

4.             //添加AllAPP按鈕,也是一個BubbleTextView對象

5.             Contextcontext = getContext();

6.            LayoutInflater inflater = LayoutInflater.from(context);

7.            BubbleTextView allAppsButton = (BubbleTextView)

8.                    inflater.inflate(R.layout.application, mContent, false);

9.     //加載AllAPP按鈕的圖標,這裏使用了selector作爲按鈕配置

10.         allAppsButton.setCompoundDrawablesWithIntrinsicBounds(null,

11.                context.getResources().getDrawable(R.drawable.all_apps_button_icon), null,null);

12.          //allAppsButton.setText(context.getString(R.string.all_apps_button_label));

13.         allAppsButton.setContentDescription(context.getString(R.string.all_apps_button_label));

14.          //allapp按鈕觸摸和點擊響應,回調Launcher的功能

15.         allAppsButton.setOnTouchListener(new View.OnTouchListener()

16.             @Override

17.             public boolean onTouch(View v, MotionEvent event) {

18.                 if (mLauncher != null &&

19.                     (event.getAction() &MotionEvent.ACTION_MASK) == MotionEvent.ACTION_DOWN) {

20.                    mLauncher.onTouchDownAllAppsButton(v);

21.                 return false;

22.  //AllAPP按鈕點擊響應的方法,點擊的處理在Launcher類裏面

23.         allAppsButton.setOnClickListener(new View.OnClickListener()

24.             @Override

25.             public void onClick(android.view.View v) {

26.                 if (mLauncher != null) {

27.                     mLauncher.onClickAllAppsButton(v);

28.  //這裏會判斷是小屏幕還是大屏幕,決定AllAPP按鈕的位置

29.          int x =getCellXFromOrder(sAllAppsButtonRank);

30.          int y =getCellYFromOrder(sAllAppsButtonRank);

31.         Log.d("Mythou_Launcher","Hotseat------>x="+x+"  y="+y);

32.         //Hotseat中清空了裝載的內容,然後重新加載allAppsButtonCellLayout mythou

33.         mContent.addViewToCellLayout(allAppsButton, -1, 0, newCellLayout.LayoutParams(x,y,1,1),

34.                 true);

 

Conclusion

Hotseat is just one CellLayout to manage allthe data.

Most of the configuration can make by the XML.

Load and bind data is the same as workspace.

 

 

 

 

Paged View

The PageView inherited theViewGroup Class, and it is one contain. The PageView actually contains thedisplay of the CellLayout.  When we touchthe screen, the View class will start the function of onInterceptTouchEvent().This function response for the original message driver, decide whether to passthe info to the upper View.

The flow of pass the info :

 

 

The code of onInterceptTouchEvent. This code forjudging the state of TouchState.

 

1.          * 這個方法主要是決定觸摸消息如何是否需要往上層傳遞。

2.          * 如果返回爲true,則會調用PagedView類的onTouchEvent方法,處理觸摸事件。否則傳給上層View

3.     public booleanonInterceptTouchEvent(MotionEvent ev)

4.            if(OWL_DEBUG) Log.d(OWL, "onInterceptTouchEvent Enterchlid="+getChildCount());

5.             //獲取速度跟蹤器,記錄各個時刻的速度。並且添加當前的MotionEvent以記錄更新速度值。 OWL

6.            acquireVelocityTrackerAndAddMovement(ev);

7.             //沒有頁面,直接跳過給父類處理。

8.             if(getChildCount() <= 0) return super.onInterceptTouchEvent(ev);

9.              * 最常見的需要攔截的情況:用戶已經進入滑動狀態,而且正在移動手指滑動

10.           * 對這種情況直接進行攔截,調用PagedViewonTouchEvent()

11.          finalint action = ev.getAction();

12.          if ((action== MotionEvent.ACTION_MOVE) &&

13.                 (mTouchState == TOUCH_STATE_SCROLLING))

14.             if(OWL_DEBUG) Log.d(OWL, "onInterceptTouchEvent movescrolling...");

15.             //截斷觸摸反饋,直接觸發onToucEvent,滑動過程中。

16.             return true;

17.          switch(action & MotionEvent.ACTION_MASK)

18.             case MotionEvent.ACTION_MOVE:

19.                 if(OWL_DEBUG) Log.d(OWL,"onInterceptTouchEvent  ACTION_MOVE...");

20.                  * 當在這裏接受到ACTION_MOVE時,說明mTouchState!=TOUCH_STATE_SCROLLING

21.                  * 並且mIsBeingDragged的值應該爲false

22.                  * 否則DragLayer就應該截獲了MotionEvent用於實現拖拽。

23.                  * 此時還沒有進入滑動狀態,當mActivePointerId== INVALID_POINTER時,

24.                  * 也就是在此之前沒有接收到任何touch事件。

25.                  * 這種情況發生在Workspace變小時,也就是之前Workspace處於SPRING_LOADED狀態。

26.                  * 當出現這種情況時直接把當前的事件當作ACTION_DOWN進行處理。

27.                  * 反之,則通過determineScrollingStart()嘗試能夠進入滑動狀態。

28.                 if (mActivePointerId != INVALID_POINTER) //已經發生觸摸,獲取到觸點OWL

29.                     determineScrollingStart(ev);

30.                     break;

31.             case MotionEvent.ACTION_DOWN:

32.                 if(OWL_DEBUG) Log.d(OWL,"onInterceptTouchEvent  ACTION_DOWN...");

33.                 final float x = ev.getX();

34.                 final float y = ev.getY();

35.                 //記錄點擊的位置的座標以及觸點的記錄(多點觸摸識別)

36.                 mDownMotionX = x;

37.                 mLastMotionX = x;

38.                 mLastMotionY = y;

39.                 mLastMotionXRemainder = 0;

40.                 mTotalMotionX = 0;

41.                 mActivePointerId = ev.getPointerId(0); //第一個觸點,返回0

42.                 mAllowLongPress = true;

43.                 if(OWL_DEBUG) Log.d(OWL, "onInterceptTouchEventmDownMotionX="+mDownMotionX+"

44.  mLastMotionY="+mLastMotionY+"  mActivePointerId="+mActivePointerId);

45.                  * 判斷目前是真正滑動頁面還是已經滑動結束,如果是真正滑動會攔截消息

46.                 final int xDist = Math.abs(mScroller.getFinalX() -mScroller.getCurrX());

47.                 final boolean finishedScrolling = (mScroller.isFinished()|| xDist < mTouchSlop);

48.                 if (finishedScrolling)

49.                     if(OWL_DEBUG) Log.d(OWL,"onInterceptTouchEvent  finishedScrolling..");

50.                     mTouchState = TOUCH_STATE_REST;

51.                     mScroller.abortAnimation();

52.                 else //按下拖動應該屬於滑動狀態

53.                     if(OWL_DEBUG) Log.d(OWL,"onInterceptTouchEvent  TOUCH_STATE_SCROLLING..");

54.                     mTouchState =TOUCH_STATE_SCROLLING;

55.                 // 識別是否觸摸狀態是否是直接翻頁狀態,如果是直接翻頁,在onTouchEvent裏面會直接調用

56.                 // snapToPage()方法,跳到目標頁面

57.                 if (mTouchState != TOUCH_STATE_PREV_PAGE &&mTouchState != TOUCH_STATE_NEXT_PAGE)

58.                     if (getChildCount() > 0)

59.                         if(hitsPreviousPage(x, y))

60.                            if(OWL_DEBUG) Log.d(OWL,"onInterceptTouchEvent  hitsPreviousPage true...");

61.                             //點擊上一頁

62.                            mTouchState = TOUCH_STATE_PREV_PAGE;

63.                         else if(hitsNextPage(x, y))

64.                            if(OWL_DEBUG) Log.d(OWL, "onInterceptTouchEvent  hitsNextPagetrue...");

65.                            mTouchState = TOUCH_STATE_NEXT_PAGE;

66.                         if(OWL_DEBUG)Log.d(OWL, "onInterceptTouchEvent  no next or pre page..");

67.                 break;

68.  //不需要攔截觸摸消息

69.             case MotionEvent.ACTION_UP:

70.             case MotionEvent.ACTION_CANCEL:

71.                 if(OWL_DEBUG) Log.d(OWL, "onInterceptTouchEventACTION_UP || ACTION_CANCEL");

72.                 mTouchState = TOUCH_STATE_REST;

73.                 mAllowLongPress = false;

74.                 mActivePointerId = INVALID_POINTER;

75.                 releaseVelocityTracker();

76.                 break;

77.             case MotionEvent.ACTION_POINTER_UP:

78.                 if(OWL_DEBUG) Log.d(OWL, "onInterceptTouchEventACTION_POINTER_UP");

79.                 onSecondaryPointerUp(ev);

80.                 releaseVelocityTracker();

81.                 break;

82.         if(OWL_DEBUG) Log.d(OWL, "onInterceptTouchEventmTouchState="+mTouchState);

83.           * 只要是mTouchState的狀態不爲TOUCH_STATE_REST,那麼就進行事件攔截,調用onTouchEvent

84.          returnmTouchState != TOUCH_STATE_REST;

 

There are four states in PageView:

//滑動結束狀態

1.     protected final static int TOUCH_STATE_REST =0;

2.     //正在滑動

3.     protected final static intTOUCH_STATE_SCROLLING = 1;

4.     //滑動到上一頁

5.     protected final static intTOUCH_STATE_PREV_PAGE = 2;

6.     //滑動到下一頁

7.     protected final static intTOUCH_STATE_NEXT_PAGE = 3;

 

Except for TOUCH_STATE_REST state, the message will not passto the upper level. The message which is hold here will handle in onTouchEvent.

The code of dealing with data in onTouchEvent:

1.     public boolean onTouchEvent(MotionEvent ev)

2.            if(OWL_DEBUG) Log.d(OWL,"onTouchEvent  entering..");

3.     //........switch (action &MotionEvent.ACTION_MASK)

4.     //.........此處省略ACTION_DOWNACTION_MOVE

5.             caseMotionEvent.ACTION_UP:

6.                if(OWL_DEBUG) Log.d(OWL, "onTouchEvent  ACTION_UP..");

7.                if (mTouchState == TOUCH_STATE_SCROLLING)

8.                    if(OWL_DEBUG) Log.d(OWL,"onTouchEvent  ACTION_UP.. TOUCH_STATE_SCROLLING");

9.                    final int activePointerId = mActivePointerId;

10.                 final int pointerIndex =ev.findPointerIndex(activePointerId);

11.                 final float x = ev.getX(pointerIndex);

12.                 final VelocityTracker velocityTracker = mVelocityTracker;

13.                 velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);

14.                 //橫向速率

15.                 int velocityX = (int)velocityTracker.getXVelocity(activePointerId);

16.                 //移動距離

17.                 final int deltaX = (int) (x - mDownMotionX);

18.                 //頁面寬

19.                 final int pageWidth =getScaledMeasuredWidth(getPageAt(mCurrentPage));

20.                 //移動距離超過頁面寬40%

21.                 boolean isSignificantMove = Math.abs(deltaX) > pageWidth*SIGNIFICANT_MOVE_THRESHOLD;

22.                 final int snapVelocity = mSnapVelocity;

23.                 mTotalMotionX += Math.abs(mLastMotionX +mLastMotionXRemainder - x);

24.                 //根據滑動距離和速率,判斷是否是滑動

25.                 boolean isFling = mTotalMotionX > MIN_LENGTH_FOR_FLING&& Math.abs(velocityX) > snapVelocity;

26.  // 這鐘情況是頁面朝一個方向移動了一段距離,然後又彈回去了。

27.                 //我們使用一個閥值來判斷是進行翻頁還是返回到初始頁面

28.                 boolean returnToOriginalPage = false;

29.                 //Math.signum 函數將正數轉換爲 1.0,將負數轉換爲 -1.00 仍然是 0實際上,它只是提取一個數的符號

30.                 if (Math.abs(deltaX) > pageWidth *RETURN_TO_ORIGINAL_PAGE_THRESHOLD &&

31.                        Math.signum(velocityX) != Math.signum(deltaX) && isFling)

32.                     //返回當前頁面

33.                     returnToOriginalPage = true;

34.                 int finalPage;//頁面朝左移動

35.                 if (((isSignificantMove && deltaX > 0 &&!isFling) ||

36.                         (isFling &&velocityX > 0)) && mCurrentPage > 0)

37.                     if(OWL_DEBUG) Log.d(OWL,"onTouchEvent  ACTION_UP.. TOUCH_STATE_SCROLLINGLeftSnap");

38.                     finalPage = returnToOriginalPage ?mCurrentPage : mCurrentPage - 1;

39.                     snapToPageWithVelocity(finalPage,velocityX);

40.                 //頁面朝右移動

41.                 else if (((isSignificantMove && deltaX < 0&& !isFling) ||

42.                         (isFling &&velocityX < 0)) && mCurrentPage < getChildCount() - 1)

43.                     if(OWL_DEBUG) Log.d(OWL,"onTouchEvent  ACTION_UP.. TOUCH_STATE_SCROLLINGRightSnap");

44.                     finalPage = returnToOriginalPage ?mCurrentPage : mCurrentPage + 1;

45.                     snapToPageWithVelocity(finalPage,velocityX);

46.                     if(OWL_DEBUG) Log.d(OWL,"onTouchEvent  ACTION_UP.. TOUCH_STATE_SCROLLING BackOrigation");

47.                     //尋找離屏幕中心最近的頁面移動

48.                     snapToDestination();

49.             else if (mTouchState == TOUCH_STATE_PREV_PAGE)

50.                 if(OWL_DEBUG) Log.d(OWL,"onTouchEvent  ACTION_UP.. TOUCH_STATE_PREV_PAGE");

51.                 //直接切換到上一頁,沒有進行滑動

52.                 int nextPage = Math.max(0, mCurrentPage - 1);

53.                 if (nextPage != mCurrentPage) {

54.                     snapToPage(nextPage);

55.                 } else {

56.                     snapToDestination();

57.             else if (mTouchState == TOUCH_STATE_NEXT_PAGE)

58.                 if(OWL_DEBUG) Log.d(OWL,"onTouchEvent  ACTION_UP.. TOUCH_STATE_NEXT_PAGE");

59.                 //直接切換到下一頁

60.                 int nextPage = Math.min(getChildCount() - 1, mCurrentPage +1);

61.                 if (nextPage != mCurrentPage) {

62.                     snapToPage(nextPage);

63.                 } else {

64.                     snapToDestination();

65.                 if(OWL_DEBUG) Log.d(OWL,"onTouchEvent  ACTION_UP.. onUnhandledTap()");

66.  //這裏是空回調,繼承PagedView的方法,會重載這方法處理一些操作。

67.                 onUnhandledTap(ev);

68.             mTouchState = TOUCH_STATE_REST;

69.             mActivePointerId = INVALID_POINTER;

70.             releaseVelocityTracker();

71.             break;return true;

 

We can see the ACTION_UP in onTouchEvent handle 3conditions, map to TOUCH_STATE_SCROLLING, TOUCH_STATE_PREV_PAGE, andTOUCH_STATE_NEXT_PAGE.

When we slip to right , the logcat will print the info.First, start the onInterceptTouchEvent(), then change the state of mTouchStatefrom TOUCH_STATE_REST to TOUCH_STATE_SCROLLING. Last , callsnapToPageWithVelocity() and turn to next page.

The code in snapToPageWithVelocity:

1.     protected void snapToPageWithVelocity(intwhichPage, int velocity)

2.            //..............

3.             //根據滑動的速度,計算一個切換動畫的顯示時間,速度越快,動畫顯示時間越短。

4.             duration= 4 * Math.round(1000 * Math.abs(distance / velocity));

5.             //跳轉到指定頁面。

6.             snapToPage(whichPage,delta, duration);

 

The snapToPage is called in the part, and add one more wayto handle the speed of slipping.  TheVelocityTracker will record the speed of slipping.  The speed will determine the time to show theanimation of changing.

1.     protected void snapToPage(int whichPage, intdelta, int duration)

2.            mNextPage = whichPage;

3.            //............

4.     //切換的時候調用ScrollerstartScroll進行切換

5.             if(!mScroller.isFinished()) mScroller.abortAnimation();

6.            mScroller.startScroll(mUnboundedScrollX, 0, delta, 0,duration);

7.            notifyPageSwitchListener();

8.            invalidate();

 

When system calls the startScroll(), the computeScroll()will also be called. And in computeScroll(), the scrollTo() will scroll thescreen, and call invalidate() to refresh the screen, create the effect ofanimation and scroll page.

1.     protected boolean computeScrollHelper()

2.     //computeScrolloffset用來計算滑動是否結束,當你調用startScroll()方法的時候這個方法返回的值一直都爲true

3.     //如果採用其它方式移動視圖比如:scrollTo() scrollBy時那麼這個方法返回false

4.             if(mScroller.computeScrollOffset())

5.                if (mScrollX != mScroller.getCurrX() || mScrollY != mScroller.getCurrY())

6.                    scrollTo(mScroller.getCurrX(), mScroller.getCurrY());

7.       //刷新界面

8.                invalidate();

9.                return true;

10.          //.............

11.          returnfalse;

 

The scrollTo is the method of View class, PagedView rewriteit for judging the position, update the X&Y coordinate.

 

     

 

 

AppsCustomizeTabHost

In the Android 4.0 launche, the widget is added.

AllApp list

The configuration of AllApp list is in the file :\res\Layout\apps_customize_pane.xml

It uses TabHost to organize 2 pages(AllApp and widget), whichcan be switched by the TabHost on the screen.

 

1.     <!-- 取消TabHost的顯示,把TabHost設置爲0dp高,避免影響all app顯示 mythou -->

2.     <com.android.launcher2.AppsCustomizeTabHost

3.        android:background="@android:color/transparent">

4.         <LinearLayout

5.            android:id="@+id/apps_customize_content"

6.            android:orientation="vertical"

7.            android:layout_width="match_parent"

8.            android:layout_height="match_parent">

9.             <!--TabHost欄,配置TahHost欄的高度寬度

10.          我這裏把TabHost取消了,因爲我的Launcher需要把Widget反正workspace裏面實現,

11.          所以全部應用列表修改成和2.XLauncher一樣 mythou-->

12.         <FrameLayout

13.             android:id="@+id/tabs_container"

14.             android:layout_width="0dp"

15.             android:layout_height="0dp"

16.             android:layout_marginTop="0dp"

17.             android:layout_gravity="center_horizontal">

18.  <!-- TabHost上面Widget 的按鈕-->

19.             <com.android.launcher2.FocusOnlyTabWidget

20.                 android:id="@android:id/tabs"

21.                 android:layout_width="match_parent"

22.                 android:layout_height="match_parent"

23.                 android:layout_gravity="left"

24.                 android:background="@android:color/transparent"

25.                 android:tabStripEnabled="false"

26.                 android:tabStripLeft="@null"

27.                 android:tabStripRight="@null"

28.                 android:divider="@null" />

29.  <!--TabHost 右邊的Android市場的圖標,不需要可以去掉-->

30.             <include

31.                 android:id="@+id/market_button"

32.                 layout="@layout/market_button"

33.                 android:layout_width="wrap_content"

34.                 android:layout_height="match_parent"

35.                 android:layout_gravity="right" />

36.         </FrameLayout>

37.  <!--下面這裏就是我們所有應用列表的選項和所有應用列表的顯示View

38.          需要注意的是AppsCustomizePagedView同時支持顯示所有應用列表和Widget列表 mythou-->

39.         <FrameLayout

40.             android:id="@android:id/tabcontent"

41.             android:layout_width="match_parent"

42.             android:layout_height="match_parent"

43.  <!-- 所有應用列表是通過自定義VIewAppsCustomizePagedView顯示,後面會詳細分析這個View

44.            下面只對部分重要屬性加入註釋-->

45.             <com.android.launcher2.AppsCustomizePagedView

46.                 android:id="@+id/apps_customize_pane_content"

47.                 android:layout_width="match_parent"

48.                 android:layout_height="match_parent"

49.  //MaxAppCellCountX MaxAppCellCounY指的是所有App圖標排列的最大行列數。

50.  //一般設置爲-1,表示無限制

51.                 launcher:maxAppCellCountX="@integer/apps_customize_maxCellCountX"

52.                launcher:maxAppCellCountY="@integer/apps_customize_maxCellCountY"

53.  //pageLayoutWidthGappageLayoutHeightGap分別表示菜單界面與屏幕邊緣的距離,

54.  //一般小屏幕這裏設置爲-1。避免邊框太窄誤觸屏幕才需要設置。

55.                 launcher:pageLayoutWidthGap="@dimen/apps_customize_pageLayoutWidthGap"

56.                launcher:pageLayoutHeightGap="@dimen/apps_customize_pageLayoutHeightGap"

57.                 launcher:pageLayoutPaddingTop="50dp"

58.  //pageLayoutPaddingXXX指的是內填充,這個和系統的padding一樣

59.                 launcher:pageLayoutPaddingBottom="@dimen/apps_customize_pageLayoutPaddingBottom"

60.                launcher:pageLayoutPaddingLeft="@dimen/apps_customize_pageLayoutPaddingLeft"

61.                launcher:pageLayoutPaddingRight="@dimen/apps_customize_pageLayoutPaddingRight"

62.  //widgetCellWithGapwidgetCellHeightGap指的是widget列表界面各個widget之間的間隔,

63.  //和系統的margin屬性類似

64.                launcher:widgetCellWidthGap="@dimen/apps_customize_widget_cell_width_gap"

65.                 launcher:widgetCellHeightGap="@dimen/apps_customize_widget_cell_height_gap"

66.  //widgetCountXWidgetCountY都是表示Widget界面每行每列顯示多少Widget

67.                launcher:widgetCountX="@integer/apps_customize_widget_cell_count_x"

68.                 launcher:widgetCountY="@integer/apps_customize_widget_cell_count_y"

69.  //提示界面的焦點

70.                launcher:clingFocusedX="@integer/apps_customize_cling_focused_x"

71.                launcher:clingFocusedY="@integer/apps_customize_cling_focused_y"

72.                 launcher:maxGap="@dimen/workspace_max_gap" />

73.  <!-- 加載全部應用時的旋轉動畫 -->

74.             <FrameLayout

75.                 android:id="@+id/animation_buffer"

76.                 android:layout_width="match_parent"

77.                 android:layout_height="match_parent"

78.                 android:background="#FF000000"

79.                 android:visibility="gone" />

80.  <!-- 分頁符,代表多少頁和當前頁面-->

81.             <include

82.                 android:id="@+id/paged_view_indicator"

83.                 layout="@layout/scroll_indicator"

84.                 android:layout_width="wrap_content"

85.                 android:layout_height="wrap_content"

86.                 android:layout_gravity="bottom" />

87.         </FrameLayout>

88.      </LinearLayout>

89.  <!--第一次進入所有應用列表的提示界面,和workspace提示界面一樣-->

90.      <includelayout="@layout/all_apps_cling"

91.         android:id="@+id/all_apps_cling"

92.         android:layout_width="match_parent"

93.         android:layout_height="match_parent"/>

94.  </com.android.launcher2.AppsCustomizeTabHost>

 

All the list of apps and widget is showed byAppsCustomizedPagedView. 

AppsCustomizedTabHost

 

AppsCustomizedTabHost is inherited by TabHost class forexpand the TabHost and adding some function.

1.     protected void onFinishInflate()

2.            //.......//創建所有應用列表Tab mythou

3.             TextViewtabView;

4.             Stringlabel;

5.             label =mContext.getString(R.string.all_apps_button_label);

6.             tabView= (TextView) mLayoutInflater.inflate(R.layout.tab_widget_indicator, tabs,false);

7.            tabView.setText(label);

8.            tabView.setContentDescription(label);

9.            addTab(newTabSpec(APPS_TAB_TAG).setIndicator(tabView).setContent(contentFactory));

10.          //WidgetTab頁面

11.          label =mContext.getString(R.string.widgets_tab_label);

12.          tabView= (TextView) mLayoutInflater.inflate(R.layout.tab_widget_indicator, tabs,false);

13.         tabView.setText(label);

14.         tabView.setContentDescription(label);

15.         addTab(newTabSpec(WIDGETS_TAB_TAG).setIndicator(tabView).setContent(contentFactory));

16.          //設置監聽器

17.         setOnTabChangedListener(this);

18.         AppsCustomizeTabKeyEventListener keyListener = new AppsCustomizeTabKeyEventListener();

19.          ViewlastTab = tabs.getChildTabViewAt(tabs.getTabCount() - 1);

20.         lastTab.setOnKeyListener(keyListener);

21.         //Android商店按鈕

22.          ViewshopButton = findViewById(R.id.market_button);

23.         shopButton.setOnKeyListener(keyListener);

24.          // Hidethe tab bar until we measure

25.         mTabsContainer.setAlpha(0f);

 

onFInishInflate function creates Tab View for TabHost.

onTabChanged runs when Tab changed.

When TabHost switch the items, it runs onTabChangedfunction. This function will switch the AppsCustomizedPagedView class. All theapps and widget will show by AppsCustomizedPagedView.

 

1.     public void onTabChanged(String tabId)

2.     //使用Runnable執行一個切換的動畫效果,因爲切換的時候會存在數據加載導致的延時問題。

3.     //在加載切換數據的過程中,加入動畫可以增強用戶體驗 mythou

4.             post(newRunnable()

5.                @Override

6.                public void run()

7.                    ArrayList<View> visiblePages = newArrayList<View>();

8.                    for (int i = visiblePageRange[0]; i <=visiblePageRange[1]; i++)

9.                        visiblePages.add(mAppsCustomizePane.getPageAt(i));

10.                 //保證每個頁面都是使用統一的動畫效果

11.                 mAnimationBuffer.scrollTo(mAppsCustomizePane.getScrollX(),0);

12.                 // mAppsCustomizePane顯示子頁面是使用相反的順序,所以添加頁面動畫的時候,

13.                 //也是使用相反的添加順序

14.                 for (int i = visiblePages.size() - 1; i >= 0; i--)

15.                     View child = visiblePages.get(i);

16.  //增加切換動畫緩存,提供下面切換動畫使用

17.                     mAnimationBuffer.addView(child, p);

18.                 // Toggle the new content

19.                 onTabChangedStart();

20.                 onTabChangedEnd(type);

21.                 //過渡動畫開始

22.                 ObjectAnimator outAnim =ObjectAnimator.ofFloat(mAnimationBuffer, "alpha", 0f);

23.  //。。。。。。。。

 

onTabChanged offered one animation of switching. It willcost some time to load and switch data, so the animation created by one threadwill executed.

onLauncherTransitionStart and onLauncherTransitionEnd

These two function is called byLauncher.java.  They will run when switchworkspace to AllApp list. Before this switch, it will callonLauncherTransitionStart, and after the switch, it will callonLauncherTransitionEnd.

 

PagedViewWithDraggableItems

The relationship of AppsCustomizedPagedView inherited.

 

Touch intercept

PagedViewWithDraggableItem is inherited by PagedView.PagedView is for screen switch, and it will intercepted the info about pageswitch in onInterceptTouchEvent(), then deal with it in onTouchEvent().

The code about PagedViewWithDraggableItems. (intercept andhandle  info)

1.    //Edited by mythou

2.    //http://www.cnblogs.com/mythou/

3.        @Override

4.        public booleanonInterceptTouchEvent(MotionEvent ev)

5.           if(OWL_DEBUG) Log.d(OWL, "enter- onInterceptTouchEvent");

6.           handleTouchEvent(ev);

7.            returnsuper.onInterceptTouchEvent(ev);

8.        @Override

9.        public booleanonTouchEvent(MotionEvent ev)

10.         if(OWL_DEBUG) Log.d(OWL, "enter- onTouchEvent ");

11.         handleTouchEvent(ev);

12.          returnsuper.onTouchEvent(ev);

 

The onlnterceptTouchEvent and onTouchEvent is simple inPagedViewWithDraggableItems, and both of them called the same way to deal withit.  The screen will callonInterceptTouchEvnet of PageViewWithDraggableItem() first, and then callPagedView to handle.

handleTouchEvent

 

1.    //Edited by mythou

2.    //http://www.cnblogs.com/mythou/

3.    private void handleTouchEvent(MotionEvent ev)

4.            finalint action = ev.getAction();

5.           if(OWL_DEBUG) Log.d(OWL, "handleTouchEventaction="+(action & MotionEvent.ACTION_MASK)+

6.                   " mTouchState="+mTouchState+" mIsDragging="+mIsDragging+"mIsDragEnabled="+mIsDragEnabled);

7.            switch(action & MotionEvent.ACTION_MASK)

8.               case MotionEvent.ACTION_DOWN: //按下事件處理

9.                   cancelDragging();

10.                 mIsDragEnabled = true;

11.                 break;

12.             case MotionEvent.ACTION_MOVE: //進入滑動狀態

13.                 if (mTouchState != TOUCH_STATE_SCROLLING &&!mIsDragging && mIsDragEnabled)

14.  //根據是否進入滾動狀態,判斷是否需要拖曳按鈕

15.                     if(OWL_DEBUG) Log.d(OWL,"handleTouchEvent--->before drag ");

16.                     determineDraggingStart(ev);

17.                     if(OWL_DEBUG) Log.d(OWL,"handleTouchEvent--->Not drag ");

18.                 break;

 

There are two states about touch:ACITON_DOWN andACITON_MOVE.                ACTION_DOWNwill handle the mark about drag the icon. And the ACTION_MOVIE will judge thescrolling state by TouchState. If not, it will run drag icon. TouchState isjudged the states by PagedView.

When we slip the screen, the system will callPagedViewWithDraggableItems’ onInterceptTouchEvent and call the code byorder.  The system will run handleTouchEvent’sACTION_MOVE, and then call determineDraggingStart(). Not the  PagedViewWithDraggableItems’sdetermineDraggingPagedView. Because the function in AppsCustomizedPagedView isempty and AppsCustomizedPagedView rewrite it.

Drag icon

 

If you drag one icon, the system will determine it is thelong click.

1.    //Edited by mythou

2.    //http://www.cnblogs.com/mythou/

3.      @Override

4.        public boolean onLongClick(Viewv)

5.           if(OWL_DEBUG) Log.d(OWL, "onLongClick Enter");

6.            //下面有幾種情況會取消長按觸摸,不是觸摸狀態,正在動畫過渡,離開了allAPP頁面

7.            if(!v.isInTouchMode()) return false;

8.            //Return early if we are still animating the pages

9.            if(mNextPage != INVALID_PAGE) return false;

10.          // Whenwe have exited all apps or are in transition, disregard long clicks

11.          if(!mLauncher.isAllAppsCustomizeOpen() ||

12.                 mLauncher.getWorkspace().isSwitchingState()) return false;

13.         if(OWL_DEBUG) Log.d(OWL, "onLongClickbeginDragging()");

14.          //調用開始拖曳的設置,裏面會設置一些標記

15.        return beginDragging(v);

 

The onLongClick() of PagedViewDraggableItems is just runsimple mark configuration.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

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