前言

这一遍文章记录了最近一两天开发一个简单的Launcher,好记性不如烂笔头,在这里记录一遍可以加深下印象,顺便记录下遗留的问题以便后续修改。

1.Launcher的概述

android系统启动的最后一步就是启动一个HOME应用程序,来展示系统已经安装的应用程序,这个就是Launcher。系统已经安装的应用程序信息可以从PMS获取到,然后展示到Launcher上,这样就可以通过点击启动相应的应用程序。

2.定义AndroidManifest.xml

首先我们新建一个工程,然后编辑AndroidManifest.xml,可以参照android系统的Launcher2/Launcher3的来写:

<activity android:name=".MainActivity"android:launchMode="singleTask"android:clearTaskOnLaunch="true"android:stateNotNeeded="true"android:resumeWhilePausing="true"android:theme="@style/Launcher"android:windowSoftInputMode="adjustPan"android:screenOrientation="nosensor"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.HOME"/><category android:name="android.intent.category.DEFAULT"/><category android:name="android.intent.category.MONKEY"/></intent-filter></activity>

其实Launcher跟我们开发其他应用差不多,主要多了一下两个category:

<category android:name="android.intent.category.HOME"/>
<category android:name="android.intent.category.DEFAULT"/>

还有要注意下设置启动模式singleTask。

这样把应用程序安装到手机上当我们按下Home键就可以选择使用这个Launcher了。

3.获取已安装应用程序信息

方案1
这样可以获取到已安装的包信息,参数0表示不接受任何flag信息。但是这个接口给有些手机厂商把权限禁止后,就不能正确获取到已安装应用列表了,所以不推荐用这个接口。

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

方案2
PackageManager().queryIntentActivities(Intent intent,int flags)可以传个Intent参数,传进去的Intent添加一个Intent.CATEGORY_LAUNCHER的Category就可以查询到需要的信息。

Intent intent = new Intent(Intent.ACTION_MAIN, null);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
List<ResolveInfo> list = getPackageManager().queryIntentActivities(intent, 0);

4.展示应用程序

展示应用程序有很多种方法,这个也就是自定义一个Launcher真正困难所在。我现在水平有限只能想到什么就做成什么样,刚开始我就直接用GridView直接展示出来,但是这样感觉跟原生的差太多了,然后我用ViewPager+RecyclerView替代了。用ViewPager是可以实现翻页,多页的切换。然后用RecyclerView只是为了可以实现退拽功能。但是现在还不能实现从当前页退拽到下一页。

这里还依赖了一个指示器,因为这个之前用过感觉还可以就直接拿来用了。

implementation 'me.relex:circleindicator:1.2.2@aar'

activity_main.xml用Viewpager+CircleIndicator,这里的current_circle就是指示器用的小圆点我是用shape画了一个圆。

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:fitsSystemWindows="true"android:layout_height="match_parent"><android.support.v4.view.ViewPagerandroid:id="@+id/viewpager"android:layout_width="match_parent"android:overScrollMode="never"android:layout_height="match_parent"/><me.relex.circleindicator.CircleIndicatorandroid:id="@+id/indicator"android:layout_width="match_parent"android:layout_height="25dp"android:layout_alignParentBottom="true"app:ci_drawable="@drawable/current_circle"app:ci_drawable_unselected="@drawable/unselected_circle" />
</RelativeLayout>
 private void initData() {mViewPager.setOffscreenPageLimit(5);//list为上面获取到信息adapter = new SampleFragmentPagerAdapter(this, list);mViewPager.setAdapter(adapter);//指示器绑定ViewPagerindicator.setViewPager(mViewPager);adapter.registerDataSetObserver(indicator.getDataSetObserver());mViewPager.setCurrentItem(0);//ViewPager的切换动画,这个有很多大神分享,都可以参考一下让桌面切换变的更炫酷mPageTransformer = new RotateDownPageTransformer();mViewPager.setPageTransformer(true,mPageTransformer );}

在SampleFragmentPagerAdapter里面只是简单的加载个View。

    public SampleFragmentPagerAdapter(Context context, List<AppInfo> list, int type) {mContext = context;mList = list;//分割下数据让每个页面都可是显示应用程序splitData();}//总感觉这边写的不好。private void splitData() {mList_first.clear();mList_second.clear();mList_third.clear();mList_forth.clear();mList_fifth.clear();int num = 16;for (int i = 0; i < mList.size(); i++) {if (mList.get(i) != null) {if (i / num == 0) {mList_first.add(mList.get(i));} else if (i / num == 1) {mList_second.add(mList.get(i));} else if (i / num == 2) {mList_third.add(mList.get(i));} else if (i / num == 3) {mList_forth.add(mList.get(i));} else if (i / num == 4) {mList_fifth.add(mList.get(i));}}}}@NonNull@Overridepublic Object instantiateItem(@NonNull ViewGroup container, int position) {//getData(position)把每个页面的数据传进去View view = new WorkSpaceView(mContext,getData(position));container.addView(view);return view;}@Overridepublic int getCount() {//返回Fragment的数量return 5;}@Overridepublic boolean isViewFromObject(@NonNull View view, @NonNull Object o) {return view == o;}

这里加载了每个页面的布局也就只有一个RecyclerView.然后按照使用流程实现然后就是在长按事件里启动退拽功能。

public class WorkSpaceView extends LinearLayout {RecyclerView recyclerView;ShowAppAdapter adapter;ItemTouchHelper mItemTouchHelper;
public WorkSpaceView(Context context, final List<AppInfo> list) {super(context);
//        inflate(context,R.layout.fragment_workspace,null);LayoutInflater.from(context).inflate(R.layout.fragment_workspace,this,true);recyclerView = findViewById(R.id.recyclerview);recyclerView.setLayoutManager(new GridLayoutManager(context, 4));adapter = new ShowAppAdapter(context, list);recyclerView.setAdapter(adapter);recyclerView.addOnItemTouchListener(new OnRecyclerItemClickListener(recyclerView) {@Overridepublic void onItemClick(RecyclerView.ViewHolder vh) {int position = vh.getAdapterPosition();// Toast.makeText(mContext, mList.get(position), Toast.LENGTH_SHORT).show();}@Overridepublic void onItemLongClick(RecyclerView.ViewHolder vh) {//开启退拽mItemTouchHelper.startDrag(vh);}});mItemTouchHelper = new ItemTouchHelper(new ItemTouchHelper.Callback() {@Overridepublic int getMovementFlags(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {//支持上下左右退拽final int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN |ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;final int swipeFlags = 0;return makeMovementFlags(dragFlags, swipeFlags);}@Overridepublic boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target) {//得到当拖拽的viewHolder的Positionint fromPosition = viewHolder.getAdapterPosition();//拿到当前拖拽到的item的viewHolderint toPosition = target.getAdapterPosition();if (fromPosition < toPosition) {for (int i = fromPosition; i < toPosition; i++) {Collections.swap(list, i, i + 1);}} else {for (int i = fromPosition; i > toPosition; i--) {Collections.swap(list, i, i - 1);}}//更新布局adapter.notifyItemMoved(fromPosition, toPosition);return true;}@Overridepublic void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int i) {}});mItemTouchHelper.attachToRecyclerView(recyclerView);adapter.notifyDataSetChanged();}
}

在RecyclerView里面也有个需要注意的就是点击启动应用程序,也可以写在上面的点击事件里面,在启动的Intent里面需要设置个flags为Intent.FLAG_ACTIVITY_NEW_TASK才能在最近任务里面看到该启动的应用程序。

int position = appViewHolder.getAdapterPosition();
String pkg = list.get(position).mResolveInfo.activityInfo.packageName;
String cls = list.get(position).mResolveInfo.activityInfo.name;
ComponentName componentName = new ComponentName(pkg, cls);
Intent intent = new Intent();
intent.setComponent(componentName);
//添加该flags才能在最近任务里面看到该应用程序
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(intent);

5.设置壁纸

作为一个Launcher可以换壁纸当然是必不可少的啦。
首先在AndroidManifest.xml可以看到我们设置的Theme

android:theme="@style/Launcher"
    <style name="Launcher" parent="android:Theme.Wallpaper"><item name="android:windowNoTitle">true</item></style>

这样壁纸就用我们默认的桌面壁纸了,那怎么才能切换呢?这入口我放在了主页的菜单里
,通过Intent直接启动系统自带的壁纸切换功能。

<menu xmlns:android="http://schemas.android.com/apk/res/android"><item android:id="@+id/action_wallpaper_settings"android:title="@string/wallpaper"android:orderInCategory="100" />
</menu>
    @Overridepublic boolean onCreateOptionsMenu(Menu menu) {getMenuInflater().inflate(R.menu.main, menu);return true;}
@Overridepublic boolean onOptionsItemSelected(MenuItem item) {switch (item.getItemId()) {case R.id.action_wallpaper_settings:Intent pickWallpaper = new Intent(Intent.ACTION_SET_WALLPAPER);Intent chooser = Intent.createChooser(pickWallpaper,"chooser_wallpaper");startActivity(chooser);return true;}return false;}

遗留问题

ViewPager的切换动画问题:
第一次设置ViewPager的切换动画没有问题,然后我想做一个可以该改变ViewPager的切换动画的按钮放在换壁纸的菜单里,但是发现出问题了。首页切换没有问题,首页切到第二页时第二页的展示出现bug了。我开始以为是这个动画的问题,然后单独设置了该动画发现没有问题就是再次设置动画效果就有问题了。
图标退拽到第二页:
现在只是图标可以在当前页面里变换位置,并不能拖到第二页。这个需要重新写个控件来代替ViewPager+RecyclerView。暂时第一版就这样。还有就是当我们变化位置了需要把数据存储下来然后加载数据时候再重新加载。

【安卓小笔记】自己制作一个Launcher相关推荐

  1. 小程序商城制作一个需要多少钱?

    小程序商城制作一个需要多少钱?小程序制作需要多少费用?这是很多朋友都想知道的问题,今天软程小编在这儿给大家说一下. 小程序的开发分为两类: 第一类:模板商城开发 模板开发是通过平台制作软件商进行操作, ...

  2. 微信小程序|基于小程序+C#制作一个考试答题小程序

    基于小程序+C#制作一个考试答题小程序打破传统线下考试答题的边界线问题,使考试不用再局限与某个统一的场所,只要有设备,哪里都能考试. 一.小程序

  3. 基于微信小程序+爬虫制作一个表情包小程序

    跟朋友聊天斗图失败气急败坏的我选择直接制作一个爬虫表情包小程序,从源头解决问题,从此再也不用担心在斗图中落入下风 精彩专栏持续更新↓↓↓ 微信小程序实战开发专栏 一.API 1.1 项目创建 1.2 ...

  4. 小程序商城制作一个需要多少钱?一般包括哪些费用?

    最近很多小伙伴都看到了小程序商城的商机,想说自己要创造一个.但是不知道小程序商城要多少钱,担心买不起.其实小程序商城制作有贵也有便宜.关键是要看选择什么方式,对小程序有什么规定.下面简单说说小程序商城 ...

  5. 微信小程序|基于小程序+C#制作一个聊天系统

    此文主要基于小程序+C#使用WebSocket制作一个聊天系统,基本实现小程序与服务端的聊天功能.用小程序自带的客服功能只能绑定微信且一对一沟通,接入市面上成熟的即时通讯预算又略显不足,干脆自己开发一 ...

  6. 微信小程序|基于小程序+C#制作一个电子书阅读器

    文章目录 一.文章前言 二.开发流程 2.1.开发工具 2.2.页面实现 2.3.数据库设计 2.4.API实现 一.文章前言 书籍是人类进步的阶梯,各位小伙伴在使用市面上各类阅读器进行阅读的时候是否 ...

  7. 【uniapp小程序】制作一个名片列表

    示例部分 HTML.CSS部分 HTML部分如下 CSS部分如下 切换名片功能的实现 这里使用动态绑定class类的方式实现 我们通过判断当前选择卡片的index来确定是否显示高亮 当我们点击不同的卡 ...

  8. 微信小程序-从零开始制作一个跑步微信小程序

    首发地址 一.准备工作 1.注册一个小程序账号,得用一个没注册过公众号的邮箱注册. 2.注册过程中需要很多认证,有很多认证,比较繁琐,如果暂时只是开发测试,不进行提审.发布的话,只要完成营业执照号填写 ...

  9. 微信小程序-从零开始制作一个跑步微信小程序 1

    小编推荐:Fundebug专注于JavaScript.微信小程序.微信小游戏,Node.js和Java实时BUG监控.真的是一个很好用的bug监控费服务,众多大佬公司都在使用. 前言 我已经把全部代码 ...

  10. 基于小程序+C#制作一个考试答题小程序

    基于小程序和C#语言,你可以使用微信小程序开发工具和Visual Studio进行开发.以下是开发考试答题小程序的基本步骤: 需求分析和设计:首先,你需要明确考试答题小程序的功能和界面设计.例如,考试 ...

最新文章

  1. mac 查看mysql是否安装_[简明核心系列] 三分钟Mac安装MySQL教程
  2. MSB6006: “cmd.exe”已退出,代码为 3.
  3. python的pandas包使用教程_「Python」pandas入门教程
  4. sybase性能优化经验浅谈
  5. 计算机组成结构IR,计算机组成与体系结构试题
  6. 简而言之:JRunner
  7. 三星关闭shell提示_啄木鸟家庭维修|三星滚筒洗衣机4c故障代码
  8. J2EE如何生成验证码图片和点击刷新验证码
  9. 乌版图 read-only file system
  10. 颜色空间直方图matlab,使用Matlab绘制图像的rgb颜色空间和Lab颜色空间分量图和分量直方图 | 学步园...
  11. [转] React之Immutable学习记录
  12. 嵌入式开发日记(6)——对串口数据读取的优化以及处理程序的改写
  13. Blender 建模
  14. 11.2 注解的使用示例1 select insert update和delete操作
  15. Linux驱动修炼之道-内存映射 mmap()/phys_to_virt()
  16. 像素px跟点pt大不同
  17. 我的世界服务器无法发送聊天信息,我的世界聊天框指令传送 | 手游网游页游攻略大全...
  18. LabVIEW与MATLAB混合编程——调用Matlab中.m的函数
  19. 网站发送邮箱验证实现找回密码
  20. WPF如何用TreeView制作好友列表、播放列表

热门文章

  1. cv2批量修改图片大小
  2. 用计算机pol计算方位角,卡西欧计算方位角 计算器算方位角.doc
  3. mysql怎么设置id自动编号_MySQL中实现ID编号自动增加的方法
  4. 新浪邮箱服务器设置,免费的新浪邮箱设置outlook怎么设置?
  5. 数据库必看--WYL篇
  6. 视界云联合创始人姜飞 荣获品途2017年NBI商业影响力新锐人物奖
  7. python---面向对象1
  8. AutoIt 脚本流行编辑工具
  9. 「补课」进行时:设计模式(2)——通过一个超级汽车工厂来了解工厂模式
  10. 【树莓派】更新树莓派SD卡测速一键脚本,SD卡读写速度测试