教你撸一个属于你独一无二的 Android Launcher

Updated on with 0 views and 0 comments

序言

之前买了台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键看看效果

Screenshot20190521102432.png

可以看到该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应该还要有展示小部件的功能,这部分未提到,感兴趣的可以查阅相关资料自行了解。