序言
之前买了台 Android 测试机,发现原生的 Launcher 不太对我胃口,毕竟 Android 的精髓在于折腾。打算学习一下 Launcher 开发,顺便有了这篇文章。实际上我们的 Android 桌面 Launcher 其实也只是一个稍微特殊一点的 App。并没有什么太高深的内容,我们都可以通过简单的代码实现你心中最完美的 Android Launcher。
惯例先上效果图
1 准备工作
实际上判断一个 App 是否是 Launcher 很简单,我们只需要在 manifest
里面增加两句 category
<activity android:name=".MainActivity"
android:screenOrientation="portrait"
>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.HOME" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
只需要配置"android.intent.category.HOME" 每次按 Home 键时都会调用该 App
尝试按一下 Home 键看看效果
可以看到该 App 已经出现在主屏幕应用列表里了。
2.编写界面
这里应该是最有趣的步骤了,你可以按照自己的喜好编写想要的界面。
作为一个 Launcher 应该设置背景为系统壁纸,增加一个 style 并且 parent="android:Theme.Wallpaper"。
<style name="AppTheme" parent="android:Theme.Wallpaper">
<item name="android:windowNoTitle">true</item>
<item name="android:windowTranslucentNavigation">false</item>
</style>
在 Application 中应用
<application
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
如果想实现沉浸式状态栏还可以在 Activity 里设置去掉状态栏
Window window = getWindow();
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
3.展示已安装 App
获取已安装的 App 信息
private List<ResolveInfo> mApps;
Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
mApps = getPackageManager().queryIntentActivities(mainIntent, 0);
通过 ResolveInfo 获取具体信息方法:
包名获取方法:resolve.activityInfo.packageName
icon获取获取方法:resolve.loadIcon(packageManager)
应用名称获取方法:resolve.loadLabel(packageManager).toString()
再通过 GridView
展示 App 表格。
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:orientation="vertical" >
<ImageView
android:layout_centerHorizontal="true"
android:id="@+id/img"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginTop="10dp"
/>
<TextView
android:layout_below="@id/img"
android:layout_centerHorizontal="true"
android:id="@+id/text"
android:lines="1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="2dp"
android:layout_gravity="center"
android:textColor="#000"
android:text="App名称"
/>
</RelativeLayout>
编写一个非常简单的 Gridview item layout 一个 Imageview 一个 TextView 分别显示 App 图标和名字。
public class AppsAdapter extends BaseAdapter {
private LayoutInflater mInflater;
public AppsAdapter() {
mInflater = LayoutInflater.from(Main3Activity.this);
}
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder;
if (convertView == null) {
viewHolder = new ViewHolder();
convertView = mInflater.inflate(R.layout.gridview_item, null);
viewHolder.img = (ImageView) convertView.findViewById(R.id.img);
viewHolder.text = (TextView) convertView.findViewById(R.id.text);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
ResolveInfo info = mApps.get(position);
viewHolder.img.setImageDrawable(info.activityInfo.loadIcon(getPackageManager()));
viewHolder.text.setText(info.activityInfo.loadLabel(getPackageManager()));
return convertView;
}
public final int getCount() {
return mApps.size();
}
public final Object getItem(int position) {
return mApps.get(position);
}
public final long getItemId(int position) {
return position;
}
}
class ViewHolder {
public ImageView img;
public TextView text;
}
至此,已经可以显示所有已安装的 App 了,初步有了 Launcher 的模样。
4.跳转已安装的 App
光有界面是不够的,Launcher 必须要有打开别的 App 的能力。给我们的 mGrid
增加一个点击事件
private AdapterView.OnItemClickListener listener = new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
ResolveInfo info = mApps.get(position);
//该应用的包名
String pkg = info.activityInfo.packageName;
//应用的主activity类
String cls = info.activityInfo.name;
ComponentName componet = new ComponentName(pkg, cls);
Intent i = new Intent();
i.setComponent(componet);
startActivity(i);
}
};
只要能获得到 packageName
和 App name 就能通过 i.setComponent(componet);
跳转到相应的 App。
还可以指定跳转到电话,图片,信息,设置界面。
private class Click implements View.OnClickListener {
@Override
public void onClick(View view) {
String pkg = null;
String cls = null;
switch (view.getId()) {
case R.id.tel:
pkg = "com.google.android.dialer";
cls = "com.google.android.dialer.extensions.GoogleDialtactsActivity";
break;
case R.id.pic:
pkg = "com.google.android.GoogleCamera";
cls = "com.android.camera.CameraLauncher";
break;
case R.id.msg:
pkg = "com.google.android.apps.messaging";
cls = "com.google.android.apps.messaging.ui.ConversationListActivity";
break;
case R.id.set:
pkg = "com.android.settings";
cls = "com.android.settings.Settings";
break;
}
if (pkg != null && cls != null) {
intentApp(pkg, cls);
}
}
}
End
Android 的乐趣在于折腾,实现自己想要的东西还是很有成就感的。另外 Launcher 应该还要有展示小部件的功能,这部分未提到,感兴趣的可以查阅相关资料自行了解。
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于