本篇文章将实现一个简单的可替代系统桌面的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应用的简单实现】
文章图片