LauncherRootView和DragLayer的布局过程

于 2015年05月20日 发布在 android开发 10 条评论 »

(系列文章点这里)

话说讲解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原理

于 2015年05月19日 发布在 android开发 7 条评论 »

(系列文章点这里)

在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需要说的就这么多。

 

 

 

招个Launcher开发

于 2015年04月23日 发布在 创业 8 条评论 »

话说公司要开始Launcher产品的开发了,不过目前开发人员有限,鉴于我博客80%的流量都来自于Launcher3相关的关键词,招聘信息就发在这里好了。

先说下我们公司的情况,公司于去年10月成立,目前有5个人(2个Android,一个Web,一个实习,一个boss),团队很作目前比较顺畅。先期项目是一个广告插件,现在已经基本完成,月盈利可以达到百万,目前由另外一位android开发维护中。

母公司是boss之前的创业公司,具有很强的盈利能力,每天可以达到50万左右的app推广能力,可以说Launcher做出来之后不愁用户量上不去。

Launcher产品的目标是要作为一个“内容分发中心”,这个内容可能是影视,音乐,小说等等,初期用户目标是三四线城市的用户。当然,盈利性保证的前提就是Launcher这个产品要好用,用户喜欢,如果你要加入我们的团队,这个就是我们要考虑并完成目标。

职位描述我也不想一条一条列出来了,大概就是要么很厉害,要么参与开发过大量用户的app,要么熟悉Launcher。只要你对Launcher产品有想法,都可以发封邮件过来我们聊一聊,“志同道合”一定会使合作十分愉快的。

就这样

 

如何给Launcher3添加左屏

于 2014年10月23日 发布在 android开发 7 条评论 »

(系列文章点这里)

如果你用过谷歌即时桌面(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图形 —— 从哪里开始?

于 2014年09月15日 发布在 android开发 2 条评论 »

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

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

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

Android图形必知必会

于 2014年09月1日 发布在 android开发 没有评论 »

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

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

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

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

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

 

Canvas.drawText绘制文字为什么会偏上?

于 2014年08月26日 发布在 android开发 3 条评论 »

如果你经常使用Canvas的draw***方法去绘制一些图像图形,你会知道绘制的时候坐标是从Canvas左上角开始计算的,如果想要把一个图像放到某个位置,直接drawBitmap传递图片左上角的坐标就行了。

那drawText就不一样了,如果你传递进去字符串,会发现文字的位置和你指定的不一样。

卧槽为啥。Android的文档也没有仔细说,打开源码一看,又跑到native代码里去执行了。经过我奋力地Google,终于把这个问题搞清楚了。

1408964085_thumb.jpeg

对于一段文字来说,如果你想把他画到Canvas上,首先你要确定这段文字的范围,即宽度和高度,那么怎么去取这一段的高度呢,如果你在网上搜,会有很多种答案,具体应该用哪一种呢?这要看你到底需要什么样的尺寸了。

Paint.getTextBounds: 当你通过这个方法来获取尺寸的时候,你可以得到能够包裹文字的最小矩形,就是图中红色边框的那部分,你可以得到一个Rect对象,包含这个最小尺寸的几个值。坑其实就在这里:这里的Rect对象坐标并不是以左上角为准的,而是相对于左边中间靠下位置的一个点,就是图中的黄色五角星。而这里水平的Baseline指的是字符串对齐的一条线(真正的含义可以需要更深入了解字体渲染的知识了)。既然这样,r.top就是一个负值了,r.bottom会是一个小一点的正值,r.left和r.right在图中画的都很清楚。通过r.width()和r.height()来获取尺寸。

那么文字的偏移就好说了,比如说你要把文字画在Canvas的左上角,坐标是(0,0),但是当你通过:

canvas.drawText(“dangwen”,0,0,paint);

来画文字的时候,发现只有文字的下半部分画出来了,因为你传递进去的参数应该是以Baseline为标准的,正确的方法是:

canvas.drawText(“dangwen”,-r.left,-r.top,paint);

Paint.getFontMetricsInt(): 当你通过这里方法来获取尺寸的时候,你获取的只是一个垂直方向上的尺寸,这里的ascent代表的是字体的上部,descent代表的是字体的下部,这里需要注意的是这和上面获得的Rect的top和bottom不太一样,他们比比ascent和descent距离稍微小一些,这些具体的高度可能和不同的字体和渲染方式有关系,这里就不深入了 #我是不懂#。

然后如果把文字写入TextView(图中蓝色部分)并且设置TextView的高度和宽度设为wrap_content,那么TextView的高度就正好是FontMetricsInt.top – FontMetricsInt.bottom, 那宽度呢? Paint.measureText()。

读书读不进去

于 2014年08月18日 发布在 吐槽 6 条评论 »

话说我这里说的读书不是读技术相关的书籍,是文学类,小说类的文章。

前一段时间读了《陆犯焉识》,是在《归来》上映之前。刚开始读的时候很感兴趣,但是读着读着就耐不下性子了,总是感觉,我靠作者怎么这么啰嗦,这都快高潮了怎么又加了一个人物,我真的不care这个多出来的人物是干什么的。然后就耽搁了,现在kindle上进度还是显示33%,不想翻看。回想上大学之后,真的是没有读过很多文学类的文章了,并不是没有兴趣,而是很急躁,读了一会就想着怎么还没看完,好像在完任务一样。我一直在向为什么。

后来一想,我操,我一定是急着想写代码!

PS:本篇使用Blogo(Mac App Store首页这几天放的那个)发布,挺方便的推荐使用。

Android和iOS开发的一些异同

于 2014年07月15日 发布在 android开发, iOS开发 没有评论 »

前一段时间用业余时间熟悉了下iOS开发,感受了下移动开发的另一面。

我同时参考了好几个资料,先看的是CodeSchool上iOS Path的Try Objective-CTry iOS,对于基本的语言和基本的在iOS上的应用熟悉了下,然后阅读《iOS开发指南:从零基础到App Store上架》来快速上手,这个过程中熟悉了Storyboard、常见的UI控件、表视图、导航设计和常用的设计模式。最后跟着@MengTodesign+code 完整地实现了一个客户端,学习了iOS的一些设计概念,CocoaPods以及开源库的使用,Sketch的使用等等。

对于一个陌生领域的开发者,接触到一个新的平台的时候,急需知道的是支撑一个应用的基本框架是什么,简单来说,就是应用的界面构成,UI和code的结合,事件处理,网络和常见问题的处理模式等等。就那我来说,我第一直觉就是想知道界面如何构成,因为这样是最直观并有成就感的,我当时的困惑是为什么 Try iOS 里面在代码里搞搞,界面就出来了,而书上的却一直使用拖拽,后来看到 “代码手写UI,xib和StoryBoard间的博弈,以及Interface Builder的一些小技巧”,才清楚了一些。再看看Android,情况好像好一些,因为基本上所有的教程都是从建立Activity并setContentView来描述界面的,不会有不清楚的地方,Android也可以用代码来控制UI,但是跟iOS一样不太好理解。

从开发环境来说,Xcode的使用体验已经非常良好了,UI很棒很简洁,该有的都有,自动补全让我很长时间不清楚Objective-C的函数到底是怎么构成的。。。不满的是在13寸rmbp上拖拽产生IBOutlet的时候屏幕空间明显不够用。同时也试用了AppCode,感觉一般。Android IDE是基于eclipse的,eclipse的界面实在是让人受不了,不过很成熟,遇到问题解决起来也快。我的个人项目已经切换到Android Studio了,界面上感觉良好,新的构建系统使得引用开源库十分方便,以后也会是方向。自动补全,重构,缩进,快速查找,配色,git支持等等两个开发环境都能满足日常需要,没有太多要吐槽的。

从布局组织上来说,Android使用的是xml,日常的界面都是在xml文件上进行更改,其中各种具体的属性可以精确控制各种组件,但是图形界面的预览功能还不是很成熟,问题多多,所以用的比较少。一般来说,一个Activity对应一个xml布局,布局中的对象可以指定一个id(类似于iOS的Identifier),在Activity的onCreate中通过findViewById()来获取布局中个个组件的对象。而iOS的界面组织可以通过一个Storyboard文件来完成,跟Android的Activity对应的概念是ViewController,在Storyboard可以拖一个ViewController出来形成一个界面,在上面添加UI组件,他可以有一个类继承ViewController作为这个界面的控制类,做一些响应UI事件的工作等等。

从开源库的使用来说,CocoaPods已经很不错了,Eclipse引用库类似,但是如果多个项目包含同名的库的话很麻烦。Android Studio很好地解决了这个问题。总体来说,开源库的引用方式也比较成熟了,而且各种库在github上也是数不胜数,庞大的开发者人群给我们贡献了很多优秀的项目,搞Android的点这里,搞iOS的点这里

从部署的角度来说,各有利弊。我目前还没有在App Store上发布过作品,不过对于苹果的审核有所耳闻,苹果对于品牌形象维护比较严格,可能存在的违规的都应尽量避免,很多开发者叫苦不迭,不过这也同时提高了App Store的应用质量。Google Play就相当于大杂烩,我的应用 Android Weekly 提交之后没隔几天就成功了,一般晚上更新,第二天早上就可以上线了。不过国内的应用市场真的是参差不齐,审核进度不一,提交内容不一,而且像豌豆荚还需要其他应用市场的详情页,真的是。。。

还有一些相同的地方,比如说列表控件,ListView对应UITableView,ListActivity对应UITableViewController,Adapter对应UITableViewDataSource,ListView.setOnItemClickListener对应UITableViewDelegate中的didSelectRowAtIndexPath。还有页面之间的跳转,Android上是用Context.startActivity(),而iOS是在两个ViewController之间连一条线(Segue),代表从一个页面调到另外一个页面的动作,在需要的时候(比如按钮按下)执行这条线就行了。

如果说,学会iOS需要100天,学会Android需要100天,那么把两者都学会则需要150天就OK了。很多移动设计的概念是相似的,触类旁通。当然,如果你要再深入的话,必须通过不断实现需求来提高,这种需求来源于日常工作,或者日常生活,比如说,用类应用不符合你的使用习惯,你需要做出一个更优秀的App来满足自己。

PagedView的原理 – 滑动

于 2014年07月15日 发布在 android开发 10 条评论 »

(系列文章点这里)

PagedView是用来左右滑屏的,Workspace正是他的子类,这里的Page就是桌面上一页一页的内容。其实他和ViewPager差不多,连名字都近似,不过PagedView更自由更复杂一些。

代码这么多,这得分析到啥时候去呀。

先说下这个滑动的过程吧。如果你还不熟悉android的触摸事件控制流程,点击这里。然后打开eclipse一步一步跟踪代码。
阅读全文 »

第 1 页,共 10 页12345...10...最旧 »