如何做一个应用程序的快捷方式入口

来源:互联网 发布:短作业优先算法例题 编辑:程序博客网 时间:2024/04/20 16:54

最近在做一个桌面的快捷方式展示的功能。将其中的一些技术点做一个分享。

一、筛选应用程序

要完成桌面快捷方式展示,首先要能够拿到当前系统中安装的应用程序,且这些应用程序是用户可见的(不包含系统的app)

网上有人用如下的方法:

List<PackageInfo> list = getPackageManager().getInstalledPackages(0);

但是从源码中可以得知,这样会得到所有系统的package:

packageManager的getInstalledPackages()方法实际上是一个抽象方法,具体实现在ApplicationPackageManager中

@SuppressWarnings("unchecked")@Overridepublic List<PackageInfo> getInstalledPackages(int flags) {    return getInstalledPackages(flags, mContext.getUserId());}/** @hide */@Overridepublic List<PackageInfo> getInstalledPackages(int flags, int userId) {    try {        ParceledListSlice<PackageInfo> slice = mPM.getInstalledPackages(flags, userId);        return slice.getList();    } catch (RemoteException e) {        throw new RuntimeException("Package manager has died", e);    }}

所以最终还是到packageManagerService中去:

@Overridepublic ParceledListSlice<PackageInfo> getInstalledPackages(int flags, int userId) {    final boolean listUninstalled = (flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0;    enforceCrossUserPermission(Binder.getCallingUid(), userId, true, "get installed packages");    // writer    synchronized (mPackages) {        ArrayList<PackageInfo> list;        if (listUninstalled) {            list = new ArrayList<PackageInfo>(mSettings.mPackages.size());            for (PackageSetting ps : mSettings.mPackages.values()) {                PackageInfo pi;                if (ps.pkg != null) {                    pi = generatePackageInfo(ps.pkg, flags, userId);                } else {                    pi = generatePackageInfoFromSettingsLPw(ps.name, flags, userId);                }                if (pi != null) {                    list.add(pi);                }            }        } else {            list = new ArrayList<PackageInfo>(mPackages.size());            for (PackageParser.Package p : mPackages.values()) {                PackageInfo pi = generatePackageInfo(p, flags, userId);                if (pi != null) {                    list.add(pi);                }            }        }        return new ParceledListSlice<PackageInfo>(list);    }}

mSettings.mPackages.values()中存储的即为当前系统的所有package,包括系统app。因此这条路不行。


其实有非常方便的方法,实例代码如下:

Intent baseIntent = new Intent();baseIntent .setAction(Intent.ACTION_MAIN);baseIntent .addCategory(Intent.CATEGORY_LAUNCHER);PackageManager packageManager = getPackageManager();        List<ResolveInfo> list = packageManager.queryIntentActivities(                baseIntent, 0 /* no flags */);        Collections.sort(list, new ResolveInfo.DisplayNameComparator(                packageManager));

Intent.CATEGORY_LAUNCHER帮助我们很好的过滤的系统app,good!


二、利用Gridview做内容展示

因为已安装的应用程序是动态的,所以这里选择使用gridview来做展示

ArrayList<HashMap<String, Object>> items = new ArrayList<HashMap<String, Object>>();final int listSize = list.size();for (int i = 0; i < listSize; i++) {     ResolveInfo resolveInfo = list.get(i);     HashMap<String, Object> map = new HashMap<String, Object>();     map.put("ItemImage",resolveInfo.loadIcon(packageManager));     map.put("ItemText", resolveInfo.loadLabel(packageManager));     items.add(map); SimpleAdapter adapter = new SimpleAdapter(this, items,                R.layout.shortcut_item,                new String[] { "ItemImage", "ItemText" }, new int[] {                        R.id.shortcut_image, R.id.shortcut_title });gridview.setAdapter(adapter);
这里的list即为第一步中得到的package列表,我们将icon信息,和application的名字保存到items这个列表中,用来作为adapter的数据来源。

这里有一点需要特别注意的,gridview或者listview这些对象在显示图片的时候只能使用本地drawable中的资源,但是这些app的图标却在另外app当中,这时候就需要用到viewBinder的绑定机制,如此一来不仅能够显示外部app中的资源,也可以从网络获取资源图片。代码也很简单:

adapter.setViewBinder(new ViewBinder() {            @Override            public boolean setViewValue(View view, Object data,                    String textRepresentation) {                if (view instanceof ImageView && data instanceof Drawable) {                    ImageView iv = (ImageView) view;                    iv.setImageDrawable((Drawable) data);                    return true;                } else                    return false;            }        });

三、实现快捷方式的选取
通过上面的步骤,我们已经实现了app的选取和展示,那么我们最后要实现的就是在上面的界面中选择一个应用,并添加到桌面。

那么这里在处理点击事件的时候要怎么处理呢?

其实Android源码已经提供了一个非常方便的Action:ACTION_PICK_ACTIVITY

Android SDK API对其的描述是:

ACTION_PICK_ACTIVITY

Input: get*Extra field EXTRA_INTENT  is an Intent used with PackageManager.queryIntentActivities  to determine the set of activities from which to pick.

Output: Class name of the activity that was selected.

要实现点击应用图标,是选中而不是启动这个app,只要做如下操作:

        Intent pickIntent = new Intent(Intent.ACTION_PICK_ACTIVITY);        Intent mainIntent = new Intent();        mainIntent.setAction(Intent.ACTION_MAIN);        mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);        pickIntent.putExtra(Intent.EXTRA_INTENT, mainIntent);        pickIntent.putExtra(Intent.EXTRA_TITLE, "选择应用程序"); // 设置界面title

在响应这个action的activity中的click处理函数中去获得这个对应app的intent,然后返回即可:

setResult(Activity.RESULT_OK, intent);finish();


四、主页面的展示

到这里我们走到最后一步了,主页面的展示,展示是一个动态添加view的过程

LayoutInflater inflater = LayoutInflater.from(this);View shortcut_view = inflater.inflate(R.layout.display_item, null);ImageView img = (ImageView) shortcut_view                    .findViewById(R.id.shortcut_img);img.setImageDrawable(appIcon);TextView tv = (TextView) shortcut_view.findViewById(R.id.shortcut_label);tv.setText(applabel);tv.setVisibility(View.INVISIBLE);tv.setOnFocusChangeListener(this);shortcut_view.setFocusable(true);shortcut_view.setOnClickListener(this);shortcut_view.setOnFocusChangeListener(this);shortcut_view.setTag(data);shortcut_view.setBackgroundResource(R.drawable.launcher_iv_border);LinearLayout.LayoutParams llparams = new LinearLayout.LayoutParams(120, 120);llparams.setMargins(10, 10, 10, 10);appContainerLayout.addView(shortcut_view, llparams);

display_item.xml是每一个动态添加的快捷方式的布局信息

<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="wrap_content"android:layout_height="wrap_content" ><ImageView     android:id="@+id/shortcut_img"     android:layout_width="wrap_content"android:layout_height="wrap_content"  android:layout_alignParentTop="true"  /><TextView     android:id="@+id/shortcut_label"     android:layout_width="fill_parent"    android:layout_height="wrap_content"     android:layout_alignParentBottom="true"     android:gravity="center_horizontal"    android:background="#a0000000"    android:textColor="#ffffff"    android:textSize="20dp"/></RelativeLayout>

我们还想要做一个简单的动态效果,当光标聚焦时才显示包名,因此为TextView添加了一个FocusChangeListener,并把默认状态设成INVISIBLE

public void onFocusChange(View arg0, boolean arg1) {        // TODO Auto-generated method stub        TextView tv = (TextView) arg0.findViewById(R.id.shortcut_label);        if (arg1) {            tv.setVisibility(View.VISIBLE);        } else {            tv.setVisibility(View.INVISIBLE);        }    }



0 0