LiveData – 数据监听神器

LiveData我认为是Jetpack构架组件里最有用一个。

以前做项目的时候,不同页面里会经常显示一些通用信息,比如当前登录用户的用户名和头像等。这些信息都保存在数据层,你的UI如果想及时响应数据的变化,那么必须想办法去监听数据的变化。假设你的N个页面都有显示头像,那么用户进入你的App的设置页面,更改了头像,然后显示了头像的这些页面是不是得及时改变?

我以前是这么做的。使用EventBus,在各个需要显示头像的Activity里注册事件,用户改完了头像,发送一个事件,然后各个页面自己去更新。

LiveData提供了一个简洁优雅的方式来完成这种通用需求。我们可以给LiveData注册观察者,一旦数据变化,就可以响应。配合LifeCycle使用,可以获取当前Activity的LifeCycle,当LifeCycle到达DESTROYED状态的时候,把观察者给删除。也即是说,你在Activity的onCreate开始监听数据之后,就不用操心再在onDestroy去写代码了,取消监听是自动的,你省事儿了。

(更多…)

继续阅读 →

ViewModel能替代onSaveInstanceState么?

ViewModel出现之前,处理横竖屏的最佳实践就是使用Activity的onSaveInstanceState()来保存Activity销毁之前的数据,在onCreate()或者onRestoreInstanceState()的时候取出数据。有了ViewModel之后,你可能会思考,这两种方式都有啥区别,是不是只用ViewModel就行了?

仁者见仁智者见智。这两种方式还是稍稍有些不一样的,我们简单做个回顾:

(更多…)

继续阅读 →

使用ViewModel存储Activity数据

ViewModel是用来存储UI相关的数据的,Activity在横竖屏变化的时候会被销毁,然后再重新创建,如果你把你的数据存储在Activity中,那么新建Activity的时候这些数据就没有了,用户就会很疑惑,屏幕旋转了下怎么界面的东西就没了?所以需要把界面相关的数据存储下来,下回接着用。使用ViewModel就可以做到这一点。

你可能会问,我的App只有竖屏是不是需要考虑这些了?

答案是否定的。这种Activity重新创建的情况有很多,只要发生了configuration changes,就会重建,横竖屏切换只是其中最典型的一种,其他还有十几种,比如字体变化和语言切换等,你总不可能每一种都不管吧,用户瞎操作出问题了咋办。

(更多…)

继续阅读 →

生命周期组件LifeCycle

我们知道Activity有着自己的生命周期,在不同的阶段有着不同的回调函数,比如onCreate、onResume等等。这些回调是非常常用的,因为你时常要利用它们完成一些事情,比如你如果用EventBus的话,可能需要分别在onCreate和onDestroy中注册和取消监听器。类似这样的操作多了去了,需要一个方便的工具来做这些操作,那就是LifeCycle组件

LifeCycle类把生命周期给抽象了出来,包含了不同的状态和事件,事件一来,状态就变,参考下图:

(更多…)

继续阅读 →

初识Android Jetpack

Android Jetpack就是谷歌提供的一堆帮助你加速开发的工具,你不必再去重复地写样板代码,对于经常使用的一些逻辑代码,Android Jetpack已经帮你实现了,你能把更多的时间放在业务逻辑上。

Android Jetpack并不是全新的,他将我们所熟知的工具都整合到一起,比如AppCompat、LiveData、DataBinding等。又增加了一些工具比如WorkManager、Paging和Navigation等。

(更多…)

继续阅读 →

LauncherRootView和DragLayer的布局过程

(系列文章点这里)

话说讲解Launcher3的布局过程真是要耗费一番功夫了,因为他的过程很难理解, 一般来说我们普通应用的布局过程其实都是按照系统自带的ViewGroup来的,如果自定义了一些布局方法,开发者在考虑xml标记的布局文件的时候,还要考虑运行时的一些动态变化。

在接着往下读之前,你最好先去了解一下View的绘制流程。

在主布局launcher.xml中, 最外层是LauncherRootView。

我们发现它只是重写了一个方法, View.fitSystemWindow(rect), 那这个方法是干什么的呢?查了官方文档,还是看不懂他到底是做什么的,但是倒是会用了。如果你的View在status bar或者navigation bar之下,那么这个方法会传递进去一个参数,说明这个View的四周有多少是被status bar或者navigation bar挡住了的。这个方法的会调用父类的setInsets()方法,我们来看看父类InsettableFrameLayout。

InsettableFrameLayout继承了FrameLayout, 并且实现了父类的一些方法,总的来说,他的目的就是给子View都设定一个Margin值,防止被status bar或者navigation bar挡住。如果一个View已经是Insettable的了,那么就忽略他。在Launcher3中,Workspace, DragLayer和AppsCustomizeTabHost都实现了这个接口,那么他们自身就不会被加Margin, 而搜索栏和Hotseat都没有实现这个接口,那么说明他们在被加到父亲上得时候会被设一个Margin,最后的到得效果就是搜索栏刚好在status bar之下(gravity为top,topmargin是status bar的高度),Hotseat刚好在navigation bar的上方(gravity为bottom,bottomMargin为navigation bar的高度)。

LauncherRootView的实现已经很明了了,他的孩子只有DragLayer,DragLayer实现了Insettable接口, 所以他不会加上margin。DragLayer其实也是InsettableFrameLayout的子类, DragLayer的孩子有Workspace,Hotseat,AppsCustomizeTabHost 和 SearchDropTargetBar等等 。

DragLayer的布局过程稍微复杂一点,不过还好。DragLayer自己定义了一个LayoutParams, 在FrameLayout原有的LayoutParams基础之上,添加了几个参数,x,y和customPosition。在DragLayer中添加孩子的时候,都会自来于提供的LayoutParams来计算View的布局位置。

我们来看下DragLayer重写的onLayout方法[如果还不清楚这个方法是干啥的话,赶快搜一搜]:

2015-05-20_17-47-41

刚开始先按照FrameLayout的方式进行布局,然后,遍历所有的孩子的LayoutParams, 如果这个孩子的LayoutParams参数中含有的customPosition参数为true,那么就会使用参数里执行的x坐标和y坐标。

为什么需要这样的功能呢?比如说我们在拖拽一个图标的时候,这个图标其实是放在了DragLayer里的[drag layer,拖拽层嘛],如果给拖拽的图标分配了一个自定义位置的布局参数,那么布局完之后就可以显示在指定位置了。拖拽一个AppWidget和显示文件夹的时候也是同样道理。

理解了Launcher最外层的两个ViewGroup[LauncherRootView,DragLayer]的布局方式之后,相信你了解的更深入了一些,同学还学了自定义ViewGroup的时候如何自定义LayoutParams布局参数。再往Launcher的底层看得话就是Workspace,CellLayout了,他们会更为复杂,接下来的博文会介绍到。

继续阅读 →

IconCache原理

(系列文章点这里)

在Launcher3中,IconCache从名字上来看就是用来做应用图标缓存的,以前没有在意过这个类,最近刚好需要研究下,就做个笔记。

IconCache不仅会保存图标,还会保存应用的title, 我们可以看到缓存都是保存在mCache中的,他是个HashMap,键是ComponentName和UserHandleCompat组成的对象, 值是一个对象CacheEntry, CacheEntry的三个成员是图标,标题和内容描述[其实我也搞不懂]。

在Launcher启动的时候, 会在LauncherApplication中初始化LauncherAppState,而他会初始化IconCache对象, 刚开运行的时候IconCache肯定是空的, 只有在Launcher.java 这个Activity启动之后才会有缓存。在Launcher.onCreate()方法中,执行了IconCache的flushInvalidIcons, 会清空现有的缓存,如果Activity的方向发生了改变[即图标的大小发生了变化],原来的缓存可能都没有用了。那什么时候IconCache会被完全清空呢, 也就是图标和文字无效的时候,在用户的语言发生了变化之后会被清空,因为获取的图标和title很可能已经不同了。

下面来看图片是怎么加进去的。添加图标最关键的方法是IconCache.cacheLocked(), 这个方法会获取某个Component[Activity]的图标和标题,并存入mCache中, 我们看到这个方法名最后是“Locked”表明,方法调用的时候已经保证是线程安全的,可以看到IconCache.cacheLocked()每个调用的方法都有关键字synchronized。

最后IconCache还会生成一个系统默认图标,如果一个Activity由于某种原因我们没有找到对应的图标, 那么IconCache会返回一个默认图标, 这个图标是从系统取到的资源, 资源id是android.R.mipmap.sym_def_app_icon, 不同的系统这个资源对应的图标是不一样的, 比如4.4 和 5.1的系统默认图标就是不一样的。

好了IconCache需要说的就这么多。

继续阅读 →

如何给Launcher3添加左屏

(系列文章点这里)

如果你用过谷歌即时桌面(Google Now)的话,你一定知道滑到桌面最左屏会是一个搜索界面,而且会呈现一些卡片比如天气,行程,新闻等等。这个东西也是我们可以自定义的,谷歌即时桌面其实也是基于Launcher3的。想必AOSP中一定有源码可以控制最左屏。

2014-10-23_14-14-48

 

如果你已经导入了Eclipse项目,可以到Launcher类中寻找Launcher.hasCustomContentToLeft()方法,从字面上理解就是左边是否有自定义内容。之后跟自定义内容相关的方法或者变量的名字基本都跟“custom content”相关。这个方法默认是返回false的,也就是最左屏不存在。不妨替换成true试试,运行后我们可以发现的确可以滑到左屏的,但是什么东西都没有,只留一个搜索框。

下一步是向自定义内容中添加View了,添加的方法是Workspace.addToCustomContentPage(…),你可以传递一个View和与左屏相关的回调接口。然后就OK了。你可以在需要的时候调用这个方法,什么时候调呢?Google已经帮你想好了。就是Launcher.addCustomContentToLeft()方法,这个方法会在Launcher.onFinishBindingItems()中调用到,也就是桌面加载结束的时候加载自定义内容。着实帮你省去了很多开发时间。

所以说,如果你想要在左屏添加天气呀,系统设置呀等是完全OK的,毕竟传递个View进去就行,你也可以弄个Fragment专门控制左屏的内容和逻辑。这样的设计还是挺符合用户的需求的,毕竟Google的东西我们在大陆没办法正常使用,你如果能设计一个类似的,国内用户可以正常使用的包含左屏信息页的Launcher,我觉得还是挺有价值的。

再说说技术实现吧,既然是在Workspace上添加一个View,那么他应该是放在CellLayout里面的,但是为什么他和其他的CellLayout大小不一,而且占据全屏呢。我们找到桌面加载每一屏的代码,在Launcher.bindScreens()里面,会在加载完正常的图标屏之后再加载自定义的左屏,通过Workspace.createCustomContentPage()。然后会发现他给新生成的CellLayout的LayoutParams新添加了属性值,isFullScreenPage=true,一旦有了这个属性,在PagedView(Workspace的父类)的onMeasure和onLayout就会对其做特殊处理,保证其占据全屏。

 

继续阅读 →

Android图形 —— 从哪里开始?

你一定遇到过Android现有的UI控件无法满足项目需求的情况,一般来说你可能需要将多个UI组合起来,或者说需要自己控制一些触摸事件,又或者为了得到更好的图形效果等等。这个时候就需要自定义控件了。

自定义控件的一个重点就是实现就是去重写他的绘制过程,也就是View.onDraw(Canvas canvas)方法。这个里面就是绘制图形的过程了,比如说一个Button,绘制按钮和按钮上的文字都是在它自己的onDraw方法中完成的。如果你想给ImageView加个蒙版,可以继承ImageView并且重写他的onDraw方法,调用完父类的onDraw之后,再自己Canvas来画一个黑色的半透明矩形,这样就可以对现有的控件进行加工了。

Canvas是什么呢?是画布,你绘制东西的时候就是绘到了这个画布上,给用户显示的东西也是这个画布上的东西。通常来说,当你调用到onDraw方法的时候对应的View已经确定了他的宽度和高度(关于View的measure和layout过程以后再详述)。那么在Canvas上,你画的范围就只有View对应的大小了。我们用代码来描述下:
(更多…)

继续阅读 →

Android图形必知必会

最近接触了Android图形方面的知识,发现其中还是有很多比较深入的东西的。

我决定写一系列的文章来讲解android.graphics.*包种的各种类的用法,如果用这些东西来给我们的应用UI增添一些色彩。如果你熟练运用这些东西,不仅可以可以实现惊艳的效果,还可以增加界面的流畅度,减少内存使用等等。

android.graphics.* 包里面主要由以下一些类:

  • Canvas
  • Bitmap及其相关的类
  • Xfermode及其子类
  • Paint及其相关类和内部类
  • Shader及其子类
  • Rect,Color,Point,Path等基础类
  • 等等等

好了,坑先挖在这里。。后续文章点页面上方的“ANDROID”

 

继续阅读 →