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

如果你经常使用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()。

继续阅读 →

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的原理 – 滑动

(系列文章点这里)

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

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

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

继续阅读 →

android常用的adb命令

(文章出处:http://www.growingwiththeweb.com/2014/01/handy-adb-commands-for-android.html)

 

查看所有已经连接上的设备:

这个命令可以查看所有已经连接上的设备和他的设备ID:

adb devices

如果有多个设备连接到电脑,可以通过 adb -s DEVICE_ID 来指定用哪一个。

 

安装应用:

通过install命令来安装apk文件,-r参数可以重新安装某个应用并保留应用数据。

adb install -r APK_FILE

#举例

adb install -r ~/application.apk

 

卸载应用:

adb uninstall PACKAGE_NAME

#举例

adb uninstall com.growingwiththeweb.example

(更多…)

继续阅读 →

Launcher3分析之拖动图标的流程——放下

(系列文章点这里)

我们来看下放下的过程,对应的触摸事件是ACTION_UP,我们直接跳到DragController的onTouchEvent。在处理ACTION_UP的时候多调用了一次handleMoveEvent,可能是因为ACTION_UP相对于上一次的时间也会有位置的变化。

up松手的时候会判断是是不是在“扔”图标(通过isFlingingToDelete),如果你用的不仔细的话,可能发现不了这个扔的过程:在说面拖动某个图标,然后快速甩向屏幕上方,就会触发删除的事件。这里检查你是否在扔是通过VelocityTracker来计算的,他会记录每次事件,然后计算出速度,包括X方向和Y方向的。如果不是在“扔”,就会调用放下图标最重要的一个方法,drop方法。
(更多…)

继续阅读 →

Launcher3分析之拖动图标的流程——移动

(系列文章点这里)

移动的逻辑还是比较清楚的,因为他抽象出来的模型十分易于理解的。

DropTarget是一个可放置(drop)区域的抽象,也就是我们松开手的时候想要把图标放到某个东西上,这个东西就是DropTarget,实现他的都是View,比如说文件夹,Workspace,删除区等等,你可以通过“ Open Type Hierarchy”来查看哪些类继承了DropTarget接口。下图是他比较重要的几个接口:

droptarget
(更多…)

继续阅读 →

Launcher3分析之拖动图标的流程——按住

(系列文章点这里)

拖动图标是Launcher的特色,非常形象地解释了如何改变桌面的布局,让小白用户也可以轻松上手,这背后所做的工作也是把复杂的东西简单化,简单到按下-移动-松开。

今天说一说这个按下的过程是如何产生的。这里说的按是长按,长按就要找到他的长按监听器。在Workspace中addInScreen方法最后,给图标设置的监听器是Launcher对象,他实现了onLongClick方法。

拖动的流程-按下
(更多…)

继续阅读 →

Android Studio试用总结

Android Studio是一年前Google I/O上推出的一款Android开发IDE,他基于JetBrains’ IntelliJ IDEA,目前还在preview阶段。增强了布局拖拽和预览功能,使用了新的构建系统Gradle,增加了android相关的重构和quick fix功能。之前听说用Android Studio来构建应用程序的时候会很方便,而且在学习常用Android开源项目的时候看到大部分都提供了对Android Studio的支持,所以就折腾了下,分享点经验给大家。

大多数Android Studio尝鲜者遇到的最大的障碍是Gradle,启动的时候非常慢,他需要从Maven库下东西,网络不稳定,这就坑了,不知道别人怎么解决的,我是翻墙搞的,光这一点就阻止了Android Studio大陆范围内推广,而且多人合作的时候同伴不一定习惯这个东西。网络的问题如果解决后,就需要了解Gradle的构建原理,这也需要费一番功夫的,去年谷歌I/O上有40分钟的视频来说明他是怎么工作的,链接在这里,讲的很清楚,在Linkedin上查到主讲人的头衔是”Tech Lead for the Android SDK at Google”。

你可以新建一个HelloWorld工程感性的认识一下Android Studio的使用。你肯定会在这个过程拿他和Eclipse做对比,很可惜的是,他们的目录结构不像AppCode和Xcode一样目录完全兼容可以相互无缝替换,不能直接import对方的工程,虽然现在Eclipse提供了功能可以把现有项目导出来再导入到Android Studio中,但是这个过程并不是无痛的,也会有些小问题,很麻烦;反过来,要把Android Studio中的项目导出来到Eclipse中,那就是纯属找事了。
(更多…)

继续阅读 →

细说ItemInfo

(系列文章点这里)

要理解ItemInfo,就要先理解桌面有哪几种东西,即有哪几种Item。

  • 小工具:就是AppWidget,时钟小工具,天气小工具,等等。
  • 快捷方式:快速启动应用的图标,一个应用可以有多个重复的快捷方式,workspace和hotseat上的都是快捷方式
  • 文件夹:可以把多个快捷方式放到一块的,用来分类,减少屏幕空间占用。

这些东西都一些共同的,可以抽象出来的东西,有自己的宽度和高度,都有所在的位置和第几等等,而ItemInfo就是抽象出来的东西,打开ItemInfo.java,其中包含的就是这些特征,然而,最关键的是ItemInfo对象和数据库记录是一一对应的。其中有个id的成员,代表的就是数据库中ID。

对于不同的item,ItemInfo也有不同的子类,小工具对应的是LauncherAppWidgetInfo,他增加了小工具的信息(查android文档:AppWidgetProviderInfo);快捷方式对应的是ShortcutInfo,他增加了启动一个Activity所需的Intent信息;文件夹对应的是FolderInfo,他增加了文件夹是否打开的标签,文件夹内图标的信息等等。

ItemInfo的成员有几个值得说说:
(更多…)

继续阅读 →