简单练手的Launcher(一)

前言

最近想系统的学习android,无奈之前的基础太差(linux),所以算是一切从头开始学习。

从Android的启动流程开始,到Launcher这边的时候,看了Launcher3的一点源码分析,因为不懂android框架API和java的一些语法,大多看的云里雾里的。所以想自己做一个简单的Launcher练练手。因为Launcher说到底还是个APP,也顺遍学习学习android应用的开发。

趁着元旦假期和后面的两三个周末时间,自娱自乐勉勉强强做了一个能看的Launcher。怕脑子有点不好使,看过的东西回忘,为了方便还是把整个过程和思路及要点都理一下,很多东西都是东拼西凑边写边做的。

由于不太懂JAVA的语法加上第一次接触android应用,Launcher3的我也没借鉴太多,很多东西都是我按自己的套路来套的(甚至一些语言上命名习惯上也一时难以纠正),所以一些错误的思路和方案选择在所难免,主要目的还是在于可以入门了解Android应用开发为主。。

下面的讲解,不会去分析自己的代码,因为没有意义,着重还是在于理思路,以及总结过程需要用到的知识点。其实中间很多地方,很多原理摊开来讲都很多,这里为了方便就不细讲了,其实很多我也没有去细究过甚至有点自己被绕进去了的感觉,不过方向就在那里,查一下也很方便。

内容实现较多,所以分了几篇,先搭上个电梯:

代码上传GITHUB:

Launcher介绍

写Launcer之前需要了解什么是Launcher。

Google 即时桌面与其他的桌面/启动器应用基本功能无异,可以用来启动其他应用,用户也可以在 Google 即时桌面中随意设置 Widget 以及其他样式。同时拥有一个应用抽屉,用以显示用户安装的所有应用。

Launcher的介绍:

https://juejin.im/entry/580088a8bf22ec0064c1884e

https://blog.csdn.net/dddxxxx/article/details/78708971

因为这里只是单纯写一个APP,还没有讨论到定制系统的Launcher的问题,所以这个暂时放到一边。(本来还误认为把setting也以为是Launcher的一部分,把虚拟键也认为是Launcher的一部分,现在看来感觉有点蠢了)

从大致的介绍上看,最主要的功能还是显示APP,打开卸载操作APP等。因为Launcher本身就是可以定制的,然后很多功能效果都是可以自己加的。为了简化程序,就照着旁边一个会议电视机的Launcher的效果(去了一些麻烦的和没看出逻辑的效果),做了一个简易版本的。

目标效果

首先Launcher整体的结构分为两层,第一层放置着一些常用的APP图标和日期时间的显示。

第二层类似于Launcher中的抽屉,放置APP。

  • 多页放置APP应用
  • 左右滑动进行翻页,同时有跟随滑动的效果
  • 下方有个页指示来说明当前处于哪一页
  • APP图标点击释放,将打开APP,同时产生图标放大效果
  • APP长按进入卸载移动模式,抖动效果提示
  • 进入卸载移动模式后,选择APP图标,可以移动APP
  • 移动APP的过程中原图标消失,产生一个缩略图,指示最近的图标在某个位置替换区域提示。
  • 移动APP支持跨页移动。当放在左右边缘一定时间后自动切换到上一页或下一页
  • 移动APP抬手后会自动替换到抬手区域,如果抬手区域内有APP图标了,则该位置上的APP和原位置APP替换
  • 替换APP后,重启Launcher, APP的位置将和上次退出时一致
  • APP进入抖动的效果的同时左上角会出现一个X,选择则进行对APP的卸载提示框,来引导用户进行卸载功能
  • 页管理器,实现对页的增加和删除功能,因为时间原因暂时没做

开发环境

支持Android 5.1以上的。

开发工具:Android Studio

屏幕大小应该没什么关系,我的想法基本是按分辨率自适应的。

实现

1.新建一个项目

新建一个环境没什么特别的,按一般的android工程建就好了。
需要注意两点。

1.1.去掉标题栏

因为我建了一个新项目是空项目,头顶上的那个标题栏看着很难受,而且Launcher不需要,所以需要把他删了。

网上的大多方法是修改Android——res——styles.xml的样式。在sytle的标签下新加了一个item的标签,如下:

<item name="windowNoTitle">true</item>

整体来说看到这里,大概可以理解成android的样式和代码是分开的,而我们开发通过XML来方便的设置布局,那么以一定也有个解析其解析出参数传入框架。

注:这里可以也可以设置背景为透明,但本身我就想固定整个背景了,所以就不修改了

1.2.设置分类

这个分类是一定要设置的,后面替换Launcher会用到。
从AOSP的源码中可知道,读取 在桌面应用的时候会使用intent_filter找出类型是HOME的APP。所以把这个改了,然后重新启动板卡就会出现2个桌面应用,一个是系统自带的,还有就是这个launcher.

系统启动这里Launcher有个过程,大概是从init到启动Zygote,Zygote调AndroidRuntimeStart这个方法,里面会fork一个system_server的紫禁城,在通过反射调用启动SystemSercer。这里面会做很多很多工作,像加载JNI,启动Android服务等,然后里面有个ActivityMangerService它会启动调用第一个App:Launcher。

boolean startHomeActivityLocked(int userId) {
    if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL
            && mTopAction == null) {
        // We are running in factory test mode, but unable to find
        // the factory test app, so just sit around displaying the
        // error message and don't try to start anything.
        return false;
    }
    Intent intent = getHomeIntent();
    ActivityInfo aInfo =
        resolveActivityInfo(intent, STOCK_PM_FLAGS, userId);
    if (aInfo != null) {
        intent.setComponent(new ComponentName(
                aInfo.applicationInfo.packageName, aInfo.name));
        // Don't do this if the home app is currently being
        // instrumented.
        aInfo = new ActivityInfo(aInfo);
        aInfo.applicationInfo = getAppInfoForUser(aInfo.applicationInfo, userId);
        ProcessRecord app = getProcessRecordLocked(aInfo.processName,
                aInfo.applicationInfo.uid, true);
        if (app == null || app.instrumentationClass == null) {
            intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
            mStackSupervisor.startHomeActivity(intent, aInfo);
        }
    }

    return true;
}

修改使之成为默认启动APP的地方在AndroidManifest.xml文件里,增加:

 <activity android:name=".launcher">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.HOME" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>

参考:

CATEGORY_HOME

added in API level 1

public static final String CATEGORY_HOME

This is the home activity, that is the first activity that is displayed when the device boots.

Constant Value: “android.intent.category.HOME”

https://developer.android.com/reference/android/content/Intent#CATEGORY_DEFAULT

官方给的解释是CATEGORY_HOME是设备启动的第一个activity。

关于intent

这里特地研究一下这个intent的东西,总体概括为:多个activity间的通信方式。后面还会说到包管理器和intent过滤器的联合使用。

可以参考:

https://developer.android.com/reference/android/content/Intent

https://developer.android.com/guide/components/intents-filters?hl=zh-cn

2.选择布局方案

这里关于什么是布局和设置布局就不细说了,我直接在android下的app–res–layout里面的XML文件直接设置了,语法就是XML的语法,就是标签和属性是特定的一些,查文档就可以了,算事比较好理解。此外,底下的TEXT是对应的XML图,DESIGN对应的是预览,也可以直接在上面拖控件和布局。我这里之前会出现XML的DESIGN无法预览的情况。

解决方法是将Style中的style标签下的parent属性前部分加一个“Base.”

<style name="AppTheme" parent="Base.Theme.AppCompat.Light.DarkActionBar">

参考:https://blog.csdn.net/qq_33409436/article/details/82460618

先从大的布局上说,整个Launcher从逻辑上是分为2层的。一个是主界面的一层,另一层是APP操作的一层。

主界面上的东西无非是几个常用的APP和时间,中心对其排列就好,没什么特别的。

APP这层就比较麻烦了,因为APP从效果上看是多页的,APP下方需要留有一个空地放置当前页的指示图标。
另外还有个移动时候的浮动图标悬浮在其他图标上的。此外还有翻页时候的动画。

大概明确的就开始选android的布局。Android里分了六大布局,因为第一次用这个布局,所以就按各种布局的特性的来选择了(实际上后来发现其实选的不太好):

参考:
https://developer.android.com/guide/topics/ui/declaring-layout?hl=zh-cn

大概想好后,就这么设计了。感觉这里其实比较重要的还是对几个布局的灵活使用和对属性的了解。

2.1.整体布局设计

主体以FrameLayout的方式来层叠每个页。第一页是主页面,第二个是APP层,第三个是左右页动画移动效果层,第四个是移动时候的浮动图标层。这些布局都是占据满屏幕,也就是将属性设置成match_parent。因为都是mainAcitvicy下直属的,所以匹配parent都是满屏。

这些布局就被层叠在一起,其实还是靠代码的状态来设置层的VISIABLE属性来控制其可见性的。

2.2.主界面布局设计

主界面的布局较为简单,中间带着一个垂直页面横竖居中对齐的线性布局LinearLayout,包括一个日期TEXTVIEW和一个时间TEXTVIEW,还有就是一个水平布局LinearLayout的系统常用的APP层。这里的Layout里采用水平分布垂直居中对齐的布局,包括几个固定的常用的APP,这里先用按键的控件来简单代替一下。中间有个比较重要的功能按钮,点击后会进入到下一层也就是APP层。

布局如图所示:
在这里插入图片描述

2.3.APP层的设计

APP层整体来说分为两大块。

APP的布局

一个是APP的布局,这里我用RelativeLayout来隔离和下层的页布局。在RelativeLayout中有个TableLayout的布局,这里的TableLayout里面又有很多行,每行里又有很多列。因为APP看着排列的整整齐齐,而我这里把每个APP抽象成一个独立的控件(因为功能都一样)了,这点非常重要,这样我就抽象出来了。至于行列数是计算出来的。

其实这里在后来看来用TableLayout的选择并不好,这里可以选择自己在这个RelativeLayout上进行直接绘制。这样自由度会高很多,后面本来想做功能,但碍于TableLayout绕了远路,但幸好不至于问题那么大。

布局如图所示:
在这里插入图片描述

页指示布局

页指示器就是一个简单的LinearLayout,上面放着几个页指示灯的图标,程序里直接New出来的。这个LinearLayout被固定了高度,并且紧贴窗口的最下面。

2.4.页动画效果层

这块的动画效果我纠结了非常久。其实后面就知道了,感觉方案用错了。而且让过程变得非常麻烦。

这里我布置了两个和前面2.3.中一样的TableLayout,但始终处于隐藏的状态,为的是得到上一页和下一页的缓冲图,这里有个绑定的概念,会让程序变得非常麻烦,到后面如果有讲到就知道为什么那么麻烦了。
此外还有3个ImageView,分别代表当前缓冲图,上一页缓冲图,下一页缓冲图。这个用来做滑页过程中的动画。只有在滑动的过程中才会显示。
布局如图所示:
在这里插入图片描述

2.5.浮动图标层

这层没什么好讲的,就一个imageView,处于所有层的最上层。


总结

这里为了协调这些布局,层次方案和代码换了好多此次,但依旧存在很多可以改进的地方,比如说ViewGroup的概念来简化抽象这些效果,不过,在于第一次做,后面想到,看到的很多可以改的方案都没有实施了。其实我问了下专业做APP的人,他的思路也不太一样,他会采用计算位置对页面的内容全部重画的方式来进行实现。所以,其实也算是条条大路通罗马,思路不太一样罢了。

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值