Android launcher应用的简单实现

本篇文章将实现一个简单的可替代系统桌面的launcher应用。
此应用应该具有如下功能:
1. 自定义的桌面应用能够被设置为系统的默认桌面,替代原有桌面。
2. 系统桌面上的app图标能够排列在我们的自定义桌面上。
3. 点击自定义桌面上的app图标,能够打开对应的app。
下面就看看具体实现:
新建一个空项目,命名为LauncherTest,然后打开项目的manifest文件:
一般情况下,自动生成的MainActivity的配置如下:


其中”android.intent.action.MAIN”这个action指定了应用的入口activity,”android.intent.category.LAUNCHER”指定了程序的图标要显示在应用程序列表里。
一、功能一
要实现功能一,要对上面的代码intent-filter里加上如下两行:

如果此桌面activity不需要另外显示图标在系统桌面上的话,就不需要category android:name=”android.intent.category.LAUNCHER”这行。
这样的话,我们的程序运行起来后,再按手机Home键时,系统就会弹出一个列表,列出所有的桌面应用,让用户选择用哪个应用打开桌面。里面就会包含我们的LauncherTest。
但是我们的应用什么功能都没有,即使选为桌面也是任何功能都没有的。
二、功能二:
要怎么列出系统桌面上显示的所有的app的图标呢?
我们可以先获取对应的app列表,然后逐个获取并显示其图标。实现如下:
//系统桌面上显示的app列表 private List localApps;

在MainActivity的onCreate()里调用如下得到app列表信息:
Intent mainIntent = new Intent(Intent.ACTION_MAIN, null); mainIntent.addCategory(Intent.CATEGORY_LAUNCHER); localApps = getPackageManager().queryIntentActivities(mainIntent, 0);

这些app图标里也会包含我们自己的应用图标,但理想效果应该是不显示比较好,因为我们的app是用来替代系统桌面的,没有别的作用。如果不显示自己的图标呢?加上以下的代码:
//遍历获取的app列表,如果哪个元素的app包名和我们的一样,就说明是我们本应用,则移除此元素。 Iterator iterator = localApps.iterator(); while(iterator.hasNext()) { ResolveInfo resolveInfo = iterator.next(); String packageName = resolveInfo.activityInfo.packageName; if(packageName.equals(getApplication().getPackageName())) { iterator.remove(); } }

在这里注意用到了iterator的remove()来移除List中的元素。那么用List类的remove()方法可以不?答案是不行,如果在List遍历的过程中用List的remove()方法删除List的元素,会发生CurrentModificationException。而Iterator的remove()方法是安全有效的,不会发生异常。
接下来,我们要获取每个app的图标并显示出来,显示图标可以用到GridView,用以把图标网格状显示,和系统桌面效果一样。
修改布局文件activity_main.xml, 加入GridView:

其中有几个属性说一下:
android:numColumns=”4”是设置图标列数为4。
android:verticalSpacing=”20dp”是设置每行之间的间距。
android:scrollbars=”none”是为了去掉列表滑动时右边的滚动条。
android:overScrollMode=”never”是为了去掉列表下拉或者上拉时,顶部或底部出现的半月形的阴影。
然后看看MainActivity中怎么使用:
我们需要给GridView设置一个适配器,然后需要给app图标的显示定义一个布局:
app图标显示的布局如下:
icon_layout.xml:

显示的效果就是上面一个图标,下面一个app名。
然后下面是对应的适配器的定义:
public class AppsAdapter extends BaseAdapter { private List apps; //构造器中传入app列表信息 public AppsAdapter(List apps){ this.apps = apps; }@Override public int getCount() { return apps.size(); }@Override public Object getItem(int i) { return apps.get(i); }@Override public long getItemId(int i) { return i; }@Override public View getView(int i, View view, ViewGroup viewGroup) {//为了简单起见,这里没有用ViewHolder,可以改成ViewHolder以改善显示效率 LayoutInflater inflater = LayoutInflater.from(MainActivity.this); View appView = inflater.inflate(R.layout.icon_layout, null); ImageView iv = (ImageView) appView.findViewById(R.id.app_icon); //设置图标适应方式和大小 iv.setScaleType(ImageView.ScaleType.FIT_CENTER); iv.setLayoutParams(new LinearLayout.LayoutParams(70, 70)); TextView tv = (TextView) appView.findViewById(R.id.app_name); ResolveInfo info = apps.get(i); //显示app图标 iv.setImageDrawable(info.activityInfo.loadIcon(getPackageManager())); //显示app名 String appName = info.loadLabel(getPackageManager()).toString(); tv.setText(appName); return appView; } }

上面的代码里,getView方法每次都会创建一个新的view,当数据很多时,显示效率会很低。优化方案点这里ViewHolder优化显示性能
然后设置此适配器为GridView的适配器:
GridView appsGrid = (GridView) findViewById(R.id.apps_list); appsGrid.setAdapter(new AppsAdapter(localApps));

自此,第二个功能就完成了,app列表已经可以完全显示在我们的app界面上了。
三、功能三
第三个功能,点击各个app图标启动对应的app。
下面是点击事件的代码:
private AdapterView.OnItemClickListener clickListener = new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView adapterView, View view, int i, long l) { ResolveInfo info = localApps.get(i); //该应用的包名 String pkg = info.activityInfo.packageName; //应用的主activity类 String cls = info.activityInfo.name; ComponentName componet = new ComponentName(pkg, cls); //打开该应用的主activity Intent intent = new Intent(); intent.setComponent(componet); startActivity(intent); } };

然后把点击事件应用到GridView上:
appsGrid.setOnItemClickListener(clickListener);

这样第三个功能也就完成了。
至此一个简单的桌面应用就完成了。效果图如下:
【Android launcher应用的简单实现】Android launcher应用的简单实现
文章图片

    推荐阅读