Launcher3源码浅析(5.1)--OverviewMode

目录

  • 前言
  • 初始化
  • 进入/退出OverviewMode
  • 壁纸设置
  • Widget插件

前言

  OverviewMode其实就是长按桌面空白处进去(或按menu键进入)的那个界面,一般也称之编辑模式页面,里面一般包含了壁纸的设置和插件的设置。

初始化

  首先说下布局,其布局还是在launcher.xml里,然后上部分显示的是workspace的布局,下面布局则是overview_panel.xml。overview_panel里包含了wallpaper_button,widget_button和settings_button这三个TextView。然后在Launcher.java的setupViews()里初始化这三个view,并设置其对应点击事件,从点击事件的处理方法可以知道,settings_button的事件其实并没有实际的实现,所以这个view一般不显示。

进入/退出OverviewMode

  进入/退出OverviewMode分别对应Workspace里的enterOverviewMode和exitOverviewMode方法,这两个方法最终都调用了enableOverviewMode:

private void enableOverviewMode(boolean enable, int snapPage, boolean animated) {
    State finalState = Workspace.State.OVERVIEW;
    if (!enable) {
        finalState = Workspace.State.NORMAL;
    }

    Animator workspaceAnim = getChangeStateAnimation(finalState, animated, 0, snapPage);
    if (workspaceAnim != null) {
        onTransitionPrepare();
        workspaceAnim.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator arg0) {
                onTransitionEnd();
            }
        });
        workspaceAnim.start();
    }
}

  这里通过finalState等参数调用getChangeStateAnimation来获取状态改变的动画对象,然后开始动画。这里Workspace.State.OVERVIEW状态就表示显示的是OverviewMode页面,Workspace.State.NORMAL则显示的是Workspace页面。

壁纸设置

 点击widget_button,调用onClickWallpaperPicker方法启动设置壁纸的activity,如下:

protected void onClickWallpaperPicker(View v) {

    final Intent pickWallpaper = new Intent(Intent.ACTION_SET_WALLPAPER);
    pickWallpaper.setComponent(getWallpaperPickerComponent());
    startActivityForResult(pickWallpaper, REQUEST_PICK_WALLPAPER);

    if (mLauncherCallbacks != null) {
        mLauncherCallbacks.onClickWallpaperPicker(v);
    }
}

  通过Intent.ACTION_SET_WALLPAPER,查看AndroidManifest.xml,可以知道是启动了LauncherWallpaperPickerActivity这个Activity,LauncherWallpaperPickerActivity继承WallpaperPickerActivity(WallpaperPickerActivity是在WallpaperPicker工程里面),WallpaperPickerActivity又继承WallpaperCropActivity,那就从WallpaperCropActivity的onCreate开始吧。onCreate里主要就调用了init(),然后WallpaperPickerActivity里又重写了init()方法。

那简单地看看WallpaperPickerActivity里的init():

protected void init() {
    setContentView(R.layout.wallpaper_cropper);
    mCropView = (CropView) findViewById(R.id.cropView);
    ......
    // Populate the built-in wallpapers
    ArrayList<WallpaperTileInfo> wallpapers = findBundledWallpapers();
    mWallpapersView = (LinearLayout) findViewById(R.id.wallpaper_list);
    SimpleWallpapersAdapter ia = new SimpleWallpapersAdapter(this, wallpapers);
    ......
   // Show the custom action bar view
    final ActionBar actionBar = getActionBar();
    actionBar.setCustomView(R.layout.actionbar_set_wallpaper);
    actionBar.getCustomView().setOnClickListener(
            new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    if (mSelectedTile != null) {
                        WallpaperTileInfo info = (WallpaperTileInfo) mSelectedTile.getTag();
                        info.onSave(WallpaperPickerActivity.this);
                    } else {
                        // no tile was selected, so we just finish the activity and go back
                        setResult(Activity.RESULT_OK);
                        finish();
                    }
                }
            });
    ......
}

  init()主要是初始化布局和一些变量,设置一些动画,事件监听等。其中调用findBundledWallpapers来加载内置的壁纸,findBundledWallpapers里调用getWallpaperArrayResourceId来加载wallpapers.xml里配置的string-array数据源。所以想设置一些内置的壁纸可以在这数据源里面设置。

  actionBar整个标题栏的响应事件其实是实现设置壁纸这一功能,可以看到调用了WallpaperTileInfo的onSave,WallpaperTileInfo是一个抽象类,系统根据图片来选择使用哪一个子类,再调用其onSave方法,可以看下ResourceWallpaperInfo:

  @Override
public void onSave(WallpaperPickerActivity a) {
        boolean finishActivityWhenDone = true;
        a.cropImageAndSetWallpaper(mResources, mResId, finishActivityWhenDone);
}

  最终是调用了cropImageAndSetWallpaper方法。cropImageAndSetWallpaper的实现在WallpaperCropActivity里:

protected void cropImageAndSetWallpaper(
        Resources res, int resId, final boolean finishActivityWhenDone) {
    // crop this image and scale it down to the default wallpaper size for
    // this device
    int rotation = getRotationFromExif(res, resId);
    Point inSize = mCropView.getSourceDimensions();
    Point outSize = getDefaultWallpaperSize(getResources(),
            getWindowManager());
    RectF crop = getMaxCropRect(
            inSize.x, inSize.y, outSize.x, outSize.y, false);
    Runnable onEndCrop = new Runnable() {
        public void run() {
            // Passing 0, 0 will cause launcher to revert to using the
            // default wallpaper size
            updateWallpaperDimensions(0, 0);
            if (finishActivityWhenDone) {
                setResult(Activity.RESULT_OK);
                finish();
            }
        }
    };
    BitmapCropTask cropTask = new BitmapCropTask(this, res, resId,
    crop, rotation, outSize.x, outSize.y, true, false, onEndCrop);
    cropTask.execute();
}

  这里启动了一个异步任务BitmapCropTask来处理,execute()之后会调用doInBackground。

protected Boolean doInBackground(Void... params) {
        return cropBitmap();
}

可以看到doInBackground里只是调用了cropBitmap:

public boolean cropBitmap() {
    boolean failure = false;

    WallpaperManager wallpaperManager = null;
    if (mSetWallpaper) {
        wallpaperManager = WallpaperManager.getInstance(mContext.getApplicationContext());
    }
    if (mSetWallpaper && mNoCrop) {
        try {
            InputStream is = regenerateInputStream();
            if (is != null) {
                wallpaperManager.setStream(is);
                Utils.closeSilently(is);
            }
        } catch (IOException e) {
            Log.w(LOGTAG, "cannot write stream to wallpaper", e);
            failure = true;
        }
        return !failure;
    } else {
    .....
    }
.....
}

  这里先是实例化了wallpaperManager对象,然后调用其setStream方法实现壁纸的设置。

  WallpaperPicker里其实还有很多操作,比如滑动,点击不同区域动画效果等,具体也没细看,就不说了。

Widget插件

  Widget是Launcher里的小部件,也叫桌面插件。apps_customize_widget.xml就是一个Widget的布局,里面包括了Widget预览图和Widget的名称尺寸。每个Widget的初始化是在AppsCustomizePagedView的syncWidgetPageItems里,看下syncWidgetPageItems的实现:

public void syncWidgetPageItems(final int page, final boolean immediate) {
    int numItemsPerPage = mWidgetCountX * mWidgetCountY;
    final PagedViewGridLayout layout = (PagedViewGridLayout) getPageAt(page);
    ......
    for (int i = 0; i < items.size(); ++i) {
        Object rawInfo = items.get(i);
        PendingAddItemInfo createItemInfo = null;
        PagedViewWidget widget = (PagedViewWidget) mLayoutInflater.inflate(R.layout.apps_customize_widget, layout, false);
        .....
        GridLayout.LayoutParams lp = new GridLayout.LayoutParams(
                GridLayout.spec(iy, GridLayout.START),
                GridLayout.spec(ix, GridLayout.TOP));
        lp.width = cellWidth;
        lp.height = cellHeight;
        lp.setGravity(Gravity.TOP | Gravity.START);
        layout.addView(widget, lp);
    }
    ......
}

  mWidgetCountX和mWidgetCountY是对应Widget里面的排列是几行几列的,在AppsCustomizePagedView.java构造函数里初始化。
PagedViewGridLayout是一个网格布局,从代码可以看到把一个个PagedViewWidget对应new了一个GridLayout.LayoutParams(这里就可以修改Widget显示的宽高了),然后都放入到了这个网格布局里去。

1.预览图的加载

  Widget里显示的预览图的加载,在WidgetPreviewLoader.java的generateWidgetPreview里实现:

public Bitmap generateWidgetPreview(AppWidgetProviderInfo info, int cellHSpan, int cellVSpan,int maxPreviewWidth, int maxPreviewHeight, Bitmap preview, int[] preScaledWidthOut) {
    // Load the preview image if possible
    if (maxPreviewWidth < 0) maxPreviewWidth = Integer.MAX_VALUE;
    if (maxPreviewHeight < 0) maxPreviewHeight = Integer.MAX_VALUE;

    Drawable drawable = null;
    if (info.previewImage != 0) {
        drawable = mManager.loadPreview(info);
        if (drawable != null) {
            drawable = mutateOnMainThread(drawable);
        } else {
            Log.w(TAG, "Can't load widget preview drawable 0x" +
                    Integer.toHexString(info.previewImage) + " for provider: " + info.provider);
        }
    }
    int previewWidth;
    int previewHeight;
    Bitmap defaultPreview = null;
    boolean widgetPreviewExists = (drawable != null);
    if (widgetPreviewExists) {
        previewWidth = drawable.getIntrinsicWidth();
        previewHeight = drawable.getIntrinsicHeight();
    }
    .....
     if (scale != 1f) {
        previewWidth = (int) (scale * previewWidth);
        previewHeight = (int) (scale * previewHeight);
    }
    // If a bitmap is passed in, we use it; otherwise, we create a bitmap of the right size
    if (preview == null) {
        preview = Bitmap.createBitmap(previewWidth, previewHeight, Config.ARGB_8888);
    }
    // Draw the scaled preview into the final bitmap
    int x = (preview.getWidth() - previewWidth) / 2;
    if (widgetPreviewExists) {
        renderDrawableToBitmap(drawable, preview, x, 0, previewWidth,previewHeight);
    } 
    ......

    return mManager.getBadgeBitmap(info, preview);
}

  代码里的drawable就是加载的预览图,一般加载的是从插件apk里默认配置的预览图(这里也可以修改成我们想要的图),然后加载不到图片,那就只能生成一个了。
previewWidth和previewHeight对应的是Widget显示的预览图的宽高。
最终的preview就是我们的预览图了。

2.其他

  另外,PagedViewWidget.java的onFinishInflate里可以设置widget_preview的背景,即预览图后面的背景;AppsCustomizePagedView.java的onPackagesUpdated里可以过滤一些不想让其显示的Widget。

你可能感兴趣的:(Launcher)