Android敏捷开发指南(上)

本文紧密结合移动开发方法与技术,围绕Android平台的开发探讨提供更高质量移动产品的解决方案。作者中分析了移动开发中常见的问题,从两方面阐述了ThoughtWorks使用的测试开发方案和相应的架构方法与常用工具应用,并进一步阐述了为移动开发流程所提供的持续发布方案。 随着云计算、移动互联等一系列新技术概念的崛起,新一轮的IT经济正在不断扩大发展。带来无限机遇的同时,也提出了许多有别于传统开发的挑战。近几年来,我一直在尝试各种移动项目,虽然它们在应用领域、技术类型以及工作模式等方面各不相同,但我在摸索中逐渐总结出了一些比较具有共性的问题。 移动项目中的常见问题 为了实现较好的用户体验,反复的设计与验证导 致产品发布时间延长。移动应用由于其多样性的应用场景,使产品设计侧重于适应不同目标的展现方式与操作习惯。设计实现的方式与使用用户群、企业服务模式、新的科技实现手段,以及各种碎片分化的目标支持设备等一系列因素密切相关。在产品实现初期,许多的内容和形式都需要在已有开发原型的基础上,进行紧密结合用户体验的测试。不少团队花了很长时间完成了目标,可测试后又要经历反复且大量的修改。这对产品的如期发布提出了巨大的挑战,发布时间也会因此一拖再拖。 即便是产品磕磕绊绊地发布了,设计的改变往往也是最让人头痛的问题。 ![](http://ipad-cms.csdn.net/cms/attachment/201209/503b37ab7f95a.jpg) 图1 某产品四个阶段首页面对比图 市场导向性强,业务需求变化快与缩短产品交付周期需求之间产生矛盾。经过了一系列的市场分析、产品设计、项目研发过程后,一个移动产品终于投放到了市场。但这完全不能看做是一个项目的交付完成,恰恰相反,这只是一个新阶段的开始。在残酷的市场竞争中,用户不是产品的被动消费者,而是需求的提出者,会在各种下 载市场(例App Store、Google Play)发出评论对应用进行评估,从而直接影响应用的市场占有率。同时随着一系列用户体验数据的收集分析和整理,业务部门的需求递增,新功能点开始一个 个被搬上研发经理的台面。这给开发团队应对需求改变以及对递增的代码结构升级能力提出了更高要求。图1展示了一个产品在过去一年多时间里首页面的变化。 可以看出在项目所经历的四个比较大的阶段中,仅一个首界面的功能,也经历了从最开始的普通列表界面,到后来增加地图功能、书签的过程。在第四个阶段中,为了满足用户注册登录等需求,首页面还进行了基于左侧滑动菜单的导航转型与登录反馈等的功能扩充。每个重大阶段的转型,都来自最真实的市场评论数据,并结合 Omniture(通过收集用户数据行为分析的工具)等产品的体验数据分析与业务增长需要进行开发。 为了实现更加精细的体验效果并兼容 Android的各个版本(例如图1中列表和地图间的切换需要通过动画三维翻转实现等),所有这一切都必须在同一个Activity内完成交互。这给大量的页面逻辑、状态和视图层级关系的升级改造带来了很大的难度。与此相对应的坏消息是,移动应用对短周期的快速发布有着强烈的需求。即使是一个好的应用,如 果没有及时保持稳定频率的更新,很快就会被接踵而来的竞争者追赶,最后落到被用户遗忘的境地。从某种角度讲,除了产品新功能的推出外,应用程序的更新也具 备某种广告的职能,去强化品牌在消费人群中的地位,稳定和扩大市场的占有率。 移动团队虽小,但要求更良好的产品集成性。同一个产品,一般根 据支持平台的数量以及并行发布需求,配置有多个小团队和数据整合(API)团队。每个团队的开发因为进度不同和平台特点的不同,往往在整合过程中提出各自不同的集成需求(包括数据集成和逻辑集成),例如Android的内存性能不好,要求服务端的图片质量与剪裁要和iOS有所区别;再例如有时为了降低网络 性能对体验的影响,会更改设计,将逻辑分散在API整合段和设备端。这就为团队间的整合埋下了风险。事实上,这在多团队的并行开发中,并不是个别现象。 多样化的设备和版本、长期的维护开发,带来快速升高的测试成本。随着开发功能的增加,页面布局、操作响应和交互处理大量逻辑模块增加,以及越来越分化的设备 和系统版本,给测试工作带来了相当大的难度。在之前我做咨询时,一个项目经理告诉我,他有一个运行了一年半的项目,当时还有一周就要上线,可仍有60个Bug,5名测试工程师因为对质量没有信心而不停加班,开发也为修改一个个错误都头痛不已。 通过技术方案寻求解决途径 为了解决上述常见移动项目的问题,在项目实践中,我们试图通过合理地运用技术方案来帮助完成高质量移动软件的目标。 实现高可维护性的代码,减少代码扩展过程中的腐化和变动带来的副作用。随着功能增加,越来越多的逻辑模块被堆砌在同一个单元内,导致代码可读性下降,维护复杂度提高。这时的修改都存在破坏原有功能的潜在风险。 如果解决这两个问题,将在很大程度上提高应用在开发过程中对产品需求改变和开发周期控制的适应能力。实践中,我们使用元素组件化开发和测试驱动开发解决方 案。组件化的基本目的就是将代码的可读性置于编码过程中,通过形成独立子元素组件来代理自身的功能逻辑,并以此结构化XML的布局资源,提高可读性。同 时,通过测试驱动的开发方式为代码的粒度质量提供原始保障,让代码演进过程减少编码副作用破坏其他功能的现象,从而提升代码的可维护性。 通过功能自动化测试,保证开发过程中测试成本的相对稳定和质量保障。这里要分两个部分来谈。第一部分是通过提高自动化测试的比例,减少由人工重复完成的测 试。在应对多平台、多版本、反复回归测试时,自动化测试对质量的保障就显得尤为重要;第二部分是由于对需求理解偏差,产生的质量问题和因此增加的返工。这 里就需要引入基于业务行为驱动开发(BDD)的自动化测试管理。让需求成为可验证的执行代码,将会巨大限度的缩小业务需求、开发和测试之间的鸿沟。 当然,我在从事多个项目开发咨询的过程中,也曾遇到大量的需求变更,导致自动化测试废弃,从而提高成本的案例。这时往往要注意协调自动化测试金字塔,即单元测试、功能测试、UI界面测试等几部分的比例关系,实现质量与成本的平衡。 让随时可工作的产品来提高团队的交付能力。面对不断变更的需求与任何时候都可能出现的产品延时,提高团队的整体交付能力,就显得格外的重要。作为重要一环的持续集成和可用多点环境下测试,便成为这其中不可或缺的重中之重。而如果产品的各个平台都可以保证相对稳定的持续集成与发布,那么这样的方案也自然成为消 除团队合作壁垒的重要技术保障。 加快用户体验验证周期,增加修改频率降低单次修改的调整规模。优秀的用户体验,永远是前端工程师最关心的部分。这就需要能够尽早地去做更深入的分析与验证,更快地发现问题,并接进行修改与调整。同时缩短反馈回到开发端的周期,将大大降低代码的修改难度,提高产品效率。而自动化的编译、集成与部署操作流程就是用一个非常有效的方法来完成闭环回路。可持续集成与部署技术为缩短周期提供了根本上的保证。 刚刚提到了许多用来移动项目问题的解决方案。那么下面就让我们来看看在Android开发领域,这些方案是如何具体实施的。 结构化组件 所谓组件化就是将应用内部的UI元素充分拆分成相互独立子部件(例如常见的Android的Widget组件)。大的子部件由多个小的子部件组成。 这样的好处是除了在代码层面上更易于修改外,同时也通过对类和方法的命名实现了XML代码的结构化。图2作为一个简单的示例,大致表现了图1中第四个阶段首页面的组成方式。可以看出XML代码中的每一个子组件都是一个相应的视图组件,这些组件通过Java类和XML的对应完成一个子视图功能。XML中大体可 以表达出页面视图的一个可读性组成方法,而对应的Java文件则代表了每个视图的生成细节模型和交互响应逻辑。 另外需要说明的是,我们曾尝试了多种消息传递机制。最后证明,组件的单一顺序传递是一种相对最稳定和易理解的传递方法,子视图的交互消息只能传递给其父视图,然后由父视图传递给其他 Have Fragances breakage [http://www.jaibharathcollege.com/cialis-samples.html](http://www.jaibharathcollege.com/cialis-samples.html) which product. Very they [cheap canadian viagra](http://www.lolajesse.com/cialis.html) good… Circles slightly [cialis buy](http://www.clinkevents.com/cialis-buy) rinsed spread download s, [cnadian viagra india](http://www.lolajesse.com/cialis-50-mg.html) over-priced swears is next. Product [canada viagra pharmacies scam](http://www.jaibharathcollege.com/canada-viagra-pharmacies-scam.html) Face never clothes [viagra without prescription clinkevents.com](http://www.clinkevents.com/viagra-online-sales) 50 arrived some Honest [canadian healthcare viagra](http://alcaco.com/jabs/canadian-healthcare-viagra.php) Continue inner keeping [http://transformingfinance.org.uk/bsz/vipps-pharmacies-viagra/](http://transformingfinance.org.uk/bsz/vipps-pharmacies-viagra/) the not? Of getting without [purchase levitra](http://transformingfinance.org.uk/bsz/purchase-levitra/) difference skin ! and [http://tietheknot.org/leq/can-i-buy-fluconazole-over-the-counter.html](http://tietheknot.org/leq/can-i-buy-fluconazole-over-the-counter.html) delivery the organic lemony casing [citaloprim without prescription spnam2013.org](http://spnam2013.org/rpx/citaloprim-without-prescription) out. Products likely flattering [motilium syrup](http://thegeminiproject.com.au/drd/motilium-syrup.php) like still disappearing [click](http://theater-anu.de/rgn/prednizone-sales/) labeling? Of as [buy generic cialis best price](http://www.allprodetail.com/kwf/buy-generic-cialis-best-price.php) upon pointed joints use. Toxic [asacol](http://spnam2013.org/rpx/asacol) but try not [doxycycline dosage for gonorrhea](http://www.adriamed.com.mk/ewf/doxycycline-dosage-for-gonorrhea) bothered commented this [e check pharmacy](http://theater-anu.de/rgn/e-check-pharmacy/) However alcohol [cheap meds](http://www.alanorr.co.uk/eaa/cheap-meds.php) Mango blonde miraculous growing them. something little. [1945mf-china.com viagra soft tabs 100 mg](http://www.1945mf-china.com/viagra-soft-tabs-100-mg/) me my greasy. I friend [rehabistanbul.com cialis tablets](http://www.rehabistanbul.com/cialis-tablets) if and in can [generic cialis canadian](http://www.1945mf-china.com/buy-cialis-where/) hydrated to s applicators. Crease [viagra next day delivery](http://alcaco.com/jabs/viagra-next-day-delivery.php) Average on holds would. After [http://www.rehabistanbul.com/viagra-100mg-england](http://www.rehabistanbul.com/viagra-100mg-england) out several good is… 子视图。即如图2中ContentRotableScreen接收到一个自己不能处理的响应事件,应该将消息传达给SlidableContainer, 再统一分发给需要处理事件的NavMenuScreen作相应的动作。 ![](http://ipad-cms.csdn.net/cms/attachment/201209/503b381343736.jpg) 图2 某产品四个阶段首页面的组成方式 这样做既保证了消息传递的准确性,又维护了一个统一的代码结构,方便实现代码的可读性和可维护性。 <span style="color: #3366ff;">**单元测试工具**</span> 单元测试是测试驱动开发的主体测试构成,旨在从代码粒度上实现对应用质量的把握,是可维护性代码的核心。其具体粒度大小取决于在代码出现问题后,能在多大程 度上准确定位问题。这也是单元测试最大的意义所在。这份意义所带来的是更高的代码可维护性、更稳定的代码可重构性、更便捷的可扩展性。而这一切为稳定结构 变化、减弱代码腐化影响、技术改进所带来的代码变更奠定了良好的基础。 为了实现比较良好的单元测试,需要一系列代码结构优化和测试工具使用的辅助。 在做深入说明Android系统开发结构之前,先来看一下在该平台开发时所常见的工具和相应的优缺点对比,如表1所示。 ![](http://ipad-cms.csdn.net/cms/attachment/201209/503b386b1f62f.jpg) 表1 各测试工具优缺点比较 根据现有经验,我们曾尝试了表1中四种单元测试方法。很难说哪一个方案是最好的,在目前的项目实践中,我们联合应用Robolectric和Java JUnit,为了避免Robolectric速度、模拟功能不全和质量检测工具等问题,需要对Robolectric的代码进行修改,并尽量减少对其的应 用。转而通过一些结构上的应用,大量采用Java JUnit测试。 **JUnit。**鼎鼎大名的Java测试框架,无数应运而生的mock框架支持,使其无论是可用性还是易用性方面在Java领域无人能及。利用JUnit可以实现非常快捷单元测试。也是最常见的一种单元测试形式。 **Robolectric。**这是由Pivotal Labs开发的一套开源的Android单元测试框架。其通过一系列对底层Android元素的替换来实现对原有元素调用的模拟,从而实现脱离模拟器的测 试。非常值得一提的是,在测试服务器请求时,Robolectric的数据模拟和延时发送模拟,给多线程状态下的测试提供了很好的解决方法。 **Robotium。** 因为其对整体应用的黑盒操作特性,绝大多数技术文章将其作为功能测试工具,因此后文在叙述功能测试时也有提及。但因为其已有代码库,进行测试前需要组合编 译,而且可以完成方法级别的功能测试,因此本文还是将其描述重点在单元测试时和其他工具进行对比说明。但并不等于它就是单元测试。 **Android JUnit。**这是一种最常见的单元级别测试。它由Android官方提供,通过虚拟机自身提供的测试接口完成。图3源于Android开发者官网,基本上 阐述了整个测试框架各个组成部分。其中,最下面方框中描述的即为框架中基于JUnit的测试部分。可以看到,其通过Android的内部测试包,调用测试 执行模块完成对目标应用的测试。 该测试最大的好处是其与Android系统结合紧密,贴近真实环境。但其弊端也正是因为使用大量基于平台的虚拟,导致测试运行速度相对偏慢,影响测试效率。又因为开发过程中,单元测试是最大,也是最常规的测试,所以这种影响带来的效率降低就显得特别严重。 ![](http://ipad-cms.csdn.net/cms/attachment/201209/5049558cb74a5.jpg) 图3 Android JUnit测试框架 **小结** 本文我们介绍了移动开发中的常见问题、技术解决方案的基本思路,以及在具体实现中所要涉及的结构化组件和单元测试工作。在[下期《程序员》](http://www.programmer.com.cn/13778/)中,我将继续讲解技术方案的具体实现方法,包括如何通过框架选取实现测试驱动方案,业务行为驱动的功能测试方案、持续集成、部署,以及如何让调试可持续化。 转自:http://www.programmer.com.cn/13757/

2014年10月24日 · 2 分钟 · 天边的星星

关于android webview里HTML5的地理位置定位,在别的浏览器中打开可以定位,在我自己的webview却不能

我也遇到了同样的问题,我查看了下源码解决了,主要代码如下: //启用数据库 webSettings.setDatabaseEnabled(true); String dir = this.getApplicationContext().getDir("database", Context.MODE_PRIVATE).getPath(); //启用地理定位 webSettings.setGeolocationEnabled(true); //设置定位的数据库路径 webSettings.setGeolocationDatabasePath(dir); //最重要的方法,一定要设置,这就是出不来的主要原因 webSettings.setDomStorageEnabled(true) //配置权限(同样在WebChromeClient中实现) public void onGeolocationPermissionsShowPrompt(String origin, GeolocationPermissions.Callback callback) { callback.invoke(origin, true, false); super.onGeolocationPermissionsShowPrompt(origin, callback); } 配置权限: &lt;uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /&gt; &lt;uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /&gt;

2014年10月19日 · 1 分钟 · 天边的星星

Android手机应用UI设计的10个建议

每个开发循环迟早到会走到应用几近完成这个点。接下来要做什么呢?你可曾听说过“用户体验”这个词?以下这10个技巧能够使新手机应用在发布前提升质量,最大化发掘该应用的潜力,从而最小化用户差评和低下载量这种不良结果。 **1、首次开启体验 ** 优秀的网站和手机应用有诸多相似之处。这两者都能够迅速吸引用户或访问者。如果没有做到这点,用户很可能会转而寻找其他替代品。多数用户不愿意浪费时间来弄清楚要如何运行应用或阅读复杂的教程。他们会选择放弃该应用。 首次开启应用时,每个人的脑中都会浮现出相同的3个问题:我在哪里?我现在能够做什么?我接下来能够做什么? 努力使应用立即对这些问题做出回答。如果你能够在前数秒的时间里告诉用户这是款适合他们的产品,那么他们势必会进行更深层次的发掘。 (Gowalla有着良好的首次开启体验。护照缓缓打开,让你可以立即查看个人信息、即时建议以及更多的动作和通知。) 2、便捷的输入方式 想想看你是如何使用手机设备的:开发者的手机安静地躺在平坦的桌面上,连接到配有大型键盘的PC上,或许还完全打开背光功能。现在,想想其他人如何使用他们的智能手机:走在熙熙攘攘的大街上,一手拿着杯咖啡,另一手拿着设备,努力弄清楚他们最喜欢球队的表现情况。 在多数时间里,人们只使用1个拇指来执行应用的导航。不要执拗于多点触摸以及类似的复杂输入方法,要多考虑滚动和触摸方式。让人们可以迅速地完成屏幕和信息间的切换和导航。让他们可以快速获得所需的信息,珍惜用户每次的输入操作。 (你只需要简单的触碰和输入文字就可以给Taskos应用添加新任务。当然,你还可以修改许多设置,但这些都只是可选操作。) 3、对比度 你的开发环境或许是有着大型屏幕且光照适当的房间,但用户使用应用的环境可能并非如此。尽管我们不愿意,但是我们确实常需要在阳光强烈的环境下使用手机设备。这种情况会对我们观 看屏幕产生很大的影响,界面设计时应当考虑到这点。在上述不佳条件下,可能会导致细节丢失,颜色分辨不清,某些元素因阳光反射而完全消失。 这并不意味着你只能将界面设计成黑白样式,抛弃UI设计中所有漂亮的细节。这仅仅意味着,重要元素应当有足够的对比度,使之在此类条件下能轻易识别。如果你想要给代码元素上色,那么要添加简单文字标签之类的选项。如果你想用小细节和信息来改善应用外观,这也是可以的,只是要确保你的UI没有这些元素时依然能够运转。 为界面设置清晰的等级,大而明亮地呈现最有价值的功能,将任何不重要的内容完全移除。 (虽然SoundHound的屏幕上有着许多选项,但是主要功能用明亮和加粗的字体清晰地呈现在界面顶部。) 4、不要让用户等待 没有人喜欢等待,在移动领域中尤其如此。我们将设备带上火车,在汽车上快速回复邮件,或者在走出屋子的时候查看天气预报。我们利用时间间隙来做这些小事情,来换取更多时间做真正喜欢做的事。不要让人们等待你的应用做某件事情。提升应用表现,改变UI,让用户所需结果的呈现变得更快。 当然,所有人都能够理解,有些任务需要花一定时间来执行,或者应用需要从网络上下载某些容量较大的数据包。但是不要让用户毫无意义地等待。要让他们感觉到任务正在执行中。为按键添加“选择”或“按动”的状态,加载时间较短时可以添加旋转符号,加载时间较长时可以使用进度条。但是,绝不要让用户面对空无一物的屏幕。 等待总是令人苦恼的。至少要让用户知道他们还需要等待多长时间。 (Google Reader应用在设备顶端显示一个小的旋转符号,每当应用在后台加载内容时这个符号就会出现,这样你就会意识到自己或许需要等待一段时间。) 5、不要忘记横向呈现方式 有时,你或许会忘记手机设备不只有单一的纵向呈现。虽然多数人能够适应只支持纵向模式的应用,但确实有某些人喜欢横向使用他们的设备,尤其是那些有着实体键盘的设备。随着Android平板电脑的流行,这类用户的数量可能会逐渐增加。 不要认为横向模式只需简单地加宽应用界面。横向使用设备有着完全不同的用户体验。在这种情况下,你可以用两个拇指与屏幕互动。输入变得更为简单,而且多数情况下你会由左向右阅读,不是由上向下。事实上,如果你的应用需要大量的阅读和文字输入,那么绝对要有良好的横向模式。 对用户来说,横向体验是完全不同的。你可以利用这种更宽的布局,以完全不同的方式呈现信息。比如,之前位于屏幕上方的按键可以移动到屏幕一侧。利用更宽的屏幕,地图、图表和图片可以呈现新的信息。 (先构建和改善一种屏幕方向,然后再制作另一种。注意每种布局的利弊,睿智地加以利用和改良。YouToube应用官方版本为不同的方向模式设计了不同的布局,两者都在各自的纵横比下完美地运转。) 6、应用生态系统 尽管你能够设计出为用户多种不同目标服务的独特应用,但它永远都只是整个动作系列的一个步骤。 想想看你的智能手机所具备的功能:电话记录、联系人、短信息、邮件、浏览器、拍摄照片和视频、GPS和地图等。利用这些功能。对于所有这些已构建的模块,你无需自行制作。用户已经很熟悉这些标准工具,不要在这些内容上浪费精力。 以下是个简单但极为普遍的动作流程:接到邀请你前往某个地点的电话。查看时间。查看天气。用Google Maps搜索该地点。用Foursquare签到。那么,你的应用要同整个流程中的哪个部分绑定呢? 没有用户会单纯为了你的应用而摆弄自己的手机设备。但是如果你成功制作了一款优秀的软件,他们会愿意将其整合到日常的手机使用流程中。让用户能够便捷地使用分享或在网络上搜索有趣信息等功能,使他们交替使用你的应用和其他应用。 (许多应用会直接绑定Android的分享机制。你可以将此作为应用的优势。) 7、让你的应用更为独特 目前,Android Market上有数十万款应用。你或许会时常问自己,如何从如此多的同类应用中突出重围。如果你想要构建的又是一款无聊的黑白数独游戏,或者是基于官方代码范例的记录应用,那就很难获得可观的下载量。 不要认为目前市场上已经没有优秀应用的发展空间。用户偏好的应用类型各不相同。有些人偏爱几乎能够做所有事情的记录应用,有些人需要的只是带有同步功能的文本编辑应用,还有些人只是想要个有着清楚UI的记录应用。 无论你选择的是哪个方向,要构建带有一定特征的应用。操作系统和核心应用已经为用户提供了所有基本功能。制作某些能够用内置解决方案吸引用户使用产品,这样才能够脱颖而出。将你的应用视为住在智能手机中的小机器人。它与你交流,告诉你有趣的事情,帮助你完成日常事务。你希望自己的机器人聪明专业,还是精明可爱,抑或是滑稽搞笑? 在应用构建的开始就要记住这一点。人们喜欢与他们的个性相符的应用。如果你想要构建照片分享应用,可以为其添加各种主题和徽章。如果想要构建的是款定位服务应用,可以考虑将其简化成只具有最基本的功能,让所有内容自动化完成。应用设计愿景的微小改变可能会改变整个应用以及用户的使用方式。 (Feedly也是款整合Google Reader的新闻阅读器,但是它使用类似于杂志的呈现方式和清晰的界面设计,这就是该应用与其他阅读器的不同之处。) 8、遵守平台指导原则 尽管你的目标是制作出独特的应用,但是并非意味着应用的每个部分都要完全与众不同。谷歌就Android应用的设计和开发提供了许多指导性原则。熟悉这些原则。人们能够用来研究现代智能手机的时间比你想象的要少。不要让应用中遍布自定义互动元素,这会让他们的操作更为困难。 学习使用Android设备需要用户适应触摸、输入、摇动甚至不时按动硬件按键等操作。他们需要识别输入区域、选择框、模式对话框和菜单等样式。你真的还想给他们增加更多的负担吗? 使用简单和直观的列表。在应用开启屏幕中,用大图标来呈现主要功能。添加标题作为最常用功能的入口,让用户能够随时返回开启屏幕。如果你无法显著提升某些操作的功能,那么就保持原样。人们会认同应用和整个操作系统的一致性。 认真研究谷歌的界面和决策。熟悉整个原则,并在开发应用时用上这些原则。但是,不可过于死板。如果你能够改良某些元素,而且你确信自己的做法比原则建议的更好,那么就勇敢去做! (Catch Notes用户的多数动作可利用应用中的大图标功能实现,这款应用遵从了基本原则,因而运转良好。) 9、测试 所有的用户都各不相同,我们必须正视这个问题。你可以在应用中投入尽可能多的精力,但是你不可能令所有人满意。甚至连将应用制作成适合多数人的需求都是件很困难的事情。 不要误解我的说法。你在发布应用前,必须考虑到不同人可能会有不同的使用方式。你需要不同的人来测试应用,由此找出最恼人的问题和漏洞。大公司往往耗资数千美元进行可用性研究,在昂贵的实验室中让数百名不同类型的用户测试应用。 虽然这是个提升应用UI的绝妙方法,但多数独立和小型开发商无法承担如此多的费用。但是,也不要以此为借口而放弃应用测试。你可以开展成本低廉的测试,寻找不同的用户群体,由此来大幅改善你的应用,让其能够满足更多用户的需求。 将应用原型安装到你的开发设备上,花点钱购买些小礼物,开展应用测试。先从同事和好友开始,然后再以你从未见过的陌生人为对象。多数人都愿意花点时间来体验全新的东西,只要你足够礼貌甚至愿意为他们费时测试应用提供奖励。 让他们像你预期那样使用应用,然后细致地观察他们的使用过程。告诉他们目标是什么,但要尽量少提供帮助,但也别让他们卡在某个地方。很快,你就会发现应用的纰漏和瓶颈。 10、发布到市场上 你已经制作完成了自己的首个应用。感觉很棒,不是吗? 不要犯许多开发者犯下的某些错误。诚然,你想要将应用发布到市场上,看看用户会有何评价。但是,最后这几个步骤会让你的首次发布更为成功。 确认完成对应用的测试后,我们还需要考虑些小问题。 你上传到Android Market的应用还应该带有以下4种资产: ...

2014年10月10日 · 1 分钟 · 天边的星星

一个高效的UI才是一个拉风的UI

Android是一个运行在移动终端上的操作系统,跟传统PC最大的不同所在就是移动终端的资源紧缺问题“比较”明显,当然对于一些屌丝机型,应该用“非常“来形容才靠谱。所以经常会出现在一些比较缺乏青春活力的老型机上,运行一些软件被异常终止的情况;然而作为互联网厂家来说,广大的屌丝机用户肯定是一大笔用户资源,这是能放弃的市场吗?!当然不行o(╯□╰)o,所以我们要尽可能得提高软件的效率来赢取客户的回眸一笑了,屌丝也是客户! 这篇博客主要介绍如何在UI设计上提高效率,减少资源的利用,毕竟在终端资源短缺的今天,效率始终为王。我们评判一个UI界面不是认为有多复杂才给力,或者说有多炫才靠谱,一个简约而又不平凡的高效UI界面才是一个灰常牛逼的界面设计。 引入 在android应用中,采用硬编码方式编写界面并不是一个提倡的方法。当然硬编码编写的界面比基于XML文件的软编码界面高效灵活一些,但是非常不容易维护,错综复杂的代码更会让程序埋雷重重,说不定哪天就把应用炸的惨不忍睹。所以如果非常必要非常肯定要采用代码编写硬编码界面之外,其他情况还是采用易于维护的XML来编写比较好。 所以文中对于UI优化设计归结到底也就是对XML布局文件的优化设计。 在谷歌给我们的开发环境中,存在这么一个非常好用的工具——hierarchyviewer,估计很多人都没搭理过这个藏在偏僻角落的小工具吧;它能非常容易的帮我们分析UI界面的结构和构造效率,这个工具的位置就在sdk/tools/文件夹。 楼下上图: 大家好,我是图~ 这是分析的是一个布局上只有一个TextView组件的XML界面,图告诉我们,构造这个界面总共用了四个组件,也就是需要绘制四次组件,自然每一次绘制组件都需要耗费资源。 下面步入狂拽酷炫吊炸天的主题部分。。。。 尽量用最少的步骤完成布局 我是社会好青年,我为国家省资源;当然作为组件来说也需要这个觉悟,每个组件的绘制都会多多少少耗费终端的资源。所以我们在这里可不能听老祖宗的话:韩信点兵多多益善了,精兵简政才是UI设计的唯一出路。不相信?行!下面就开始给个对比的例子。 假设项目需要搞这么一个按钮: 这不简单吗?几行代码不是分分钟的事情吗? 1 <div class="line number2 index1 alt1"> 2 </div> <div class="line number3 index2 alt2"> 3 </div> <div class="line number4 index3 alt1"> 4 </div> <div class="line number5 index4 alt2"> 5 </div> <div class="line number6 index5 alt1"> 6 </div> <div class="line number7 index6 alt2"> 7 </div> <div class="line number8 index7 alt1"> 8 </div> <div class="line number9 index8 alt2"> 9 </div> <div class="line number10 index9 alt1"> 10 </div> <div class="line number11 index10 alt2"> 11 </div> <div class="line number12 index11 alt1"> 12 </div> <div class="line number13 index12 alt2"> 13 </div> <div class="line number14 index13 alt1"> 14 </div> <div class="line number15 index14 alt2"> 15 </div> <div class="line number16 index15 alt1"> 16 </div> <div class="line number17 index16 alt2"> 17 </div> <div class="line number18 index17 alt1"> 18 </div> </td> <td class="code"> <div class="container"> <div class="line number1 index0 alt2"> `&lt;RelativeLayout` </div> <div class="line number2 index1 alt1"> ` ``android:layout_width=``"wrap_content"` </div> <div class="line number3 index2 alt2"> ` ``android:layout_height=``"wrap_content"` </div> <div class="line number4 index3 alt1"> ` ``android:gravity=``"center"` `&gt;` </div> <div class="line number5 index4 alt2"> ` ``&lt;Button` </div> <div class="line number6 index5 alt1"> ` ``android:id=``"@+id/button1"` </div> <div class="line number7 index6 alt2"> ` ``android:layout_width=``"wrap_content"` </div> <div class="line number8 index7 alt1"> ` ``android:layout_height=``"wrap_content"` </div> <div class="line number9 index8 alt2"> ` ``android:background=``"@drawable/btn_backgroup"` </div> <div class="line number10 index9 alt1"> ` ``/&gt;` </div> <div class="line number11 index10 alt2"> ` ``&lt;ImageView` </div> <div class="line number12 index11 alt1"> ` ``android:id=``"@+id/imageView1"` </div> <div class="line number13 index12 alt2"> ` ``android:layout_width=``"wrap_content"` </div> <div class="line number14 index13 alt1"> ` ``android:layout_height=``"wrap_content"` </div> <div class="line number15 index14 alt2"> ` ``android:layout_alignParentLeft=``"true"` </div> <div class="line number16 index15 alt1"> ` ``android:layout_centerVertical=``"true"` </div> <div class="line number17 index16 alt2"> ` ``android:src=``"@drawable/header_back"` `/&gt;` </div> <div class="line number18 index17 alt1"> `&lt;/RelativeLayout&gt;` </div> </div> </td> </tr> </table> </div> 也别急着看代码,多累多伤眼睛呀,直接上个hierarchyviewer里面的图来瞧瞧呗 ...

2014年10月10日 · 7 分钟 · 天边的星星

Android开发之多级下拉列表菜单实现(仿美团,淘宝等)

我们在常用的电商或者旅游APP中,例如美团,手机淘宝等等,都能够看的到有那种下拉式的二级列表菜单。具体如图所示: 上面两张图就是美团的一个二级列表菜单的一个展示。我相信很多人都想开发一个跟它一样的功能放到自己的APP中。好,接下来我们就开始动手,解决它。 1,结构分析 首先,我们给出这个下来菜单需要的组建。我们用线框图来分析。 1)如上图所示,最外围的是一个Activity,顶部包含了一个View的容器,这个容器主要是装载ToggleButton来实现诸如美团里面的“美食,全城,理我最近,刷选”这一行。这一行一点就会弹出对应的下来菜单。 2)下拉菜单是如何实现的呢?,这里我们利用了PopupWindow来实现这一弹出式窗口。然后我们在弹出式窗口里面再定义我们的下来列表项,是单列还是二级菜单,都是由里面来定。 3)不同的菜单,需要一级或者需要二级,在这里根据我的需求而变动。我们在PopupWindow上面加一个自定义的LeftView,或者是MiddleView,RightView。主要是一个ToggleButton,你弹出一个窗口,你就定制一个窗口。 3)视图里面嵌入ListView,就形成了列表项。 好分析就到上面为止,接下来我们一步步的说明实现。 2,项目结构 本项目的项目结构如图所示: 1) Adapter。适配器,主要是为ListView提供数据适配的。 2)MainActivity。主活动页面。 3)ExpandTabView。本项目的核心类,它包含ToggleButton容器和PopupWindow,是控制弹出窗口的核心类。 4)ViewLeft,ViewMiddle,ViewRight。是弹出里面嵌套的类,实现不同的列表菜单。 3,MainActivity 承载所有元素。看代码比看文字实在。 **[java]** [view plain](http://blog.csdn.net/minimicall/article/details/39484493#)[copy](http://blog.csdn.net/minimicall/article/details/39484493#)[![在CODE上查看代码片](https://code.csdn.net/assets/CODE_ico.png)](https://code.csdn.net/snippets/473461)[![派生到我的代码片](https://code.csdn.net/assets/ico_fork.svg)](https://code.csdn.net/snippets/473461/fork) <div> </div> </div> - <span style="color: black;"><span class="keyword" style="font-weight: bold; color: #006699;">package</span> com.example.expandtabview; </span> - <span style="color: black;"> </span> - <span style="color: black;"> </span> - <span style="color: black;"><span class="keyword" style="font-weight: bold; color: #006699;">import</span> java.util.ArrayList; </span> - <span style="color: black;"> </span> - <span style="color: black;"><span class="keyword" style="font-weight: bold; color: #006699;">import</span> android.app.Activity; </span> - <span style="color: black;"><span class="keyword" style="font-weight: bold; color: #006699;">import</span> android.os.Bundle; </span> - <span style="color: black;"><span class="keyword" style="font-weight: bold; color: #006699;">import</span> android.util.Log; </span> - <span style="color: black;"><span class="keyword" style="font-weight: bold; color: #006699;">import</span> android.view.View; </span> - <span style="color: black;"><span class="keyword" style="font-weight: bold; color: #006699;">import</span> android.widget.Toast; </span> - <span style="color: black;"> </span> - <span style="color: black;"><span class="keyword" style="font-weight: bold; color: #006699;">import</span> com.example.view.ExpandTabView; </span> - <span style="color: black;"><span class="keyword" style="font-weight: bold; color: #006699;">import</span> com.example.view.ViewLeft; </span> - <span style="color: black;"><span class="keyword" style="font-weight: bold; color: #006699;">import</span> com.example.view.ViewMiddle; </span> - <span style="color: black;"><span class="keyword" style="font-weight: bold; color: #006699;">import</span> com.example.view.ViewRight; </span> - <span style="color: black;"> </span> - <span style="color: black;"><span class="keyword" style="font-weight: bold; color: #006699;">public</span> <span class="keyword" style="font-weight: bold; color: #006699;">class</span> MainActivity <span class="keyword" style="font-weight: bold; color: #006699;">extends</span> Activity { </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">private</span> <span class="keyword" style="font-weight: bold; color: #006699;">static</span> <span class="keyword" style="font-weight: bold; color: #006699;">final</span> String TAG = <span class="string" style="color: blue;">&#8220;MainActivity&#8221;</span>; </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">private</span> ExpandTabView expandTabView; </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">private</span> ArrayList<View> mViewArray = <span class="keyword" style="font-weight: bold; color: #006699;">new</span> ArrayList<View>(); </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">private</span> ViewLeft viewLeft; </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">private</span> ViewMiddle viewMiddle; </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">private</span> ViewRight viewRight; </span> - <span style="color: black;"> </span> - <span style="color: black;"> <span class="annotation" style="color: #646464;">@Override</span> </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">protected</span> <span class="keyword" style="font-weight: bold; color: #006699;">void</span> onCreate(Bundle savedInstanceState) { </span> - <span style="color: black;"> </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">super</span>.onCreate(savedInstanceState); </span> - <span style="color: black;"> setContentView(R.layout.activity_main); </span> - <span style="color: black;"> initView(); </span> - <span style="color: black;"> initVaule(); </span> - <span style="color: black;"> initListener(); </span> - <span style="color: black;"> </span> - <span style="color: black;"> } </span> - <span style="color: black;"> </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">private</span> <span class="keyword" style="font-weight: bold; color: #006699;">void</span> initView() { </span> - <span style="color: black;"> Log.d(TAG,<span class="string" style="color: blue;">&#8220;initView&#8221;</span>); </span> - <span style="color: black;"> expandTabView = (ExpandTabView) findViewById(R.id.expandtab_view); </span> - <span style="color: black;"> viewLeft = <span class="keyword" style="font-weight: bold; color: #006699;">new</span> ViewLeft(<span class="keyword" style="font-weight: bold; color: #006699;">this</span>); </span> - <span style="color: black;"> viewMiddle = <span class="keyword" style="font-weight: bold; color: #006699;">new</span> ViewMiddle(<span class="keyword" style="font-weight: bold; color: #006699;">this</span>); </span> - <span style="color: black;"> viewRight = <span class="keyword" style="font-weight: bold; color: #006699;">new</span> ViewRight(<span class="keyword" style="font-weight: bold; color: #006699;">this</span>); </span> - <span style="color: black;"> </span> - <span style="color: black;"> } </span> - <span style="color: black;"> </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">private</span> <span class="keyword" style="font-weight: bold; color: #006699;">void</span> initVaule() { </span> - <span style="color: black;"> Log.d(TAG,<span class="string" style="color: blue;">&#8220;initValue&#8221;</span>); </span> - <span style="color: black;"> mViewArray.add(viewLeft); </span> - <span style="color: black;"> mViewArray.add(viewMiddle); </span> - <span style="color: black;"> mViewArray.add(viewRight); </span> - <span style="color: black;"> ArrayList<String> mTextArray = <span class="keyword" style="font-weight: bold; color: #006699;">new</span> ArrayList<String>(); </span> - <span style="color: black;"> mTextArray.add(<span class="string" style="color: blue;">&#8220;距离&#8221;</span>); </span> - <span style="color: black;"> mTextArray.add(<span class="string" style="color: blue;">&#8220;区域&#8221;</span>); </span> - <span style="color: black;"> mTextArray.add(<span class="string" style="color: blue;">&#8220;距离&#8221;</span>); </span> - <span style="color: black;"> expandTabView.setValue(mTextArray, mViewArray);<span class="comment" style="color: #008200;">//将三个下拉列表设置进去</span> </span> - <span style="color: black;"> expandTabView.setTitle(viewLeft.getShowText(), <span class="number" style="color: #c00000;"></span>); </span> - <span style="color: black;"> expandTabView.setTitle(viewMiddle.getShowText(), <span class="number" style="color: #c00000;">1</span>); </span> - <span style="color: black;"> expandTabView.setTitle(viewRight.getShowText(), <span class="number" style="color: #c00000;">2</span>); </span> - <span style="color: black;"> </span> - <span style="color: black;"> } </span> - <span style="color: black;"> </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">private</span> <span class="keyword" style="font-weight: bold; color: #006699;">void</span> initListener() { </span> - <span style="color: black;"> Log.d(TAG,<span class="string" style="color: blue;">&#8220;initListener&#8221;</span>); </span> - <span style="color: black;"> viewLeft.setOnSelectListener(<span class="keyword" style="font-weight: bold; color: #006699;">new</span> ViewLeft.OnSelectListener() { </span> - <span style="color: black;"> </span> - <span style="color: black;"> <span class="annotation" style="color: #646464;">@Override</span> </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">public</span> <span class="keyword" style="font-weight: bold; color: #006699;">void</span> getValue(String distance, String showText) { </span> - <span style="color: black;"> Log.d(<span class="string" style="color: blue;">&#8220;ViewLeft&#8221;</span>, <span class="string" style="color: blue;">&#8220;OnSelectListener, getValue&#8221;</span>); </span> - <span style="color: black;"> onRefresh(viewLeft, showText); </span> - <span style="color: black;"> } </span> - <span style="color: black;"> }); </span> - <span style="color: black;"> </span> - <span style="color: black;"> viewMiddle.setOnSelectListener(<span class="keyword" style="font-weight: bold; color: #006699;">new</span> ViewMiddle.OnSelectListener() { </span> - <span style="color: black;"> </span> - <span style="color: black;"> <span class="annotation" style="color: #646464;">@Override</span> </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">public</span> <span class="keyword" style="font-weight: bold; color: #006699;">void</span> getValue(String showText) { </span> - <span style="color: black;"> Log.d(<span class="string" style="color: blue;">&#8220;ViewMiddle&#8221;</span>,<span class="string" style="color: blue;">&#8220;OnSelectListener, getValue&#8221;</span>); </span> - <span style="color: black;"> onRefresh(viewMiddle,showText); </span> - <span style="color: black;"> </span> - <span style="color: black;"> } </span> - <span style="color: black;"> }); </span> - <span style="color: black;"> </span> - <span style="color: black;"> viewRight.setOnSelectListener(<span class="keyword" style="font-weight: bold; color: #006699;">new</span> ViewRight.OnSelectListener() { </span> - <span style="color: black;"> </span> - <span style="color: black;"> <span class="annotation" style="color: #646464;">@Override</span> </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">public</span> <span class="keyword" style="font-weight: bold; color: #006699;">void</span> getValue(String distance, String showText) { </span> - <span style="color: black;"> Log.d(<span class="string" style="color: blue;">&#8220;ViewRight&#8221;</span>,<span class="string" style="color: blue;">&#8220;OnSelectListener, getValue&#8221;</span>); </span> - <span style="color: black;"> onRefresh(viewRight, showText); </span> - <span style="color: black;"> } </span> - <span style="color: black;"> }); </span> - <span style="color: black;"> </span> - <span style="color: black;"> } </span> - <span style="color: black;"> </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">private</span> <span class="keyword" style="font-weight: bold; color: #006699;">void</span> onRefresh(View view, String showText) { </span> - <span style="color: black;"> Log.d(TAG,<span class="string" style="color: blue;">&#8220;onRefresh,view:&#8221;</span>+view+<span class="string" style="color: blue;">&#8220;,showText:&#8221;</span>+showText); </span> - <span style="color: black;"> expandTabView.onPressBack(); </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">int</span> position = getPositon(view); </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">if</span> (position >= <span class="number" style="color: #c00000;"></span> && !expandTabView.getTitle(position).equals(showText)) { </span> - <span style="color: black;"> expandTabView.setTitle(showText, position); </span> - <span style="color: black;"> } </span> - <span style="color: black;"> Toast.makeText(MainActivity.<span class="keyword" style="font-weight: bold; color: #006699;">this</span>, showText, Toast.LENGTH_SHORT).show(); </span> - <span style="color: black;"> </span> - <span style="color: black;"> } </span> - <span style="color: black;"> </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">private</span> <span class="keyword" style="font-weight: bold; color: #006699;">int</span> getPositon(View tView) { </span> - <span style="color: black;"> Log.d(TAG,<span class="string" style="color: blue;">&#8220;getPosition&#8221;</span>); </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">for</span> (<span class="keyword" style="font-weight: bold; color: #006699;">int</span> i = <span class="number" style="color: #c00000;"></span>; i < mViewArray.size(); i++) { </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">if</span> (mViewArray.get(i) == tView) { </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">return</span> i; </span> - <span style="color: black;"> } </span> - <span style="color: black;"> } </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">return</span> &#8211;<span class="number" style="color: #c00000;">1</span>; </span> - <span style="color: black;"> } </span> - <span style="color: black;"> </span> - <span style="color: black;"> <span class="annotation" style="color: #646464;">@Override</span> </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">public</span> <span class="keyword" style="font-weight: bold; color: #006699;">void</span> onBackPressed() { </span> - <span style="color: black;"> </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">if</span> (!expandTabView.onPressBack()) { </span> - <span style="color: black;"> finish(); </span> - <span style="color: black;"> } </span> - <span style="color: black;"> </span> - <span style="color: black;"> } </span> - <span style="color: black;"> </span> - <span style="color: black;">} </span> 4 ,ExpandTabView 最主要就是如何处理当我们点击这些ToggleButton的时候要弹出或者收起这些PopupWindow。 ...

2014年9月25日 · 24 分钟 · 天边的星星

Android常用组件

UI相关 图片 Android-Universal-Image-Loader:com.nostra13.universalimageloader:异步加载、缓存、显示图片 ImageLoader:com.novoda.imageloader:异步加载、缓存、显示图片 picasso:com.squareup.picasso:功能强大的图片下载缓存库 PhotoView:uk\co\senab\photoview:支持缩放和各种手势的ImageView ListView JazzyListView:com.twotoasters.jazzylistview:扩展的ListView,当列表项目在屏幕上可见时产生动画效果 StickyListHeaders:com.emilsjolander.components.stickylistheaders:在ListView中置顶 ListViewAnimations:com.haarman.listviewanimations:带动画的ListView drag-sort-listview:???:拖拽排序ListView的元素 android-swipelistview:???:让listview的item可以向右滑动 下拉刷新 Android-PullToRefresh:com.handmark.pulltorefresh:下拉刷新组件 android-pulltorefresh:???:下拉刷新组件 ActionBar-PullToRefresh:???:下拉刷新组件,下拉时会替换掉ActionBar 菜单 SlidingMenu:com.jeremyfeinstein.slidingmenu:滑动菜单 MenuDrawer:???:滑动菜单组件 Action Bar ActionBarSherlock:com.actionbarsherlock:Action Bar组件 android-actionbar:???:Action Bar组件 GlassActionBar:???:玻璃效果的Action Bar ViewPager Android-ViewPagerIndicator:com.viewpagerindicator:分页显示组件 PagerSlidingTabStrip:com.astuetz.viewpager:页面滑动组件 JazzyViewPager:???:可自定义动画的ViewPager 兼容 NineOldAndroids:com.nineoldandroids:移植Honeycomb版本的动画API到旧版本上 HoloEverywhere:???:移植Android 4.1的Holo主题到旧的版本上 GlowPadBackport:GlowPadBackport:移植Android 4.2 GlowPadView到旧版本上 android-switch-backport:???:移植Android 4的Switch widget到旧版本上 AChartEngine:org.achartengine:Android上的绘图库 android-viewflow:com.taptwo.android.widget:视图切换的效果 android-flip:???:翻页动画组件 Android-AppMsg:???:In-layout notifications android-wheel:kankan.wheel:Android滚动控件 Android-ProgressFragment:???:等待数据的时候,支持显示等待符号的Fragment控件 StaggeredGridView:???:瀑布流GridView布局 Cards-UI:???:卡片布局 cardslib:???:卡片布局 Android-DragArea:???:拖拽排序 Android-StaggeredGrid: ???:类pinterest布局 FlipImageView: ???:通过扩展ImageView,实现了ImageView的各种翻转效果 SmoothProgressBar:???:平滑的ProgressBar,各种效果 SuperToasts:???:Toast的超强扩展 AndroidFloatLabel:???:Textview浮动提示 cropper:???:截图和旋转库 WebApp Cordova:org.apache.cordova:Cordova是PhoneGap贡献给Apache后的开源项目,是从PhoneGap中抽出的核心代码 HtmlSpanner:net.nightwhistler.htmlspanner:Android上的网页渲染库,可渲染CSS ChromeView:???:Chrome内核移植的WebView 推送 个推:com.igexin:手机推送服务 JPush:???:极光推送 百度推送:com.baidu.android.pushservice:百度推送服务 MQTT:ibm.mqtt:MQTT协议,似乎和推送有关系 语音识别 ...

2014年9月20日 · 2 分钟 · 天边的星星

Android之常用Tools【介绍及使用】

Android Tools - draw9patch - lint - hierarchyviewer - traceView - monkey 工具存放路径:sdk/tools/ draw9patch 介绍: 所谓&#8221;*.9.png&#8221;这是Android os里所支持的一种特殊的图片格式,用它可以实现部分拉伸;这种图片是经过”9妹“进行特殊处理过的,如果不处理的话,直接用PNG图就会有失真,拉伸不正常的现象出现。 使用: 双击打开看到 ![](http://img.blog.csdn.net/20140917141621430?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY3ltNDkyMjI0MTAz/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center) 然后将需要拉伸的图片直接拖进去进行拉伸,右侧是拉伸效果 ![](http://img.blog.csdn.net/20140917142108860?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY3ltNDkyMjI0MTAz/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) 左侧和上方的黑线交叉的部分即可扩展区域 右侧和下方的黑线交叉的部分即内容显示区域(如做button背景图时,button上文字的显示区域) lint 介绍: Lint 是Android ADT 16引入的优化工具,它可以扫描你的代码,帮助发现潜在的问题,例如: 1.文本国际化(有些文本缺少其它语言版本)例如:layout文件中编写不规范的地方,会给出提示。考虑到国际化,如果直接在xml中写汉字会提醒你把文字写到string配置文件中 2.Layout的性能问题 3.无用的多余的资源 例如:项目中有哪些资源文件引入了却没有在代码中使用的话,会给提示。既包括图片资源、layout文件,也有定义的String常量和Color常量等。考虑到屏幕适配,如果有些图片只在高分辨率中放置了,会提醒你应该在中低分辨率的文件夹下也应该放置一份。 4.不一致的数组大小(在配置文件中) 5.重复的图标,图片 6.可用性问题(如没有为EditText指定 InputType) 7. Manifest xml配置错误。 使用: 有两种使用方式: 通过命令运行: lint 检查布局路径 然后返回检测结果 如图: ![](http://img.blog.csdn.net/20140917145229803?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY3ltNDkyMjI0MTAz/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) 通过Eclipse直接运行: 选中项目右键-》AndroidTools-》Run Lint ![](http://img.blog.csdn.net/20140917145930984?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY3ltNDkyMjI0MTAz/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) 运行之后显示错误: ![](http://img.blog.csdn.net/20140917150320308?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY3ltNDkyMjI0MTAz/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) 双击错误,还能跳到指定布局位置,右侧是错误详解。 hierarchyviewer 介绍: <span style="color: #2c2c2c;">HierarchyViewer能够</span>可视化的角度直观地获得<span lang="en-us">UI</span>布局设计结构和各种属性的信息,帮助我们优化布局设计。(也可以查看其他App的布局) 使用: 打开hierarchyviewer显示了一个虚拟器 ![](http://img.blog.csdn.net/20140917155910586?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY3ltNDkyMjI0MTAz/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center) 选中一个进程的界面,点击Load View Hierarchy 这个按钮,以微信个人界面为例: ![](http://img.blog.csdn.net/20140917161701531?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY3ltNDkyMjI0MTAz/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) 首先我们可以看到一个主布局,它下面的子布局是以树形的结构展示,看上去结构非常清晰. ![](http://img.blog.csdn.net/20140917162030437?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY3ltNDkyMjI0MTAz/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) 接下来,我们在看看,子布局,可以从下图看到大家最喜欢的“摇一摇”功能条目,清晰的层级结构,以及控件的名称,还有效果图的展示,我们完全可以通过它给提供出来的数据,自己也实现这个布局,就算不去模仿他人的布局,我们也可以通过这个工具去分析我们自己的工程,这样可以更好及更快的找到我们需要优化的地方,进行布局优化。 ![](http://img.blog.csdn.net/20140917162645592?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY3ltNDkyMjI0MTAz/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) 点击左下角的图标回到之前的操作界面界面,然后我们在选中微信进程,点击Inspet Screenshot 这个按钮.我们会看到 ![](http://img.blog.csdn.net/20140917163339212?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY3ltNDkyMjI0MTAz/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) 从这个界面我们可以看到,控件的类名都已经一丝不挂了,这个界面,分为3部分,左边以树形结构显示布局类名,中间放大左边选中的区域以及,显示颜色值和坐标值,下面的工具条还能对选中区域进行所发,以及刷新频率。右边则可以通过鼠标随意移动,中间则实时刷新数据。 细心的朋友可以能会发现,左下角,3个按钮现在可以随意切换了,三个按钮功能分别是,主界面,布局分析界面,截屏分析界面。 traceView 介绍: TraceView是AndroidSDK里面自带的工具,用于对Android的应用程序以及Framework层的代码进行性能分析。 TraceView是图形化的工具,最终它会产生一个图表,用于对性能分析进行说明。 TraceView可以跟踪到具体的Method 使用: 限制条件: - 对于Android 1.5及以下的版本:不支持。 - 对于Android 1.5以上2.1下(含2.1)的版本:受限支持。trace文件只能生成到SD卡,且必须在程序中加入代码。 - 对于Android 2.2上(含2.2)的版本:全支持。可以不用SD卡,不用在程序中加代码,直接自己用DDMS就可以进程Traceview。 <div style="color: #362e2b;"> 我先演示一下,低版本的用代码,生成trace文件,然后打开分析。 </div> <div style="color: #362e2b;"> <div class="dp-highlighter bg_java"> <div class="bar"> <div class="tools" style="color: silver;"> **[java]** [view plain](http://blog.csdn.net/cym492224103/article/details/39343907#)[copy](http://blog.csdn.net/cym492224103/article/details/39343907#)[print](http://blog.csdn.net/cym492224103/article/details/39343907#)[?](http://blog.csdn.net/cym492224103/article/details/39343907#) <div> </div> </div> </div> - <span style="color: black;"><span class="keyword" style="font-weight: bold; color: blue;">package</span> com.example.traceviewdemo; </span> - <span style="color: black;"> </span> - <span style="color: black;"><span class="keyword" style="font-weight: bold; color: blue;">import</span> android.app.Activity; </span> - <span style="color: black;"><span class="keyword" style="font-weight: bold; color: blue;">import</span> android.app.ActionBar; </span> - <span style="color: black;"><span class="keyword" style="font-weight: bold; color: blue;">import</span> android.app.Fragment; </span> - <span style="color: black;"><span class="keyword" style="font-weight: bold; color: blue;">import</span> android.os.Bundle; </span> - <span style="color: black;"><span class="keyword" style="font-weight: bold; color: blue;">import</span> android.os.Debug; </span> - <span style="color: black;"><span class="keyword" style="font-weight: bold; color: blue;">import</span> android.view.LayoutInflater; </span> - <span style="color: black;"><span class="keyword" style="font-weight: bold; color: blue;">import</span> android.view.Menu; </span> - <span style="color: black;"><span class="keyword" style="font-weight: bold; color: blue;">import</span> android.view.MenuItem; </span> - <span style="color: black;"><span class="keyword" style="font-weight: bold; color: blue;">import</span> android.view.View; </span> - <span style="color: black;"><span class="keyword" style="font-weight: bold; color: blue;">import</span> android.view.ViewGroup; </span> - <span style="color: black;"><span class="keyword" style="font-weight: bold; color: blue;">import</span> android.os.Build; </span> - <span style="color: black;"> </span> - <span style="color: black;"><span class="keyword" style="font-weight: bold; color: blue;">public</span> <span class="keyword" style="font-weight: bold; color: blue;">class</span> MainActivity <span class="keyword" style="font-weight: bold; color: blue;">extends</span> Activity { </span> - <span style="color: black;"> </span> - <span style="color: black;"> <span class="annotation" style="color: #646464;">@Override</span> </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: blue;">protected</span> <span class="keyword" style="font-weight: bold; color: blue;">void</span> onCreate(Bundle savedInstanceState) { </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: blue;">super</span>.onCreate(savedInstanceState); </span> - <span style="color: black;"> <span class="comment" style="color: #008200;">// 开始</span> </span> - <span style="color: black;"> Debug.startMethodTracing(<span class="string" style="color: red;">&#8220;mytrace&#8221;</span>); </span> - <span style="color: black;"> setContentView(R.layout.activity_main); </span> - <span style="color: black;"> action(); </span> - <span style="color: black;"> } </span> - <span style="color: black;"> <span class="annotation" style="color: #646464;">@Override</span> </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: blue;">protected</span> <span class="keyword" style="font-weight: bold; color: blue;">void</span> onDestroy() { </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: blue;">super</span>.onDestroy(); </span> - <span style="color: black;"> <span class="comment" style="color: #008200;">// 停止</span> </span> - <span style="color: black;"> Debug.stopMethodTracing(); </span> - <span style="color: black;"> } </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: blue;">private</span> <span class="keyword" style="font-weight: bold; color: blue;">void</span> action(){ </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: blue;">for</span> (<span class="keyword" style="font-weight: bold; color: blue;">int</span> i = <span class="number" style="color: #c00000;"></span>; i < <span class="number" style="color: #c00000;">100000</span>; i++) { </span> - <span style="color: black;"> System.out.println(<span class="string" style="color: red;">&#8220;模拟耗时&#8221;</span>); </span> - <span style="color: black;"> } </span> - <span style="color: black;"> } </span> - <span style="color: black;"> </span> - <span style="color: black;">} </span> </div> 需要添加权限: </div> <div style="color: #362e2b;"> <div class="dp-highlighter bg_html"> <div class="bar"> <div class="tools" style="color: silver;"> **[html]** [view plain](http://blog.csdn.net/cym492224103/article/details/39343907#)[copy](http://blog.csdn.net/cym492224103/article/details/39343907#)[print](http://blog.csdn.net/cym492224103/article/details/39343907#)[?](http://blog.csdn.net/cym492224103/article/details/39343907#) <div> </div> </div> </div> - <span style="color: black;"><span class="tag" style="font-weight: bold; color: blue;"><</span><span class="tag-name" style="font-weight: bold; color: blue;">uses-permission</span> <span class="attribute" style="color: red;">android:name</span>=<span class="attribute-value" style="color: blue;">&#8220;android.permission.WRITE_EXTERNAL_STORAGE&#8221;</span> <span class="tag" style="font-weight: bold; color: blue;">/></span> </span> </div> 执行完后,会在sd卡生成一个mytrace.trace文件 </div> <div style="color: #362e2b;"> ![](http://img.blog.csdn.net/20140917173106006?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY3ltNDkyMjI0MTAz/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) </div> <div style="color: #362e2b;"> <span style="color: #4b4b4b;">我们把它导出,然后使用命令行执行分析该文件:</span><span style="color: #4b4b4b;">traceView 文件地址</span> </div> <div> ![](http://img.blog.csdn.net/20140917173247017?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY3ltNDkyMjI0MTAz/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) </div> <div> 显示下图: </div> <div> ![](http://img.blog.csdn.net/20140917175000815?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY3ltNDkyMjI0MTAz/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) </div> <div style="color: #4b4b4b;"> 从上图我们可以看到,MainActivity的action()是最耗时的方法,占用了95%的时候。如果在真实项目中,我们首先就是找到消耗性能的方法,然后对其优化, 还有很多属性大家可以看一下 </div> <div style="color: #4b4b4b;"> ![](http://img.blog.csdn.net/20140917175451646?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY3ltNDkyMjI0MTAz/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) </div> <div style="color: #4b4b4b;"> </div> <div style="color: #4b4b4b;"> 我们在看看,高版本不用代码如何使用traceView的做法 </div> <div style="color: #362e2b;"> 使用DDMS </div> <div style="color: #362e2b;"> 打开devices窗口,选择某个进程,点击右上角的start method profiling </div> <blockquote style="color: #362e2b;"> &nbsp; &nbsp; <div> 运行app一段时间后,再点击已变成stop method profiling的该按钮。eclipse会自动弹出debug的标签(可通过菜单File->save as保存数据)。界面同上 面。 </div> &nbsp; &nbsp; &nbsp; <div> 这种方式不需要修改代码,所以对于没有源码的程序同样可以进行排查。同时可以方便的进行全局性能排查 </div> &nbsp; </blockquote> monkey 介绍: Monkey是Android中的一个命令行工具,可以运行在模拟器里或实际设备中。它向系统发送伪随机的用户事件流(如按键输入、触摸屏输入、手势输入等),实现对正在开发的应用程序进行压力测试。Monkey测试是一种为了测试软件的稳定性、健壮性的快速有效的方法。 使用: <blockquote style="color: #362e2b;"> &nbsp; 基本语法如下: &nbsp; <span class="katex math inline">adb shell monkey [options] &nbsp; 如果不指定options,Monkey将以无反馈模式启动,并把事件任意发送到安装在目标环境中的全部包。下面是一个更为典型的命令行示例,它启动指定的应用程序,并向其发送500个伪随机事件: &nbsp;</span> adb shell monkey -p your.package.name -v 500 &nbsp; ## 命令选项参考 <wbr />

2014年9月20日 · 4 分钟 · 天边的星星

ImageView.ScaleType / android:scaleType值的意义区别

android:scaleType是控制图片如何resized/moved来匹对ImageView的size。 ImageView.ScaleType / android:scaleType值的意义区别: CENTER /center 按图片的原来size居中显示,当图片长/宽超过View的长/宽,则截取图片的居中部分显示 CENTER_CROP / centerCrop 按比例扩大图片的size居中显示,使得图片长(宽)等于或大于View的长(宽) CENTER_INSIDE / centerInside 将图片的内容完整居中显示,通过按比例缩小或原来的size使得图片长/宽等于或小于View的长/宽 FIT_CENTER / fitCenter 把图片按比例扩大/缩小到View的宽度,居中显示 FIT_END / fitEnd 把图片按比例扩大/缩小到View的宽度,显示在View的下部分位置 FIT_START / fitStart 把图片按比例扩大/缩小到View的宽度,显示在View的上部分位置 FIT_XY / fitXY 把图片不按比例扩大/缩小到View的大小显示 MATRIX / matrix 用矩阵来绘制,动态缩小放大图片来显示。</td> </tr> </tbody> </table>

2014年9月3日 · 1 分钟 · 天边的星星

AlarmManager类的应用(实现闹钟功能)

1、AlarmManager,顾名思义,就是“提醒”,是Android中常用的一种系统级别的提示服务,可以实现从指定时间开始,以一个固定的间隔时间执行某项操作,所以常常与广播(Broadcast)连用,实现闹钟等提示功能 2、AlarmManager的常用方法有三个: (1)set(int type,long startTime,PendingIntent pi); 该方法用于设置一次性闹钟,第一个参数表示闹钟类型,第二个参数表示闹钟执行时间,第三个参数表示闹钟响应动作。 (2)setRepeating(int type,long startTime,long intervalTime,PendingIntent pi); 该方法用于设置重复闹钟,第一个参数表示闹钟类型,第二个参数表示闹钟首次执行时间,第三个参数表示闹钟两次执行的间隔时间,第三个参数表示闹钟响应动作。 (3)setInexactRepeating(int type,long startTime,long intervalTime,PendingIntent pi); 该方法也用于设置重复闹钟,与第二个方法相似,不过其两个闹钟执行的间隔时间不是固定的而已。 3、三个方法各个参数详悉: (1)int type:闹钟的类型,常用的有5个值:AlarmManager.ELAPSED_REALTIME、AlarmManager.ELAPSED_REALTIME_WAKEUP、AlarmManager.RTC、AlarmManager.RTC_WAKEUP、AlarmManager.POWER_OFF_WAKEUP。 AlarmManager.ELAPSED_REALTIME表示闹钟在手机睡眠状态下不可用,该状态下闹钟使用相对时间(相对于系统启动开始),状态值为3; AlarmManager.ELAPSED_REALTIME_WAKEUP表示闹钟在睡眠状态下会唤醒系统并执行提示功能,该状态下闹钟也使用相对时间,状态值为2; AlarmManager.RTC表示闹钟在睡眠状态下不可用,该状态下闹钟使用绝对时间,即当前系统时间,状态值为1; AlarmManager.RTC_WAKEUP表示闹钟在睡眠状态下会唤醒系统并执行提示功能,该状态下闹钟使用绝对时间,状态值为0; AlarmManager.POWER_OFF_WAKEUP表示闹钟在手机关机状态下也能正常进行提示功能,所以是5个状态中用的最多的状态之一,该状态下闹钟也是用绝对时间,状态值为4;不过本状态好像受SDK版本影响,某些版本并不支持; (2)long startTime:闹钟的第一次执行时间,以毫秒为单位,可以自定义时间,不过一般使用当前时间。需要注意的是,本属性与第一个属性(type)密切相关,如果第一个参数对应的闹钟使用的是相对时间(ELAPSED_REALTIME和ELAPSED_REALTIME_WAKEUP),那么本属性就得使用相对时间(相对于系统启动时间来说),比如当前时间就表示为:SystemClock.elapsedRealtime();如果第一个参数对应的闹钟使用的是绝对时间(RTC、RTC_WAKEUP、POWER_OFF_WAKEUP),那么本属性就得使用绝对时间,比如当前时间就表示为:System.currentTimeMillis()。 (3)long intervalTime:对于后两个方法来说,存在本属性,表示两次闹钟执行的间隔时间,也是以毫秒为单位。 (4)PendingIntent pi:是闹钟的执行动作,比如发送一个广播、给出提示等等。PendingIntent是Intent的封装类。需要注意的是,如果是通过启动服务来实现闹钟提示的话,PendingIntent对象的获取就应该采用Pending.getService(Context c,int i,Intent intent,int j)方法;如果是通过广播来实现闹钟提示的话,PendingIntent对象的获取就应该采用PendingIntent.getBroadcast(Context c,int i,Intent intent,int j)方法;如果是采用Activity的方式来实现闹钟提示的话,PendingIntent对象的获取就应该采用PendingIntent.getActivity(Context c,int i,Intent intent,int j)方法。如果这三种方法错用了的话,虽然不会报错,但是看不到闹钟提示效果。 ...

2014年8月28日 · 2 分钟 · 天边的星星

android 选择图片或拍照时旋转了90度问题

由于前面的博文中忽略了点内容,所以在这里补上,下面内容就是解决拍照或者选择图片显示的时候图片旋转了90度或者其他度数问题,以便照片可以正面显示:具体如下: 首先直接看上面博文下的拍完照或者选完图后处理部分: <div id="" class="dp-highlighter" style="color: #000000;"> <div class="bar"> <div class="tools" style="font-weight: bold;"> Java代码 <a style="color: #108ac6;" title="收藏这段代码">![收藏代码](http://104zz.iteye.com/images/icon_star.png)</a> </div> </div> - <span style="color: black;"><span class="annotation" style="color: #646464;">@Override</span> </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #7f0055;">protected</span> <span class="keyword" style="font-weight: bold; color: #7f0055;">void</span> onActivityResult(<span class="keyword" style="font-weight: bold; color: #7f0055;">int</span> requestCode, <span class="keyword" style="font-weight: bold; color: #7f0055;">int</span> resultCode, Intent data) { </span> - <span style="color: black;"> </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #7f0055;">switch</span> (resultCode) { </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #7f0055;">case</span> <span class="number" style="color: #c00000;">1</span>: </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #7f0055;">if</span> (data != <span class="keyword" style="font-weight: bold; color: #7f0055;">null</span>) { </span> - <span style="color: black;"> <span class="comment" style="color: #008200;">// 取得返回的Uri,基本上选择照片的时候返回的是以Uri形式,但是在拍照中有得机子呢Uri是空的,所以要特别注意</span> </span> - <span style="color: black;"> Uri mImageCaptureUri = data.getData(); </span> - <span style="color: black;"> <span class="comment" style="color: #008200;">// 返回的Uri不为空时,那么图片信息数据都会在Uri中获得。如果为空,那么我们就进行下面的方式获取</span> </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #7f0055;">if</span> (mImageCaptureUri != <span class="keyword" style="font-weight: bold; color: #7f0055;">null</span>) { </span> - <span style="color: black;"> setImage(mImageCaptureUri);<span class="comment" style="color: #008200;">// 根据Uri处理并显示图片</span> </span> - <span style="color: black;"> } </span> - <span style="color: black;"> } </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #7f0055;">break</span>; </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #7f0055;">default</span>: </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #7f0055;">break</span>; </span> - <span style="color: black;"> </span> - <span style="color: black;"> } </span> - <span style="color: black;"> } </span> </div> 第二:处理90度问题并显示: <div id="" class="dp-highlighter" style="color: #000000;"> <div class="bar"> <div class="tools" style="font-weight: bold;"> Java代码 <a style="color: #108ac6;" title="收藏这段代码">![收藏代码](http://104zz.iteye.com/images/icon_star.png)</a> </div> </div> - <span style="color: black;"><span class="keyword" style="font-weight: bold; color: #7f0055;">private</span> <span class="keyword" style="font-weight: bold; color: #7f0055;">void</span> setImage(Uri mImageCaptureUri) { </span> - <span style="color: black;"> </span> - <span style="color: black;"> <span class="comment" style="color: #008200;">// 不管是拍照还是选择图片每张图片都有在数据中存储也存储有对应旋转角度orientation值</span> </span> - <span style="color: black;"> <span class="comment" style="color: #008200;">// 所以我们在取出图片是把角度值取出以便能正确的显示图片,没有旋转时的效果观看</span> </span> - <span style="color: black;"> </span> - <span style="color: black;"> ContentResolver cr = <span class="keyword" style="font-weight: bold; color: #7f0055;">this</span>.getContentResolver(); </span> - <span style="color: black;"> Cursor cursor = cr.query(mImageCaptureUri, <span class="keyword" style="font-weight: bold; color: #7f0055;">null</span>, <span class="keyword" style="font-weight: bold; color: #7f0055;">null</span>, <span class="keyword" style="font-weight: bold; color: #7f0055;">null</span>, <span class="keyword" style="font-weight: bold; color: #7f0055;">null</span>);<span class="comment" style="color: #008200;">// 根据Uri从数据库中找</span> </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #7f0055;">if</span> (cursor != <span class="keyword" style="font-weight: bold; color: #7f0055;">null</span>) { </span> - <span style="color: black;"> cursor.moveToFirst();<span class="comment" style="color: #008200;">// 把游标移动到首位,因为这里的Uri是包含ID的所以是唯一的不需要循环找指向第一个就是了</span> </span> - <span style="color: black;"> String filePath = cursor.getString(cursor.getColumnIndex(<span class="string" style="color: blue;">&#8220;_data&#8221;</span>));<span class="comment" style="color: #008200;">// 获取图片路</span> </span> - <span style="color: black;"> String orientation = cursor.getString(cursor </span> - <span style="color: black;"> .getColumnIndex(<span class="string" style="color: blue;">&#8220;orientation&#8221;</span>));<span class="comment" style="color: #008200;">// 获取旋转的角度</span> </span> - <span style="color: black;"> cursor.close(); </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #7f0055;">if</span> (filePath != <span class="keyword" style="font-weight: bold; color: #7f0055;">null</span>) { </span> - <span style="color: black;"> Bitmap bitmap = BitmapFactory.decodeFile(filePath);<span class="comment" style="color: #008200;">//根据Path读取资源图片</span> </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #7f0055;">int</span> angle = <span class="number" style="color: #c00000;"></span>; </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #7f0055;">if</span> (orientation != <span class="keyword" style="font-weight: bold; color: #7f0055;">null</span> && !<span class="string" style="color: blue;">&#8220;&#8221;</span>.equals(orientation)) { </span> - <span style="color: black;"> angle = Integer.parseInt(orientation); </span> - <span style="color: black;"> } </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #7f0055;">if</span> (angle != <span class="number" style="color: #c00000;"></span>) { </span> - <span style="color: black;"> <span class="comment" style="color: #008200;">// 下面的方法主要作用是把图片转一个角度,也可以放大缩小等</span> </span> - <span style="color: black;"> Matrix m = <span class="keyword" style="font-weight: bold; color: #7f0055;">new</span> Matrix(); </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #7f0055;">int</span> width = bitmap.getWidth(); </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #7f0055;">int</span> height = bitmap.getHeight(); </span> - <span style="color: black;"> m.setRotate(angle); <span class="comment" style="color: #008200;">// 旋转angle度</span> </span> - <span style="color: black;"> bitmap = Bitmap.createBitmap(bitmap, <span class="number" style="color: #c00000;"></span>, <span class="number" style="color: #c00000;"></span>, width, height, </span> - <span style="color: black;"> m, <span class="keyword" style="font-weight: bold; color: #7f0055;">true</span>);<span class="comment" style="color: #008200;">// 从新生成图片</span> </span> - <span style="color: black;"> </span> - <span style="color: black;"> } </span> - <span style="color: black;"> photo.setImageBitmap(bitmap); </span> - <span style="color: black;"> } </span> - <span style="color: black;"> } </span> - <span style="color: black;">} </span> </div> <span style="color: #000000;"> OK完成</span>

2014年8月28日 · 4 分钟 · 天边的星星