现在的位置: 首页 > 综合 > 正文

Android中ICS4.0Launcher中Fold的功能详解【androidICS4.0–>Launcher系列三】

2013年08月12日 ⁄ 综合 ⁄ 共 19463字 ⁄ 字号 评论关闭

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的文件夹

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

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

  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。代码如下:

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

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

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

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


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

  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. // Update the model
  6. LauncherModel.addItemToDatabase(Launcher.this, folderInfo, container, screen, cellX, cellY,
  7. false);
  8. sFolders.put(folderInfo.id, folderInfo);
  9. // Create the view
  10. FolderIcon newFolder =
  11. FolderIcon.fromXml(R.layout.folder_icon, this, layout, folderInfo, mIconCache);
  12. mWorkspace.addInScreen(newFolder, container, screen, cellX, cellY,
    1, 1,
  13. isWorkspaceLocked());
  14. return newFolder;
  15. }

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

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

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

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

  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定义了一个方法---->如下:

  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);方法中的操作如下:

  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. // We rearrange the items in case there are any empty gaps
  16. setupContentForNumItems(count);
  17. // If our folder has too many items we prune them from the list. This is an issue
  18. // when upgrading from the old Folders implementation which could contain an unlimited
  19. // number of items.
  20. for (ShortcutInfo item: overflow) {
  21. mInfo.remove(item);
  22. LauncherModel.deleteItemFromDatabase(mLauncher, item);
  23. }
  24. mItemsInvalidated = true;
  25. updateTextViewFocus();
  26. mInfo.addListener(this);
  27. if (!sDefaultFolderName.contentEquals(mInfo.title)) {
  28. mFolderName.setText(mInfo.title);
  29. } else {
  30. mFolderName.setText("");
  31. }
  32. }

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

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

  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()方法中:

  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. Drawable animateDrawable = ((TextView) destView).getCompoundDrawables()[1];
  5. computePreviewDrawingParams(animateDrawable.getIntrinsicWidth(), destView.getMeasuredWidth());
  6. // This will animate the dragView (srcView) into the new folder
  7. onDrop(srcInfo, srcView, dstRect, scaleRelativeToDragLayer,
    1, postAnimationRunnable);
  8. // This will animate the first item from it's position as an icon into its
  9. // position as the first item in the preview
  10. animateFirstItem(animateDrawable, INITIAL_ITEM_ANIMATION_DURATION);
  11. postDelayed(new Runnable() {
  12. public void run() {
  13. addItem(destInfo);
  14. }
  15. }, INITIAL_ITEM_ANIMATION_DURATION);
  16. }

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

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

  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. float offset = (1 - r) * mMaxPerspectiveShift;
  7. float scaledSize = scale * mBaselineIconSize;
  8. float scaleOffsetCorrection = (1 - scale) * mBaselineIconSize;
  9. // We want to imagine our coordinates from the bottom left, growing up and to the
  10. // right. This is natural for the x-axis, but for the y-axis, we have to invert things.
  11. float transY = mAvailableSpaceInPreview - (offset + scaledSize + scaleOffsetCorrection);
  12. float transX = offset + scaleOffsetCorrection;
  13. float totalScale = mBaselineIconScale * scale;
  14. final int overlayAlpha = (int) (80 * (1
    - r));
  15. if (params == null) {
  16. params = new PreviewItemDrawingParams(transX, transY, totalScale, overlayAlpha);
  17. } else {
  18. params.transX = transX;
  19. params.transY = transY;
  20. params.scale = totalScale;
  21. params.overlayAlpha = overlayAlpha;
  22. }
  23. return params;
  24. }

主要工作是:计算图标的排列,每一个相对上一个有点偏移的距离;效果图如下:

以上基本是把Step 2流程大致过了一遍。

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

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

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

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

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

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

来看代码如下:

  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],
    1, 1, 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内部类的代码如下:

  1. OnAlarmListener mReorderAlarmListener = new OnAlarmListener() {
  2. public void onAlarm(Alarm alarm) {
  3. realTimeReorder(mEmptyCell, mTargetCell);
  4. }
  5. };

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

  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()方法,

  1. public void onDrop(DragObject d) {
  2. ShortcutInfo item;
  3. 。。。 。。。
  4. f (d.dragView.hasDrawn()) {
  5. mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, mCurrentDragView);
  6. } else {
  7. mCurrentDragView.setVisibility(VISIBLE);
  8. }
  9. mInfo.add(item); 。。。 。。。

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

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

  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的位置信息在数据库中。

  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源代码。

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

抱歉!评论已关闭.