Android中ICS4.0Launcher中Fold的功能详解

  AndroidICS4.0的文件夹和2.3的文件夹区别比较大,主要区别有:

       一、android2.3的文件夹大小是固定的,4.0的文件夹大小是按照里面的元素大小决定的。

       二、android2.3的文件夹图标是固定的文件夹的形式展示的,而4.0是从文件中取前3个的缩略图垂直展示在屏幕上的。估计谷歌怕侵犯苹果文件夹的知识产权,所以没有做成和苹果一样的效果。

       三、android2.3的文件中可以放多于16的应用程序的快捷方式,而4.0最多只能放16个快捷方式。

       四、android2.3的文件夹中的图标不可以交换位置,而4.0的文件夹中的图标可以相互交换位置。

转载请标明出处:http://blog.csdn.net/wdaming1986/article/details/7748738

 对比图如下:

                      android2.3的文件夹                                                                android4.0的文件夹

                    Android中ICS4.0Launcher中Fold的功能详解_第1张图片                          Android中ICS4.0Launcher中Fold的功能详解_第2张图片

 

下面来看看4.0的代码怎么实现文件夹的:

 

Step 1:如果系统一开始有fold,一启动launcher的时候,在Launcher.java类中bindFolders回调方法中:

[java] view plain copy print ?
  1. /** 
  2.     * Implementation of the method from LauncherModel.Callbacks. 
  3.     */  
  4.    public void bindFolders(HashMap<Long, FolderInfo> folders) {  
  5.        setLoadOnResume();  
  6.        sFolders.clear();  
  7.        sFolders.putAll(folders);  
  8.    }  

绑定所有fold的对象交给sFolders,去处理。
private static HashMap<Long, FolderInfo> sFolders = new HashMap<Long, FolderInfo>();

 

Step 2:如果是把一个图标拖放到另一图标上面,也形成folder。具体流程如下:

1、首先在workspace中的onDrop()方法中会判断是否会形成一个fold。代码如下:

[java] view plain copy print ?
  1. public void onDrop(DragObject d) {  
  2.   
  3.     ...  ...   
  4.   
  5.     // If the item being dropped is a shortcut and the nearest drop  
  6.                 // cell also contains a shortcut, then create a folder with the two shortcuts.  
  7.                 if (!mInScrollArea && createUserFolderIfNecessary(cell, container,  
  8.                         dropTargetLayout, mTargetCell, false, d.dragView, null)) {  
  9.                     return;  
  10.                 }  
  11.   
  12.     ... ...  
  13. }  


2、在Workspace.java类的createUserFolderIfNecessary()方法中来增加fold,具体代码如下:

[java] view plain copy print ?
  1. boolean createUserFolderIfNecessary(View newView, long container, CellLayout target,  
  2.             int[] targetCell, boolean external, DragView dragView, Runnable postAnimationRunnable) {  
  3.   
  4.     。。。 。。。  
  5.  FolderIcon fi =  
  6.                 mLauncher.addFolder(target, container, screen, targetCell[0], targetCell[1]);  
  7.             destInfo.cellX = -1;  
  8.             destInfo.cellY = -1;  
  9.             sourceInfo.cellX = -1;  
  10.             sourceInfo.cellY = -1;  
  11.   
  12.   。。。 。。。  
  13. }  

通过mLauncher.addFolder来传递folder的信息,包含一些位置信息绑定哪个屏幕的。


 

3、在Launcher.java类的addFolder()这个方法是真正形成folder的,以及在launcher的数据库中插入一条信息,代码如下:

[java] view plain copy print ?
  1. FolderIcon addFolder(CellLayout layout, long container, final int screen, int cellX,  
  2.            int cellY) {  
  3.        final FolderInfo folderInfo = new FolderInfo();  
  4.        folderInfo.title = getText(R.string.folder_name);  
  5.   
  6.        // Update the model  
  7.        LauncherModel.addItemToDatabase(Launcher.this, folderInfo, container, screen, cellX, cellY,  
  8.                false);  
  9.        sFolders.put(folderInfo.id, folderInfo);  
  10.   
  11.        // Create the view  
  12.        FolderIcon newFolder =  
  13.            FolderIcon.fromXml(R.layout.folder_icon, this, layout, folderInfo, mIconCache);  
  14.        mWorkspace.addInScreen(newFolder, container, screen, cellX, cellY, 11,  
  15.                isWorkspaceLocked());  
  16.        return newFolder;  
  17.    }  

FolderIcon.fromXml()这个方法是从xml中形成folder,addInScreen(),把相应的信息插入数据库。

 

4、在FolderIcon.java中fromXml()方法中的代码如下:

 

[java] view plain copy print ?
  1. static FolderIcon fromXml(int resId, Launcher launcher, ViewGroup group,  
  2.             FolderInfo folderInfo, IconCache iconCache) {  
  3.   
  4.         if (INITIAL_ITEM_ANIMATION_DURATION >= DROP_IN_ANIMATION_DURATION) {  
  5.             throw new IllegalStateException("DROP_IN_ANIMATION_DURATION must be greater than " +  
  6.                     "INITIAL_ITEM_ANIMATION_DURATION, as sequencing of adding first two items " +  
  7.                     "is dependent on this");  
  8.         }  
  9.   
  10.         FolderIcon icon = (FolderIcon) LayoutInflater.from(launcher).inflate(resId, group, false);  
  11.   
  12.         icon.mFolderName = (BubbleTextView) icon.findViewById(R.id.folder_icon_name);  
  13.         icon.mFolderName.setText(folderInfo.title);  
  14.         icon.mPreviewBackground = (ImageView) icon.findViewById(R.id.preview_background);  
  15.   
  16.         icon.setTag(folderInfo);  
  17.         icon.setOnClickListener(launcher);  
  18.         icon.mInfo = folderInfo;  
  19.         icon.mLauncher = launcher;  
  20.         icon.setContentDescription(String.format(launcher.getString(R.string.folder_name_format),  
  21.                 folderInfo.title));  
  22.         Folder folder = Folder.fromXml(launcher);  
  23.         folder.setDragController(launcher.getDragController());  
  24.         folder.setFolderIcon(icon);  
  25.         folder.bind(folderInfo);  
  26.         icon.mFolder = folder;  
  27.   
  28.         icon.mFolderRingAnimator = new FolderRingAnimator(launcher, icon);  
  29.         folderInfo.addListener(icon);  
  30.   
  31.         return icon;  
  32.     }  

     Folder folder = Folder.fromXml(launcher);是真正产生了一个folder对象。代码如下:

[java] view plain copy print ?
  1. /** 
  2.     * Creates a new UserFolder, inflated from R.layout.user_folder. 
  3.     * 
  4.     * @param context The application's context. 
  5.     * 
  6.     * @return A new UserFolder. 
  7.     */  
  8.    static Folder fromXml(Context context) {  
  9.        return (Folder) LayoutInflater.from(context).inflate(R.layout.user_folder, null);  
  10.    }  

 

并且给folder设置拖拽的控制器,绑定folderInfo设置folderInfo.addListener(icon)图标改变的监听。
这个接口 interface  FolderListener定义了一个方法---->如下:

[java] view plain copy print ?
  1. interface FolderListener {  
  2.      public void onAdd(ShortcutInfo item);  
  3.      public void onRemove(ShortcutInfo item);  
  4.      public void onTitleChanged(CharSequence title);  
  5.      public void onItemsChanged();  
  6.  }  

 

在folder.bind(folderInfo);方法中的操作如下:

 

[java] view plain copy print ?
  1. void bind(FolderInfo info) {  
  2.     mInfo = info;  
  3.     ArrayList<ShortcutInfo> children = info.contents;  
  4.     ArrayList<ShortcutInfo> overflow = new ArrayList<ShortcutInfo>();  
  5.     setupContentForNumItems(children.size());  
  6.     int count = 0;  
  7.     for (int i = 0; i < children.size(); i++) {  
  8.         ShortcutInfo child = (ShortcutInfo) children.get(i);  
  9.         if (!createAndAddShortcut(child)) {  
  10.             overflow.add(child);  
  11.         } else {  
  12.             count++;  
  13.         }  
  14.     }  
  15.   
  16.     // We rearrange the items in case there are any empty gaps  
  17.     setupContentForNumItems(count);  
  18.   
  19.     // If our folder has too many items we prune them from the list. This is an issue   
  20.     // when upgrading from the old Folders implementation which could contain an unlimited  
  21.     // number of items.  
  22.     for (ShortcutInfo item: overflow) {  
  23.         mInfo.remove(item);  
  24.         LauncherModel.deleteItemFromDatabase(mLauncher, item);  
  25.     }  
  26.   
  27.     mItemsInvalidated = true;  
  28.     updateTextViewFocus();  
  29.     mInfo.addListener(this);  
  30.   
  31.     if (!sDefaultFolderName.contentEquals(mInfo.title)) {  
  32.         mFolderName.setText(mInfo.title);  
  33.     } else {  
  34.         mFolderName.setText("");  
  35.     }  
  36. }  

主要的操作是:给拖拽进来的快捷方式安排位置,判断Folder是否已经放满,设置监听,设置folder的Name;

 

5、folder桌面的缩略图怎么形成的,是在第2步Workspace.java的createUserFolderIfNecessary()方法中

[java] view plain copy print ?
  1. // If the dragView is null, we can't animate  
  2.             boolean animate = dragView != null;  
  3.             if (animate) {  
  4.                 fi.performCreateAnimation(destInfo, v, sourceInfo, dragView, folderLocation, scale,  
  5.                         postAnimationRunnable);  
  6.             } else {  
  7.                 fi.addItem(destInfo);  
  8.                 fi.addItem(sourceInfo);  
  9.             }  

fi.performCreateAnimation()这个方法是给folder添加个动画。

 

6、在FolderIcon.java中的performCreateAnimation()方法中:

[java] view plain copy print ?
  1. public void performCreateAnimation(final ShortcutInfo destInfo, final View destView,  
  2.            final ShortcutInfo srcInfo, final View srcView, Rect dstRect,  
  3.            float scaleRelativeToDragLayer, Runnable postAnimationRunnable) {  
  4.   
  5.        Drawable animateDrawable = ((TextView) destView).getCompoundDrawables()[1];  
  6.        computePreviewDrawingParams(animateDrawable.getIntrinsicWidth(), destView.getMeasuredWidth());  
  7.   
  8.        // This will animate the dragView (srcView) into the new folder  
  9.        onDrop(srcInfo, srcView, dstRect, scaleRelativeToDragLayer, 1, postAnimationRunnable);  
  10.   
  11.        // This will animate the first item from it's position as an icon into its  
  12.        // position as the first item in the preview  
  13.        animateFirstItem(animateDrawable, INITIAL_ITEM_ANIMATION_DURATION);  
  14.   
  15.        postDelayed(new Runnable() {  
  16.            public void run() {  
  17.                addItem(destInfo);  
  18.            }  
  19.        }, INITIAL_ITEM_ANIMATION_DURATION);  
  20.    }  

computePreviewDrawingParams()这个方法是计算绘制folder图标的方法;

 

7、在FolderIcon.java类中的computePreviewItemDrawingParams()方法中:

[java] view plain copy print ?
  1. private PreviewItemDrawingParams computePreviewItemDrawingParams(int index,  
  2.            PreviewItemDrawingParams params) {  
  3.        index = NUM_ITEMS_IN_PREVIEW - index - 1;  
  4.        float r = (index * 1.0f) / (NUM_ITEMS_IN_PREVIEW - 1);  
  5.        float scale = (1 - PERSPECTIVE_SCALE_FACTOR * (1 - r));  
  6.   
  7.        float offset = (1 - r) * mMaxPerspectiveShift;  
  8.        float scaledSize = scale * mBaselineIconSize;  
  9.        float scaleOffsetCorrection = (1 - scale) * mBaselineIconSize;  
  10.   
  11.        // We want to imagine our coordinates from the bottom left, growing up and to the  
  12.        // right. This is natural for the x-axis, but for the y-axis, we have to invert things.  
  13.        float transY = mAvailableSpaceInPreview - (offset + scaledSize + scaleOffsetCorrection);  
  14.        float transX = offset + scaleOffsetCorrection;  
  15.        float totalScale = mBaselineIconScale * scale;  
  16.        final int overlayAlpha = (int) (80 * (1 - r));  
  17.   
  18.        if (params == null) {  
  19.            params = new PreviewItemDrawingParams(transX, transY, totalScale, overlayAlpha);  
  20.        } else {  
  21.            params.transX = transX;  
  22.            params.transY = transY;  
  23.            params.scale = totalScale;  
  24.            params.overlayAlpha = overlayAlpha;  
  25.        }  
  26.        return params;  
  27.    }  

主要工作是:计算图标的排列,每一个相对上一个有点偏移的距离;效果图如下:
                                                  Android中ICS4.0Launcher中Fold的功能详解_第3张图片
以上基本是把Step 2流程大致过了一遍。

 

Step 3:folder类中的长按事件的传递,以及Fold中长按交换位置的流程过一下:

1、先来说给Folder设置长按监听的地方,因为Folder.java类是继承了View.OnClickListener,
        View.OnLongClickListener事件,所以,长按事件就交给自己的onLongClick()事件来处理。

 

2、所以Folder中的长按事件,被自己的public boolean onLongClick(View v) {}时间捕获,代码如下:

[java] view plain copy print ?
  1. public boolean onLongClick(View v) {  
  2.   
  3.   ...  ...  
  4.    mLauncher.getWorkspace().onDragStartedWithItem(v);  
  5.             mLauncher.getWorkspace().beginDragShared(v, this);  
  6.             mIconDrawable = ((TextView) v).getCompoundDrawables()[1];  
  7.   
  8.             mCurrentDragInfo = item;  
  9.             mEmptyCell[0] = item.cellX;  
  10.             mEmptyCell[1] = item.cellY;  
  11.             mCurrentDragView = v;  
  12.   
  13.             mContent.removeView(mCurrentDragView);  
  14.             mInfo.remove(mCurrentDragInfo);  
  15.             mDragInProgress = true;  
  16.             mItemAddedBackToSelfViaIcon = false;  
  17.   
  18.    ...  ...  
  19. }  

同样拖拽事件是交给Workspace来处理,最后也是统一交给DragController.java类处理和分发相应的事件。这个过程在
Android-->Launcher拖拽事件详解【androidICS4.0--Launcher系列二】中做了详细的介绍,这里就不做赘述了。

 

3、主要看Folder.java类中的onDragOver()这个方法,当在文件夹中拖拽到另一个快捷方式的上面的时候,发生交换,

来看代码如下:

[java] view plain copy print ?
  1. public void onDragOver(DragObject d) {  
  2.        float[] r = getDragViewVisualCenter(d.x, d.y, d.xOffset, d.yOffset, d.dragView, null);  
  3.        mTargetCell = mContent.findNearestArea((int) r[0], (int) r[1], 11, mTargetCell);  
  4.        if (mTargetCell[0] != mPreviousTargetCell[0] || mTargetCell[1] != mPreviousTargetCell[1]) {  
  5.            mReorderAlarm.cancelAlarm();  
  6.            mReorderAlarm.setOnAlarmListener(mReorderAlarmListener);  
  7.            mReorderAlarm.setAlarm(150);  
  8.            mPreviousTargetCell[0] = mTargetCell[0];  
  9.            mPreviousTargetCell[1] = mTargetCell[1];  
  10.        }  
  11.    }  

这个方法主要做的操作是:判断拖拽的是哪个对象mContent.findNearestArea((int) r[0], (int) r[1], 1, 1, mTargetCell);判断在

哪个目标的附近,然后判断和是否是正在拖拽的对象的坐标,设置mReorderAlarmListener来进行交换,设置150毫秒用来处理动画的。

 

4、在ReorderAlarmListener内部类的代码如下:

[java] view plain copy print ?
  1. OnAlarmListener mReorderAlarmListener = new OnAlarmListener() {  
  2.       public void onAlarm(Alarm alarm) {  
  3.           realTimeReorder(mEmptyCell, mTargetCell);  
  4.       }  
  5.   };  

 

5、在realTimeReorder()方法中传递这个快捷方式在屏幕x轴,y轴上的为止,进行交换,代码如下:

[java] view plain copy print ?
  1. private void realTimeReorder(int[] empty, int[] target) {  
  2.        boolean wrap;  
  3.        int startX;  
  4.        int endX;  
  5.        int startY;  
  6.        int delay = 0;  
  7.        float delayAmount = 30;  
  8.        if (readingOrderGreaterThan(target, empty)) {  
  9.            wrap = empty[0] >= mContent.getCountX() - 1;  
  10.            startY = wrap ? empty[1] + 1 : empty[1];  
  11.            for (int y = startY; y <= target[1]; y++) {  
  12.                startX = y == empty[1] ? empty[0] + 1 : 0;  
  13.                endX = y < target[1] ? mContent.getCountX() - 1 : target[0];  
  14.                for (int x = startX; x <= endX; x++) {  
  15.                    View v = mContent.getChildAt(x,y);  
  16.                    if (mContent.animateChildToPosition(v, empty[0], empty[1],  
  17.                            REORDER_ANIMATION_DURATION, delay)) {  
  18.                        empty[0] = x;  
  19.                        empty[1] = y;  
  20.                        delay += delayAmount;  
  21.                        delayAmount *= 0.9;  
  22.                    }  
  23.                }  
  24.            }  
  25.        } else {  
  26.            wrap = empty[0] == 0;  
  27.            startY = wrap ? empty[1] - 1 : empty[1];  
  28.            for (int y = startY; y >= target[1]; y--) {  
  29.                startX = y == empty[1] ? empty[0] - 1 : mContent.getCountX() - 1;  
  30.                endX = y > target[1] ? 0 : target[0];  
  31.                for (int x = startX; x >= endX; x--) {  
  32.                    View v = mContent.getChildAt(x,y);  
  33.                    if (mContent.animateChildToPosition(v, empty[0], empty[1],  
  34.                            REORDER_ANIMATION_DURATION, delay)) {  
  35.                        empty[0] = x;  
  36.                        empty[1] = y;  
  37.                        delay += delayAmount;  
  38.                        delayAmount *= 0.9;  
  39.                    }  
  40.                }  
  41.            }  
  42.        }  
  43.    }  

readingOrderGreaterThan()这个方法的作用是判断是从上往下拖动,还是从下往上拖动,这两种情况的交换方式不一样。循环也就不一样。交换的过程中通过animateChildToPosition();这个方法设置了一个动画。

 

6、在放下的时候会触发Folder.java的onDrop()方法,

[java] view plain copy print ?
  1. public void onDrop(DragObject d) {  
  2.        ShortcutInfo item;  
  3.   
  4.   。。。  。。。  
  5. f (d.dragView.hasDrawn()) {  
  6.                mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, mCurrentDragView);  
  7.            } else {  
  8.                mCurrentDragView.setVisibility(VISIBLE);  
  9.            }  
  10.   
  11.  mInfo.add(item); 。。。  。。。   

作用是设置放下的view可见,把当前的这个快捷方式添加到mInfo中。

 

7、当执行完onDrop()方法后会走onDropCompleted()方法:

[java] view plain copy print ?
  1. public void onDropCompleted(View target, DragObject d, boolean success) {  
  2.   ... ...  
  3.  // Reordering may have occured, and we need to save the new item locations. We do this once  
  4.         // at the end to prevent unnecessary database operations.  
  5.         updateItemLocationsInDatabase();  
  6.   ...  ...  
  7. }  

这个方法的作用是更新item的位置信息在数据库中。

[java] view plain copy print ?
  1. private void updateItemLocationsInDatabase() {  
  2.         ArrayList<View> list = getItemsInReadingOrder();  
  3.         for (int i = 0; i < list.size(); i++) {  
  4.             View v = list.get(i);  
  5.             ItemInfo info = (ItemInfo) v.getTag();  
  6.             LauncherModel.moveItemInDatabase(mLauncher, info, mInfo.id, 0,  
  7.                         info.cellX, info.cellY);  
  8.         }  
  9.     }  

好了,folder的大致流程就是这些,更详细的请参考launcher源代码。

写的仓促,欢迎大家指出里面的错误,如果有不解的欢迎留言!

你可能感兴趣的:(java,数据库,android,null,animation,fold)