【Launcher开发】壁纸选择

    在拖拽分析(上)一文中我们分析了WorkSpace长按事件监听位于Launcher.java的onLongClick方法中,其中WorkSpace会在onInterceptTouchEvent方法对TouchDown事件的位置进行判断,并使用setTag方法保存点击位置所在的cellInfo,如果cellInfo中的cell变量不为空则说明当前长按的是快捷图标,需进入startDrag进行拖拽;如果cell变量为空则说明长按处没有控件,进入壁纸选择的逻辑中,当时我们一语带过,没有深究。今天这篇文章则补齐Launcher应用壁纸选择流程。

    从onLongClick中可知,在给出长按震动反馈后,调用了Launcher.java的startWallpaper方法:

/**
     * 跳转壁纸选择页面
     */
    private void startWallpaper() {
        showWorkspace(true);
        final Intent pickWallpaper = new Intent(Intent.ACTION_SET_WALLPAPER);
        /**
         * 提供一个符合intent的Activity列表给用户选择
         */
        Intent chooser = Intent.createChooser(pickWallpaper,
                getText(R.string.chooser_wallpaper));
        startActivityForResult(chooser, REQUEST_PICK_WALLPAPER);
    }

    在startWallpager中首先动画显示Workspace(当前已经在桌面,所以此方法内部不执行动画代码段),然后就是用隐式意图启动符合Intent.ACTION_SET_WALLPAGER的应用,如果符合条件的应用有多个,则系统会弹出弹框供用户选择要打开的应用,具体弹框如下图所示:

    由此图中可知,满足Intent.ACITON_SET_WALLPAGER条件的应用有模拟器自带的Launcher3、Launcher(本应用)、Live Wallpagers、Photos、Pictures应用,此处我们研究自身应用Luancher,我们进入AndroidManifest.xml文件中可知壁纸选择页面正是WallpagerChooser:

<activity
            android:name="com.android.launcher2.WallpaperChooser"
            android:theme="@style/Theme.WallpaperPicker"
            android:label="@string/pick_wallpaper"
            android:icon="@mipmap/ic_launcher_wallpaper"
            android:finishOnCloseSystemDialogs="true"
            android:process=":wallpaper_chooser">
            <intent-filter>
                <action android:name="android.intent.action.SET_WALLPAPER" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
            <meta-data android:name="android.wallpaper.preview"
                android:resource="@xml/wallpaper_picker_preview" />
        </activity>

    需要注意的是,此Intent需要使用到权限:

    <uses-permission android:name="android.permission.SET_WALLPAPER" />
    <uses-permission android:name="android.permission.SET_WALLPAPER_HINTS" />

    在WallpagerChooser中则是启动一个Fragment进行壁纸选择,在WallpagerChooserDialogFragment的onCreate中:

@Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        findWallpapers();

        /* If this fragment is embedded in the layout of this activity, then we should
         * generate a view to display. Otherwise, a dialog will be created in
         * onCreateDialog()
         */
        if (mEmbedded) {
            View view = inflater.inflate(R.layout.wallpaper_chooser, container, false);
            view.setBackground(mWallpaperDrawable);

            final Gallery gallery = (Gallery) view.findViewById(R.id.gallery);
            gallery.setCallbackDuringFling(false);
            gallery.setOnItemSelectedListener(this);
            gallery.setAdapter(new ImageAdapter(getActivity()));

            View setButton = view.findViewById(R.id.set);
            setButton.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    selectWallpaper(gallery.getSelectedItemPosition());
                }
            });
            return view;
        }
        return null;
    }


    private void selectWallpaper(int position) {
        try {
            WallpaperManager wpm = (WallpaperManager) getActivity().getSystemService(
                    Context.WALLPAPER_SERVICE);
            wpm.setResource(mImages.get(position));
            Activity activity = getActivity();
            activity.setResult(Activity.RESULT_OK);
            activity.finish();
        } catch (IOException e) {
            Log.e(TAG, "Failed to set wallpaper: " + e);
        }
    }

        1.首先把默认的壁纸加载到mThumbs(缩略图)和mImages(原图)集合中。

        2.然后使用填充了一个拥有Gallery和壁纸确认按钮的布局,在Gallery中显示集合中的壁纸缩略图。

        3.确认按钮的点击事件中选择Gallery中被选中的图标,把当前被选中的bitmap设置到WallpagerManager中去。

    这里补充点知识,一般Android系统在Activity窗口之下还有一个壁纸窗口,它位于最底层。我们的bitmap设置到WallpagerManager,其会调用WallpagerManagerService的setWallpager方法把把bitmap保存在到一个已定义好的文件中。然后利用FileObserver通知WallpagerService(其实是其子类ImageWallpager)的Engine(其实是其子类DrawbleEngine)在壁纸窗口绘制mBackground(通过WallpagerManager的getBitmap方法获取)。

    既然Activity窗口在壁纸窗口之上,那么我们为了显示壁纸窗口需要让Activity窗口半透明或全透明才行,事实上我们的Launcher.java在显示Workspace时正是这么做的,即在onResume和改变壁纸可见性FLAG时都会设置Launcher.xml的根View的背景为R.drawable.workspace_bg的透明背景。以下是Launcher.java中改变壁纸可见性的代码,它会在点击all apps页面出现或消失时调用,当all apps页面消失时visible=true,显示时则反之。

void updateWallpaperVisibility(boolean visible) {
        int wpflags = visible ? WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER : 0;
        int curflags = getWindow().getAttributes().flags
                & WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
        if (wpflags != curflags) {
            getWindow().setFlags(wpflags, WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER);
        }
        setWorkspaceBackground(visible);
    }

    可以看到通过给Window设置FLAG_SHOW_WALLPAGER属性达到显示壁纸的作用,其实我们的清单文件中已经通过给Launcher.java设置主题达到了同样的目的,这也是我们第一次进入Lancher的workspace中就能在底部显示壁纸窗口的原因。

<style name="Theme.Holo.Wallpaper">
        <item name="windowBackground">@color/transparent</item>
        <item name="colorBackgroundCacheHint">@null</item>
        <item name="windowShowWallpaper">true</item>
    </style>

    通过给Activity窗口设置透明色和添加windowShowWallpager属性为true,就可保证使用继承自此主题的Activity能够显示壁纸窗口了。

    本篇总结:

        1.如果应用需要设置壁纸的功能,则需要在设置壁纸页面添加IntentFilter的action为android.intent.action.SET_WALLPAPER,且需要声明SET_WALLPAGER权限。在用户完成壁纸选择时需通过WallPagerManager的setBitmap、setResource、setStream等方法之一把用户选择的壁纸设置给壁纸窗口。

        2. 如果当前应用的某个Activity页面想要在其窗口下显示壁纸窗口,则可让Activity窗口使用系统自带的壁纸主题,或使用其他主题的Activity窗口的主题添加windowBackground和windowShowWallpager属性,当然!也可在代码中通过window.setFlag方式添加FLAG_SHOW_WALLPAGER达到同样效果。

猜你喜欢

转载自blog.csdn.net/qq_33859911/article/details/80799394