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

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

一、筛选应用程序

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

网上有人用如下的方法:

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

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

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

@SuppressWarnings("unchecked")
@Override
public List<PackageInfo> getInstalledPackages(int flags) {
    return getInstalledPackages(flags, mContext.getUserId());
}

/** @hide */
@Override
public 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中去:

@Override
public 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
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值