Android开源项目第四篇——开发及测试工具篇

本文为那些不错的Android开源项目第四篇——开发工具篇,主要介绍Android开发工具和测试工具相关的开源项目。 最新内容请访问[AndroidOpenProject@Github](https://github.com/Trinea/android-open-project),欢迎Star和Fork。 对你有帮助的话,去知乎点个赞让更多人了解:Android 优秀开源项目及特效推荐。 Android开源项目系列汇总已完成,包括: [Android开源项目第一篇——个性化控件(View)篇](http://www.trinea.cn/android/android-open-source-projects-view/) Android开源项目第二篇——工具库篇 Android开源项目第三篇——优秀项目篇 Android开源项目第四篇——开发及测试工具篇 Android开源项目第五篇——优秀个人和团体篇 **1、Buck** facebook开源的Android编译工具,效率是ant的两倍。主要优点在于: (1) 加快编译速度,通过并行利用多核cpu和跟踪不变资源减少增量编译时间实现 (2) 可以在编译系统中生成编译规则而无须另外的系统生成编译规则文件 (3) 编译同时可生成单元测试结果 (4) 既可用于IDE编译也可用于持续集成编译 (5) facebook持续优化中 项目地址:https://github.com/facebook/buck **2、Android Maven Plugin** Android Maven插件,可用于对android三方依赖进行管理。在J2EE开发中,maven是非常成熟的依赖库管理工具,可统一管理依赖库。 项目地址:https://github.com/jayway/maven-android-plugin **3、Spoon** 可用于android不同机型设备自动化测试,能将应用apk和测试apk运行在不同机器上并生成相应测试报告。 项目地址:https://github.com/square/spoon **4、Android FEST** 提供一些列方便的断言,可用于提高编写Android自测代码效率 项目地址:https://github.com/square/fest-android **5、SelectorChapek for Android** Android Studio插件,可根据固定文件名格式资源自动生成drawable selectors xml文件。 项目地址:https://github.com/inmite/android-selector-chapek **6、Android Resource Navigator** chrome插件,可以方便的查看github上android源码工程的styles.xml和themes.xml。主要功能: (1) 快速打开android styles.xml themes.xml (2) 方便在资源间跳转。styles.xml themes.xml文件中资源链接跳转,可以方便跳转到某个资源 (3) 方便查找某个style和theme。chrome地址栏输入arn+tab+搜索内容回车即可 (4) 自动下载不同分辨率下的drawable (5) 通过映射查找那些不是按照固定命名规则命名的style和theme 项目地址:https://github.com/jgilfelt/android-resource-navigator 示例:https://chrome.google.com/webstore/detail/android-resource-navigato/agoomkionjjbejegcejiefodgbckeebo?hl=en&gl=GB ...

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

Android开源项目第三篇——优秀项目篇

本文为那些不错的Android开源项目第三篇——优秀项目篇,主要介绍那些还不错的完整Android项目。 最新内容请访问[AndroidOpenProject@Github](https://github.com/Trinea/android-open-project),欢迎Star和Fork。 对你有帮助的话,去知乎点个赞让更多人了解:Android 优秀开源项目及特效推荐。 Android开源项目系列汇总已完成,包括: [Android开源项目第一篇——个性化控件(View)篇](http://www.trinea.cn/android/android-open-source-projects-view/) Android开源项目第二篇——工具库篇 Android开源项目第三篇——优秀项目篇 Android开源项目第四篇——开发及测试工具篇 Android开源项目第五篇——优秀个人和团体篇 记录的项目主要依据是项目有意思或项目分层规范比较好。 **Linux** 项目地址:https://github.com/torvalds/linux **Android** 项目地址:https://android.googlesource.com/或https://github.com/android 以上两个项目,不解释 **(1) ZXing 二维码扫描工具** 项目地址:https://github.com/zxing/zxing或https://code.google.com/p/zxing/ APK地址:https://play.google.com/store/apps/details?id=com.google.zxing.client.android PS:现在市面上很多应用的二维码扫描功能都是从这个修改而来 **(2) photup 编辑机批量上传照片到facebook上** 项目地址:https://github.com/chrisbanes/photup APK地址:https://play.google.com/store/apps/details?id=uk.co.senab.photup PS:代码分包合理,很棒。不过这个项目依赖的开源项目比较多,比较难编译 ![android photo up image process](http://farm4.staticflickr.com/3694/11100968474_75ab4fbd56_o.jpg) **(3) Github的Android客户端项目** 项目地址:https://github.com/github/android APK地址:https://play.google.com/store/apps/details?id=com.github.mobile **(4) MIUI便签** 项目地址:https://github.com/MiCode/Notes APK地址:https://github.com/Trinea/TrineaDownload/blob/master/miui-note-demo.apk?raw=true PS:项目分包比较合理,相比较miui的文件管理器https://github.com/MiCode/FileExplorer代码规范较好得多 **(5) 四次元-新浪微博客户端** 项目地址:https://github.com/qii/weiciyuan APK地址:https://play.google.com/store/apps/details?id=org.qii.weiciyuan **(6) gnucash-一个记账理财软件** 项目地址:https://github.com/codinguser/gnucash-android APK地址:http://play.google.com/store/apps/details?id=org.gnucash.android **(7) AntennaPod支持rss订阅、音乐订阅** 项目地址:https://github.com/danieloeh/AntennaPod APK地址:https://play.google.com/store/apps/details?id=de.danoeh.antennapod ...

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

Android开源项目第二篇——工具库篇

本文为那些不错的Android开源项目第二篇——开发工具库篇,主要介绍常用的开发库,包括依赖注入框架、图片缓存、网络相关、数据库ORM建模、Android公共库、Android 高版本向低版本兼容、多媒体相关及其他。 最新内容请访问[AndroidOpenProject@Github](https://github.com/Trinea/android-open-project),欢迎Star和Fork。 对你有帮助的话,去知乎点个赞让更多人了解:[Android 优秀开源项目及特效推荐](http://www.zhihu.com/question/19804692/answer/21890050)。 Android开源项目系列汇总已完成,包括: [Android开源项目第一篇——个性化控件(View)篇](http://www.trinea.cn/android/android-open-source-projects-view/) [Android开源项目第二篇——工具库篇](http://www.trinea.cn/android/android-open-source-projects-dev-lib/) [Android开源项目第三篇——优秀项目篇](http://www.trinea.cn/android/android-open-source-projects-excellent-project/) [Android开源项目第四篇——开发及测试工具篇](http://www.trinea.cn/android/android-open-source-projects-dev-tool/) [Android开源项目第五篇——优秀个人和团体篇](http://www.trinea.cn/android/android-open-source-projects-excellent-personal-group/) 通过这些项目你可以大幅度减少不必要的开发而将精力放在更重要的地方。 **一、依赖注入DI** 通过依赖注入减少View、服务、资源简化初始化,事件绑定等重复繁琐工作 **1. AndroidAnnotations(Code Diet) android快速开发框架** 项目地址:[https://github.com/excilys/androidannotations](https://github.com/excilys/androidannotations) 文档介绍:[https://github.com/excilys/androidannotations/wiki](https://github.com/excilys/androidannotations/wiki) 官方网站:[http://androidannotations.org/](http://androidannotations.org/) 特点:(1)依赖注入:包括view,extras,系统服务,资源等等 (2)简单的线程模型,通过annotation表示方法运行在ui线程还是后台线程 (3)事件绑定:通过annotation表示view的响应事件,不用在写内部类 (4)REST客户端:定义客户端接口,自动生成REST请求的实现 (5)没有你想象的复杂:AndroidAnnotations只是在在编译时生成相应子类 (6)不影响应用性能:仅50kb,在编译时完成,不会对运行时有性能影响。 PS:与roboguice的比较:roboguice通过运行时读取annotations进行反射,所以可能影响应用性能,而AndroidAnnotations在编译时生成子类,所以对性能没有影响 **2. roboguice 帮你处理了很多代码异常,利用annotation使得更少的代码完成项目** 项目地址:[https://github.com/roboguice/roboguice](https://github.com/roboguice/roboguice) 文档介绍:[https://github.com/roboguice/roboguice/wiki](https://github.com/roboguice/roboguice/wiki) **3. butterknife 利用annotation帮你快速完成View的初始化,减少代码** 项目地址:[https://github.com/JakeWharton/butterknife](https://github.com/JakeWharton/butterknife) 文档介绍:[http://jakewharton.github.io/butterknife/](http://jakewharton.github.io/butterknife/) **4. Dagger 依赖注入,适用于Android和Java** 项目地址:[https://github.com/square/dagger](https://github.com/square/dagger) 文档介绍:[http://square.github.io/dagger/](http://square.github.io/dagger/) **二、图片缓存** **1. Android-Universal-Image-Loader 图片缓存** 目前使用最广泛的图片缓存,支持主流图片缓存的绝大多数特性。 项目地址:[https://github.com/nostra13/Android-Universal-Image-Loader](https://github.com/nostra13/Android-Universal-Image-Loader) Demo地址:[https://github.com/Trinea/TrineaDownload/blob/master/universal-imageloader-demo.apk?raw=true](https://github.com/Trinea/TrineaDownload/blob/master/universal-imageloader-demo.apk?raw=true) 文档介绍:[http://www.intexsoft.com/blog/item/74-universal-image-loader-part-3.html](http://www.intexsoft.com/blog/item/74-universal-image-loader-part-3.html) **2. picasso square开源的图片缓存** 项目地址:[https://github.com/square/picasso](https://github.com/square/picasso) 文档介绍:[http://square.github.io/picasso/](http://square.github.io/picasso/) 特点:(1)可以自动检测adapter的重用并取消之前的下载 (2)图片变换 (3)可以加载本地资源 (4)可以设置占位资源 (5)支持debug模式 **3. ImageCache 图片缓存,包含内存和Sdcard缓存** 项目地址:[https://github.com/Trinea/AndroidCommon](https://github.com/Trinea/AndroidCommon) Demo地址:[https://play.google.com/store/apps/details?id=cn.trinea.android.demo](https://play.google.com/store/apps/details?id=cn.trinea.android.demo) 文档介绍:[http://www.trinea.cn/?p=704](http://www.trinea.cn/?p=704) 特点:(1)支持预取新图片,支持等待队列 (2)包含二级缓存,可自定义文件名保存规则 (3)可选择多种缓存算法(FIFO、LIFO、LRU、MRU、LFU、MFU等13种)或自定义缓存算法 (4)可方便的保存及初始化恢复数据 (5)支持不同类型网络处理 (6)可根据系统配置初始化缓存等 **三、网络相关** **1. Asynchronous Http Client for Android Android异步Http请求** 项目地址:[https://github.com/loopj/android-async-http](https://github.com/loopj/android-async-http) 文档介绍:[http://loopj.com/android-async-http/](http://loopj.com/android-async-http/) 特点:(1) 在匿名回调中处理请求结果 (2) 在UI线程外进行http请求 (3) 文件断点上传 (4) 智能重试 (5) 默认gzip压缩 (6) 支持解析成Json格式 (7) 可将Cookies持久化到SharedPreferences **2. android-query 异步加载,更少代码完成Android加载** 项目地址:[https://github.com/androidquery/androidquery](https://github.com/androidquery/androidquery)或[https://code.google.com/p/android-query/](https://code.google.com/p/android-query/) 文档介绍:[https://code.google.com/p/android-query/#Why_AQuery](https://code.google.com/p/android-query/#Why_AQuery)? Demo地址:[https://play.google.com/store/apps/details?id=com.androidquery](https://play.google.com/store/apps/details?id=com.androidquery) 特点:[https://code.google.com/p/android-query/#Why_AQuery](https://code.google.com/p/android-query/#Why_AQuery)? **3. Async Http Client Java异步Http请求** 项目地址:[https://github.com/AsyncHttpClient/async-http-client](https://github.com/AsyncHttpClient/async-http-client) 文档介绍:[http://sonatype.github.io/async-http-client/](http://sonatype.github.io/async-http-client/) **4. Ion 支持图片、json、http post等异步请求** 项目地址:[https://github.com/koush/ion](https://github.com/koush/ion) 文档介绍:[https://github.com/koush/ion#more-examples](https://github.com/koush/ion#more-examples) **5. HttpCache Http缓存** 项目地址:[https://github.com/Trinea/AndroidCommon](https://github.com/Trinea/AndroidCommon) Demo地址:[https://play.google.com/store/apps/details?id=cn.trinea.android.demo](https://play.google.com/store/apps/details?id=cn.trinea.android.demo) Demo代码:[https://github.com/Trinea/AndroidDemo/blob/master/src/cn/trinea/android/demo/HttpCacheDemo.java](https://github.com/Trinea/AndroidDemo/blob/master/src/cn/trinea/android/demo/HttpCacheDemo.java) 特点是:(1) 根据cache-control、expires缓存http请求 (2) 支持同步、异步Http请求 (3) 在匿名回调中处理请求结果 (4) 在UI线程外进行http请求 (5) 默认gzip压缩 **6. Http Request** 项目地址:[https://github.com/kevinsawicki/http-request](https://github.com/kevinsawicki/http-request) 文档介绍:[https://github.com/kevinsawicki/http-request#examples](https://github.com/kevinsawicki/http-request#examples) **7. okhttp square开源的http工具类** 项目地址:[https://github.com/square/okhttp](https://github.com/square/okhttp) 文档介绍:[http://square.github.io/okhttp/](http://square.github.io/okhttp/) 特点:(1) 支持SPDY([http://zh.wikipedia.org/wiki/SPDY)协议。SPDY协议是Google开发的基于传输控制协议的应用层协议,通过压缩,多路复用(一个TCP链接传送网页和图片等资源](http://zh.wikipedia.org/wiki/SPDY)%E5%8D%8F%E8%AE%AE%E3%80%82SPDY%E5%8D%8F%E8%AE%AE%E6%98%AFGoogle%E5%BC%80%E5%8F%91%E7%9A%84%E5%9F%BA%E4%BA%8E%E4%BC%A0%E8%BE%93%E6%8E%A7%E5%88%B6%E5%8D%8F%E8%AE%AE%E7%9A%84%E5%BA%94%E7%94%A8%E5%B1%82%E5%8D%8F%E8%AE%AE%EF%BC%8C%E9%80%9A%E8%BF%87%E5%8E%8B%E7%BC%A9%EF%BC%8C%E5%A4%9A%E8%B7%AF%E5%A4%8D%E7%94%A8(%E4%B8%80%E4%B8%AATCP%E9%93%BE%E6%8E%A5%E4%BC%A0%E9%80%81%E7%BD%91%E9%A1%B5%E5%92%8C%E5%9B%BE%E7%89%87%E7%AD%89%E8%B5%84%E6%BA%90))和优先级来缩短加载时间。 (2) 如果SPDY不可用,利用连接池减少请求延迟 (3) Gzip压缩 (4) Response缓存减少不必要的请求 **8. Retrofit RESTFUL API设计** 项目地址:[https://github.com/square/retrofit](https://github.com/square/retrofit) 文档介绍:[http://square.github.io/retrofit/](http://square.github.io/retrofit/) **四、数据库 orm工具包** orm的db工具类,简化建表、查询、更新、插入、事务、索引的操作 **1. greenDAO Android Sqlite orm的db工具类** 项目地址:[https://github.com/greenrobot/greenDAO](https://github.com/greenrobot/greenDAO) 文档介绍:[http://greendao-orm.com/documentation/](http://greendao-orm.com/documentation/) 官方网站:[http://greendao-orm.com/](http://greendao-orm.com/) 特点:(1)性能佳 (2) 简单易用的API (3) 内存小好小 (4) 库大小小 **2. ActiveAndroid Android Sqlite orm的db工具类** 项目地址:[https://github.com/pardom/ActiveAndroid](https://github.com/pardom/ActiveAndroid) 文档介绍:[https://github.com/pardom/ActiveAndroid/wiki/_pages](https://github.com/pardom/ActiveAndroid/wiki/_pages) **3. Sprinkles Android Sqlite orm的db工具类** 项目地址:[https://github.com/emilsjolander/sprinkles](https://github.com/emilsjolander/sprinkles) 文档介绍:[http://emilsjolander.github.io/blog/2013/12/18/android-with-sprinkles/](http://emilsjolander.github.io/blog/2013/12/18/android-with-sprinkles/) 特点:比较显著的特点就是配合[https://github.com/square/retrofit](https://github.com/square/retrofit)能保存从服务器获取的数据 **五、Android公共库** **1. Guava Google的基于java1.6的类库集合的扩展项目** 包括collections, caching, primitives support, concurrency libraries, common annotations, string processing, I/O等等. 这些高质量的API可以使你的JAVa代码更加优雅,更加简洁 项目地址:[https://code.google.com/p/guava-libraries/](https://code.google.com/p/guava-libraries/) 文档介绍:[https://code.google.com/p/guava-libraries/wiki/GuavaExplained](https://code.google.com/p/guava-libraries/wiki/GuavaExplained) **2. AndroidCommon Android公共库** 项目地址:[https://github.com/Trinea/AndroidCommon](https://github.com/Trinea/AndroidCommon) Demo地址:[https://play.google.com/store/apps/details?id=cn.trinea.android.demo](https://play.google.com/store/apps/details?id=cn.trinea.android.demo) 文档介绍:[http://www.trinea.cn/?p=778](http://www.trinea.cn/?p=778) 包括:(1)缓存(图片缓存、预取缓存、网络缓存) (2) 公共View(下拉及底部加载更多ListView、底部加载更多ScrollView、滑动一页Gallery) (3) Android常用工具类(网络、下载、Android资源操作、shell、文件、Json、随机数、Collection等等) **六、Android 高版本向低版本兼容** **1. ActionBarSherlock 为Android所有版本提供统一的ActionBar,解决4.0以下ActionBar的适配问题** 项目地址:[https://github.com/JakeWharton/ActionBarSherlock](https://github.com/JakeWharton/ActionBarSherlock) Demo地址:[https://play.google.com/store/apps/details?id=com.actionbarsherlock.sample.demos](https://play.google.com/store/apps/details?id=com.actionbarsherlock.sample.demos) APP示例:太多了。。现在连google都在用 **2. Nine Old Androids 将Android 3.0(Honeycomb)所有动画API(ObjectAnimator ValueAnimator等)兼容到Android1.0** 项目地址:[https://github.com/JakeWharton/NineOldAndroids](https://github.com/JakeWharton/NineOldAndroids) Demo地址:[https://play.google.com/store/apps/details?id=com.jakewharton.nineoldandroids.sample](https://play.google.com/store/apps/details?id=com.jakewharton.nineoldandroids.sample) 文档介绍:[http://nineoldandroids.com/](http://nineoldandroids.com/) **3. HoloEverywhere 将Android 3.0的Holo主题兼容到Android2.1++** 项目地址:[https://github.com/Prototik/HoloEverywhere](https://github.com/Prototik/HoloEverywhere) Demo地址:[https://raw.github.com/Prototik/HoloEverywhere/repo/org/holoeverywhere/demo/2.1.0/demo-2.1.0.apk](https://raw.github.com/Prototik/HoloEverywhere/repo/org/holoeverywhere/demo/2.1.0/demo-2.1.0.apk) 文档介绍:[http://android-developers.blogspot.com/2012/01/holo-everywhere.html](http://android-developers.blogspot.com/2012/01/holo-everywhere.html) **七、多媒体相关** **1. cocos2d-x 跨平台的2d游戏框架,支持Android、IOS、Linux、Windows等众多平台** 项目地址:[https://github.com/cocos2d/cocos2d-x](https://github.com/cocos2d/cocos2d-x) 文档介绍:[http://www.cocos2d-x.org/wiki](http://www.cocos2d-x.org/wiki) 官方网站:[http://www.cocos2d-x.org/](http://www.cocos2d-x.org/) **2. Vitamio 是一款Android与iOS平台上的全能多媒体开发框架** 项目地址:[https://github.com/yixia/VitamioBundle](https://github.com/yixia/VitamioBundle) 网站介绍:[http://www.vitamio.org/docs/](http://www.vitamio.org/docs/) 特点:(1) 全面支持硬件解码与GPU渲染 (2) 能够流畅播放720P甚至1080P高清MKV,FLV,MP4,MOV,TS,RMVB等常见格式的视频 (3) 在Android与iOS上跨平台支持 MMS, RTSP, RTMP, HLS(m3u8)等常见的多种视频流媒体协议,包括点播与直播。 **3. PhotoProcessing 利用ndk处理图片库**,支持Instafix、Ansel、Testino、XPro、Retro、BW、Sepia、Cyano、Georgia、Sahara、HDR、Rotate、Flip 项目地址:[https://github.com/lightbox/PhotoProcessing](https://github.com/lightbox/PhotoProcessing) Demo地址:[https://github.com/Trinea/TrineaDownload/blob/master/photo-processing.apk?raw=true](https://github.com/Trinea/TrineaDownload/blob/master/photo-processing.apk?raw=true) **4. Android StackBlur 图片模糊效果工具类** 项目地址:[https://github.com/kikoso/android-stackblur](https://github.com/kikoso/android-stackblur) Demo地址:[https://github.com/kikoso/android-stackblur/blob/master/StackBlurDemo/bin/StackBlurDemo.apk?raw=true](https://github.com/kikoso/android-stackblur/blob/master/StackBlurDemo/bin/StackBlurDemo.apk?raw=true) 文档介绍:[https://github.com/kikoso/android-stackblur#usage](https://github.com/kikoso/android-stackblur#usage) **八、其他** **1. Salvage view 带View缓存的Viewpager PagerAdapter,很方便使用** 项目地址:[https://github.com/JakeWharton/salvage](https://github.com/JakeWharton/salvage) **2. Android-PasscodeLock 应用锁**,每次启动或从任何Activity启动应用都需要输入四位数字的密码方可进入 项目地址:[https://github.com/wordpress-mobile/Android-PasscodeLock](https://github.com/wordpress-mobile/Android-PasscodeLock) Demo地址:[https://play.google.com/store/apps/details?id=com.sothree.umano](https://play.google.com/store/apps/details?id=com.sothree.umano) APP示例:Wordpress Android,支付宝,挖财 **3. android-lockpattern Android的图案密码解锁** 项目地址:[https://code.google.com/p/android-lockpattern/](https://code.google.com/p/android-lockpattern/) Demo地址:[https://play.google.com/store/apps/details?id=group.pals.android.lib.ui.lockpattern.demo](https://play.google.com/store/apps/details?id=group.pals.android.lib.ui.lockpattern.demo) 使用介绍:[https://code.google.com/p/android-lockpattern/wiki/QuickUse](https://code.google.com/p/android-lockpattern/wiki/QuickUse) 示例APP:Android开机的图案密码解锁,支付宝的密码解锁 **4. GlowPadBackport将Android4.2的锁屏界面解锁扩展到Android1.6及1.6+** 项目地址:[https://github.com/rock3r/GlowPadBackport](https://github.com/rock3r/GlowPadBackport) Demo地址:[https://play.google.com/store/apps/details?id=net.sebastianopoggi.samples.ui.GlowPadSample](https://play.google.com/store/apps/details?id=net.sebastianopoggi.samples.ui.GlowPadSample) 效果图:[https://lh6.ggpht.com/U070b6Lh6cVsVwx4jN-5nq0xqiB1PBzrYABPeJIEe2hZQ5UWOxc-FDUG77wADelToHA=h310-rw](https://lh6.ggpht.com/U070b6Lh6cVsVwx4jN-5nq0xqiB1PBzrYABPeJIEe2hZQ5UWOxc-FDUG77wADelToHA=h310-rw) **5. GlowPadView Android4锁屏界面解锁** 项目地址:[https://github.com/nadavfima/GlowPadView](https://github.com/nadavfima/GlowPadView) 效果图:[https://raw.github.com/nadavfima/GlowPadView/master/example.png](https://raw.github.com/nadavfima/GlowPadView/master/example.png) **6. Android Priority Job Queue Android后台任务队列** 项目地址:[https://github.com/path/android-priority-jobqueue](https://github.com/path/android-priority-jobqueue) 文档介绍:[https://github.com/path/android-priority-jobqueue#getting-started](https://github.com/path/android-priority-jobqueue#getting-started) **7. jsoup 一个解析html的java库,可方便的提取和操作数据** 项目地址:[https://github.com/jhy/jsoup](https://github.com/jhy/jsoup) 官方网站:[http://jsoup.org/](http://jsoup.org/) 作用:(1) 从一个url、文件或string获得html并解析 (2) 利用dom遍历或css选择器查找、提取数据 (3) 操作html元素 (4) 根据白名单去除用于提交的非法数据防止xss攻击 (5) 输出整齐的html **8.ZIP java压缩和解压库** 项目地址:[https://github.com/zeroturnaround/zt-zip](https://github.com/zeroturnaround/zt-zip) 文档介绍:[https://github.com/zeroturnaround/zt-zip#examples](https://github.com/zeroturnaround/zt-zip#examples) 作用:(1) 解压和压缩,并支持文件夹内递归操作 (2) 支持包含和排除某些元素 (3) 支持重命名元素 (4) 支持遍历zip包内容 (5) 比较两个zip包等功能 **9. Cobub Razor 开源的mobile行为分析系统**,包括web端、android端,支持ios和window phone 项目地址:[https://github.com/cobub/razor](https://github.com/cobub/razor) Demo地址:[http://demo.cobub.com/razor](http://demo.cobub.com/razor) 网站介绍:[http://dev.cobub.com/](http://dev.cobub.com/) **10. aFileChooser 文件选择器**,可内嵌到程序中,而无需使用系统或三方文件选择器。 项目地址:[https://github.com/iPaulPro/aFileChooser](https://github.com/iPaulPro/aFileChooser) **11. androidpn 基于xmpp协议的消息推送解决方案**,包括服务器端和android端。 项目地址:[https://github.com/dannytiehui/androidpn](https://github.com/dannytiehui/androidpn) **12. Android插件式开发** 项目地址:[https://github.com/umeng/apf](https://github.com/umeng/apf)

2014年12月14日 · 2 分钟 · 天边的星星

Android开源项目第一篇——个性化控件(View)篇

本文为那些不错的Android开源项目第一篇——个性化控件(View)篇,主要介绍Android上那些不错个性化的View,包括ListView、ActionBar、Menu、ViewPager、Gallery、GridView、ImageView、ProgressBar及其他如Dialog、Toast、EditText、TableView、Activity Animation等等。 最新内容请访问[AndroidOpenProject@Github](https://github.com/Trinea/android-open-project),欢迎Star和Fork。 对你有帮助的话,去知乎点个赞让更多人了解:[Android 优秀开源项目及特效推荐](http://www.zhihu.com/question/19804692/answer/21890050)。 Android开源项目系列汇总已完成,包括: [Android开源项目第一篇——个性化控件(View)篇](http://www.trinea.cn/android/android-open-source-projects-view/) Android开源项目第二篇——工具库篇 Android开源项目第三篇——优秀项目篇 Android开源项目第四篇——开发及测试工具篇 Android开源项目第五篇——优秀个人和团体篇 本文中你可以找到那些精美App中各种有特性的View,如Gmail的左滑出菜单、Google plus的卡片式ListView,Pinterest的瀑布流,微信的左滑删除,微博的个页面下拉刷新等等。长期更新,欢迎大家补充和推荐^_^ **一、ListView** **1. android-pulltorefresh 一个强大的拉动刷新开源项目,支持各种控件下拉刷新** ListView、ViewPager、WevView、ExpandableListView、GridView、(Horizontal )ScrollView、Fragment上下左右拉动刷新,比下面johannilsson那个只支持ListView的强大的多。并且他实现的下拉刷新ListView在item不足一屏情况下也不会显示刷新提示,体验更好。 项目地址:https://github.com/chrisbanes/Android-PullToRefresh Demo地址:https://github.com/Trinea/TrineaDownload/blob/master/pull-to-refreshview-demo.apk?raw=true APP示例:新浪微博各个页面 **2. android-pulltorefresh-listview 下拉刷新ListView** 项目地址:https://github.com/johannilsson/android-pulltorefresh Demo地址:https://github.com/Trinea/TrineaDownload/blob/master/pull-to-refresh-listview-demo.apk?raw=true PS:这个被很多人使用的项目实际有不少bug,推荐使用上面的android-pulltorefresh **3. DropDownListView 下拉刷新及滑动到底部加载更多ListView** 项目地址:https://github.com/Trinea/AndroidCommon Demo地址:https://github.com/Trinea/TrineaDownload/blob/master/TrineaAndroidDemo.apk?raw=true 文档介绍:http://www.trinea.cn/?p=523 **4. DragSortListView 拖动排序的ListView** 同时支持ListView滑动item删除,各个Item高度不一、单选、复选、CursorAdapter做为适配器、拖动背景变化等 项目地址:https://github.com/bauerca/drag-sort-listview Demo地址:https://play.google.com/store/apps/details?id=com.mobeta.android.demodslv APP示例:Wordpress Android **5. SwipeListView 支持定义ListView左右滑动事件,支持左右滑动位移,支持定义动画时间** 项目地址:https://github.com/47deg/android-swipelistview Demo地址:https://play.google.com/store/apps/details?id=com.fortysevendeg.android.swipelistview APP示例:微信 **6. Android-SwipeToDismiss 滑动Item消失ListView** 项目地址:https://github.com/romannurik/Android-SwipeToDismiss 支持3.0以下版本见:https://github.com/JakeWharton/SwipeToDismissNOA Demo地址:https://github.com/JakeWharton/SwipeToDismissNOA/SwipeToDismissNOA.apk/qr_code **7. StickyListHeaders GroupName滑动到顶端时会固定不动直到另外一个GroupName到达顶端的ExpandListView,支持快速滑动,支持Android2.3及以上** 项目地址:[https://github.com/emilsjolander/StickyListHeaders](https://github.com/emilsjolander/StickyListHeaders) APP示例:Android 4.0联系人 效果图:[https://raw.github.com/emilsjolander/StickyListHeaders/master/demo.gif](https://raw.github.com/emilsjolander/StickyListHeaders/master/demo.gif) **8. pinned-section-listview GroupName滑动到顶端时会固定不动直到另外一个GroupName到达顶端的ExpandListView** 项目地址:https://github.com/beworker/pinned-section-listview 效果图:https://raw.github.com/beworker/pinned-section-listview/master/screen1.png ...

2014年12月14日 · 3 分钟 · 天边的星星

Android WebView 与HttpClient 共用本地cookie问题

我是为了解决:WebView 缓存下来的cookie可以用于HttpClient,因为我的HttpClient单独需要取一些数据,但是依赖于本地的cookie。如果没有cookie返回来的是登录页面 [html] view plaincopy在CODE上查看代码片派生到我的代码片 核心代码: mainActivity.java public void onCreate(Bundle savedInstanceState) { View v = inflater.inflate(R.layout.main_fragment, container, false); mWebView = (WebView) v.findViewById(R.id.webview); MyWebViewClient webviewClient = new MyWebViewClient(); mWebView.setWebViewClient(webviewClient); WebSettings webset = mWebView.getSettings(); webset.setJavaScriptEnabled(true); mWebView.loadUrl(Constants.TALKGROUP_URL); return v; } private class MyWebViewClient extends WebViewClient { @Override public void onPageFinished(WebView view, String url) { super.onPageFinished(view, url); IWLog.d(TAG, “onPageFinished() url is:”+url); /* 将cookie保存起来*/ String c = CookieManager.getInstance().getCookie(url); DataCenter.setCookie(c); CookieSyncManager.getInstance().sync(); } @Override public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) { IWLog.d(TAG, “onReceivedError() errorCode:” + errorCode+”—-failingUrl”+failingUrl); super.onReceivedError(view, errorCode, description, failingUrl); } ...

2014年12月5日 · 2 分钟 · 天边的星星

Android(安卓)WebView设置cookie

最近两天一直想用安卓模拟登陆,利用新的WebView显示登陆后可以访问的页面,但是不管怎么访问需要登陆后才能访问的页面,还是跳回到登陆页,后来网上看了下是cookie没有设置,找了半天才到到合适的设置方法: 登陆方法: private Cookie cookie; public static HttpContext localContext; <span style="color: #ff0000;">**public static List cookies;//此为保存的cookie**</span> public String cookieStr; // public static Cookie cookie = null; /** * 登陆时保存cookie * @param url * @param data * @return */ public String login(String url,String data[]){ HttpClient client = null; String html = null; try { client = new DefaultHttpClient(); <span style="color: #ff0000;">**// 创建cookie store的本地实例 CookieStore cookieStore = new BasicCookieStore(); localContext = new BasicHttpContext(); // 绑定定制的cookie store到本地内容中 localContext.setAttribute(ClientContext.COOKIE_STORE, cookieStore); **</span> HttpPost post = new HttpPost(url); List parameters = new ArrayList(); parameters.add(new BasicNameValuePair("username", data[0])); parameters.add(new BasicNameValuePair("passwd", data[1])); parameters.add(new BasicNameValuePair("login", "%B5%C7%A1%A1%C2%BC")); UrlEncodedFormEntity entity = new UrlEncodedFormEntity(parameters,"utf-8"); post.setEntity(entity); HttpResponse response = client.execute(post<span style="color: #ff0000;">,localContext</span>); if(response.getStatusLine().getStatusCode() == 200){ InputStream content = response.getEntity().getContent(); BufferedReader buffer = new BufferedReader(new InputStreamReader(content,"gbk")); String line = null; while((line=buffer.readLine())!=null){ html+=line; } <span style="color: #ff0000;">** cookies = cookieStore.getCookies();**</span> System.out.println("cookies.size():"+cookies.size()); if (!cookies.isEmpty()) { for(int i=0;i&lt;cookies.size();i++){ system.out.println("-="" "+cookies.get(i).tostring());="" }="" buffer.close();="" }else{="" system.out.println("utilslogin:"+response.getstatusline().getstatuscode());="" catch="" (exception="" e)="" {="" e.printstacktrace();="" }finally{="" if(client!="null){" client.getconnectionmanager().shutdown();="" return="" html;="" }&lt;="" pre=""&gt; ...

2014年12月5日 · 2 分钟 · 天边的星星

Sqlite删除列方法

sqlite中是不支持删除列操作的,所以网上alter table table_name drop column col_name这个语句在sqlite中是无效的,而替代的方法可以如下: 1.根据原表创建一张新表 2.删除原表 3.将新表重名为旧表的名称 示例例子如下 1.创建一张旧表Student,包含id(主码),name, tel create table student ( id integer primary key, name text, tel text ) 2.给旧表插入两个值 insert into student(id,name,tel) values(101,”Jack”,”110″) insert into student(id,name,tel) values(102,”Rose”,”119″) 结果如图 3.接下来我们删除电话这个列,首先根据student表创建一张新表teacher create table teacher as select id,name from student 结果如图 可以看到tel这一列已经没有了 4.然后我们删除student这个表 drop table if exists student 5.将teacher这个表重命名为student alter table teacher rename to student 结果演示: select * from student order by name desc(desc降序, asc升序) ...

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

【安卓】从源码的角度深入分析Scroller

这篇文章我将从源码的角度深入分析Scroller类。在阅读的时候,建议大家打开源码对照着看,否则可能看的云里雾里。 **一.Scroller的用途** ** ** 熟悉android的同学必然对Scroller不陌生,Scroller是一个**弹性滑动对象**,可以制作很多酷炫的滑动效果,Lancher中的滑屏效果就有使用到Scroller。 我们知道,View类中的scrollTo和scrollBy方法提供了滑动操作,但是这种滑动操作是瞬间完成的,就是说你为scrollTo提供终点坐标,该方法只要一调用,我们就会发现已经滚动到目的地了,这种方式很显然用户体验是不好的,因而android工程师为我们封装了Scroller类,这个类可以为View带来**缓慢移动的效果**。 具体使用方式通常是通过在你自定义的View中调用Scroller的startScroll并刷新视图,另外需重写computeScroll方法(刷新视图过程中会调用此方法),在该方法中调用Scroller的computeScrollOffset计算应该滚动到的位置,然后使用scrollTo滚动到该位置,再调用invalidate刷新,就可以实现滚动效果了(不了解的同学建议先去熟悉Scroller用法)。 Scroller使用示例(代码片段): **[java]** [view plain](http://blog.csdn.net/chdjj/article/details/41678897#)[copy](http://blog.csdn.net/chdjj/article/details/41678897#)[![在CODE上查看代码片](https://code.csdn.net/assets/CODE_ico.png)](https://code.csdn.net/snippets/539577)[![派生到我的代码片](https://code.csdn.net/assets/ico_fork.svg)](https://code.csdn.net/snippets/539577/fork) <div> </div> </div> </div> - <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> MyView <span class="keyword" style="font-weight: bold; color: #006699;">extends</span> LinearLayout </span> - <span style="color: black;">{ </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">private</span> Scroller mScroller; </span> - <span style="color: black;"> &#8230; &#8230; </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> smoothScrollTo(<span class="keyword" style="font-weight: bold; color: #006699;">int</span> destX,<span class="keyword" style="font-weight: bold; color: #006699;">int</span> destY) </span> - <span style="color: black;"> { </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">int</span> scrollX = getScrollX(); </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">int</span> scrollY = getScrollY(); </span> - <span style="color: black;"> </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">int</span> deltaX = destX-scrollX; </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">int</span> deltaY = destY-scrollY; </span> - <span style="color: black;"> mScroller.startScroll(scrollX,scrollY,deltaX, deltaY, <span class="number" style="color: #c00000;">1000</span>); </span> - <span style="color: black;"> invalidate(); </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> computeScroll() </span> - <span style="color: black;"> { </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">if</span>(mScroller != <span class="keyword" style="font-weight: bold; color: #006699;">null</span>) </span> - <span style="color: black;"> { </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">if</span>(mScroller.computeScrollOffset()) </span> - <span style="color: black;"> { </span> - <span style="color: black;"> scrollTo(mScroller.getCurrX(),mScroller.getCurrY()); </span> - <span style="color: black;"> Log.d(TAG,<span class="string" style="color: blue;">&#8220;scrollX=&#8221;</span>+getScrollX()+<span class="string" style="color: blue;">&#8220;,scrollY=&#8221;</span>+getScrollY()); </span> - <span style="color: black;"> postInvalidate(); </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> **二.ScrollTo、ScrollBy、getScrollX、getScrollY方法分析** ** ** 在分析Scroller源码之前,我们必须先了解ScrollTo,ScrollBy,getScrollX,getScrollY这几个方法的作用。这四个方法都是View类提供的,这点先明确。 我们先分析getScrollX和getScrollY方法,查看源码实现: **[java]** [view plain](http://blog.csdn.net/chdjj/article/details/41678897#)[copy](http://blog.csdn.net/chdjj/article/details/41678897#)[![在CODE上查看代码片](https://code.csdn.net/assets/CODE_ico.png)](https://code.csdn.net/snippets/539577)[![派生到我的代码片](https://code.csdn.net/assets/ico_fork.svg)](https://code.csdn.net/snippets/539577/fork) <div> </div> </div> </div> - <span style="color: black;"><span class="comment" style="color: #008200;">/**</span> </span> - <span style="color: black;"><span class="comment" style="color: #008200;"> * Return the scrolled left position of this view. This is the left edge of</span> </span> - <span style="color: black;"><span class="comment" style="color: #008200;"> * the displayed part of your view. You do not need to draw any pixels</span> </span> - <span style="color: black;"><span class="comment" style="color: #008200;"> * farther left, since those are outside of the frame of your view on</span> </span> - <span style="color: black;"><span class="comment" style="color: #008200;"> * screen.</span> </span> - <span style="color: black;"><span class="comment" style="color: #008200;"> *</span> </span> - <span style="color: black;"><span class="comment" style="color: #008200;"> * @return The left edge of the displayed part of your view, in pixels.</span> </span> - <span style="color: black;"><span class="comment" style="color: #008200;"> */</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;">final</span> <span class="keyword" style="font-weight: bold; color: #006699;">int</span> getScrollX() { </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">return</span> mScrollX; </span> - <span style="color: black;"> } </span> - <span style="color: black;"> <span class="comment" style="color: #008200;">/**</span> </span> - <span style="color: black;"><span class="comment" style="color: #008200;"> * Return the scrolled top position of this view. This is the top edge of</span> </span> - <span style="color: black;"><span class="comment" style="color: #008200;"> * the displayed part of your view. You do not need to draw any pixels above</span> </span> - <span style="color: black;"><span class="comment" style="color: #008200;"> * it, since those are outside of the frame of your view on screen.</span> </span> - <span style="color: black;"><span class="comment" style="color: #008200;"> *</span> </span> - <span style="color: black;"><span class="comment" style="color: #008200;"> * @return The top edge of the displayed part of your view, in pixels.</span> </span> - <span style="color: black;"><span class="comment" style="color: #008200;"> */</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;">final</span> <span class="keyword" style="font-weight: bold; color: #006699;">int</span> getScrollY() { </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">return</span> mScrollY; </span> - <span style="color: black;"> } </span> </div> <div> <div> getScrollX和getScrollY方法返回的是mScrollX和mScrollY变量。这两个变量是什么呢? </div> <div> </div> <div> 这里我直接告诉大家,**mScrollX和mScrollY指的是视图内容相对于视图原始起始坐标的偏移量**,mScrollX和mScrollY的默认值为0,因为默认是没有偏移的。另外需注意偏移量的正负问题,因为是相对视图起始坐标的,所以如果你是**向右偏移那么mScrollX应该是负数,而向左偏移mScrollX为正数**。再举个例子,比如你定义了一个ImageView,其左上角的坐标为(100,80),此时mScrollX和mScrollY值都为0(没有偏移),现在你要把该ImageView移到(120,100)处,也就是右下方,那么你的mScrollX应该是100-120=-20,mScrollY应该是80-100=-20,这下你该明白了吧。 </div> </div> <div> 明白了这个问题,scrollBy和scrollTo也就不在话下了,我们查看源码: </div> <div> </div> <div class="dp-highlighter bg_java"> <div class="bar"> <div class="tools" style="color: silver;"> **[java]** [view plain](http://blog.csdn.net/chdjj/article/details/41678897#)[copy](http://blog.csdn.net/chdjj/article/details/41678897#)[![在CODE上查看代码片](https://code.csdn.net/assets/CODE_ico.png)](https://code.csdn.net/snippets/539577)[![派生到我的代码片](https://code.csdn.net/assets/ico_fork.svg)](https://code.csdn.net/snippets/539577/fork) <div> </div> </div> </div> - <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> scrollTo(<span class="keyword" style="font-weight: bold; color: #006699;">int</span> x, <span class="keyword" style="font-weight: bold; color: #006699;">int</span> y) { </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">if</span> (mScrollX != x || mScrollY != y) { </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">int</span> oldX = mScrollX; </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">int</span> oldY = mScrollY; </span> - <span style="color: black;"> mScrollX = x; </span> - <span style="color: black;"> mScrollY = y; </span> - <span style="color: black;"> invalidateParentCaches(); </span> - <span style="color: black;"> onScrollChanged(mScrollX, mScrollY, oldX, oldY); </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">if</span> (!awakenScrollBars()) { </span> - <span style="color: black;"> postInvalidateOnAnimation(); </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;">public</span> <span class="keyword" style="font-weight: bold; color: #006699;">void</span> scrollBy(<span class="keyword" style="font-weight: bold; color: #006699;">int</span> x, <span class="keyword" style="font-weight: bold; color: #006699;">int</span> y) { </span> - <span style="color: black;"> scrollTo(mScrollX + x, mScrollY + y); </span> - <span style="color: black;"> } </span> </div> <div> 先看scrollTo方法,它首先判断x,y方向的偏移量(即mScrollX,mScrollY)是否和传进来的偏移量(即x,y)相同,如果相同那么直接返回,否则我们将更新mScrollX和mScrollY,并且通知界面发生改变请求重绘。通过这种方式就可以实现滚动效果了,只是是瞬间移动的。再看这个scrollBy,直接调用的是scrollTo,根据参数很容易发现,这个方法的作用是在当前偏移基础上,再继续偏移(x,y)单位。需要注意的是这两个方法的**参数是偏移量而不是实际位置**哦! </div> <div> </div> <div> 这里还有一点需要注意,那就是你**调用一个View的scrollTo方法进行滚动时,滚动的并不是该View本身,而是该View的内容。**比如你要对一个Button进行滚动的话,应该在Button外面包一个ViewGroup,然后调用ViewGroup的scrollTo方法。这一点也很重要,大家需要注意下! </div> <div> </div> <div> **三.Scroller源码分析** </div> <div> ** ** </div> <div> Scroller的精华在于computeScrollOffset和startScroll方法,所以我们重点分析这两个方法。 </div> <div> 分析之前,先看这个类中定义的一些变量: </div> <div class="dp-highlighter bg_java"> <div class="bar"> <div class="tools" style="color: silver;"> **[java]** [view plain](http://blog.csdn.net/chdjj/article/details/41678897#)[copy](http://blog.csdn.net/chdjj/article/details/41678897#)[![在CODE上查看代码片](https://code.csdn.net/assets/CODE_ico.png)](https://code.csdn.net/snippets/539577)[![派生到我的代码片](https://code.csdn.net/assets/ico_fork.svg)](https://code.csdn.net/snippets/539577/fork) <div> </div> </div> </div> - <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> mMode;<span class="comment" style="color: #008200;">//模式,有SCROLL_MODE和FLING_MODE</span> </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> mStartX;<span class="comment" style="color: #008200;">//起始x方向偏移</span> </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> mStartY;<span class="comment" style="color: #008200;">//起始y方向偏移</span> </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> mFinalX;<span class="comment" style="color: #008200;">//终点x方向偏移</span> </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> mFinalY;<span class="comment" style="color: #008200;">//终点y方向偏移</span> </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> mCurrX;<span class="comment" style="color: #008200;">//当前x方向偏移</span> </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> mCurrY;<span class="comment" style="color: #008200;">//当前y方向偏移</span> </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;">long</span> mStartTime;<span class="comment" style="color: #008200;">//起始时间</span> </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> mDuration;<span class="comment" style="color: #008200;">//滚动持续时间</span> </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;">float</span> mDurationReciprocal;<span class="comment" style="color: #008200;">//持续时间的倒数</span> </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;">float</span> mDeltaX;<span class="comment" style="color: #008200;">//x方向应该滚动的距离,mDeltaX=mFinalX-mStartX</span> </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;">float</span> mDeltaY;<span class="comment" style="color: #008200;">//y方向应该滚动的距离,mDeltaY=mFinalY-mStartY</span> </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;">boolean</span> mFinished;<span class="comment" style="color: #008200;">//是否结束</span> </span> </div> 对这些变量有个大致印象之后,我们就开始看startScroll源码了: **[java]** [view plain](http://blog.csdn.net/chdjj/article/details/41678897#)[copy](http://blog.csdn.net/chdjj/article/details/41678897#)[![在CODE上查看代码片](https://code.csdn.net/assets/CODE_ico.png)](https://code.csdn.net/snippets/539577)[![派生到我的代码片](https://code.csdn.net/assets/ico_fork.svg)](https://code.csdn.net/snippets/539577/fork) <div> </div> </div> </div> - <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> startScroll(<span class="keyword" style="font-weight: bold; color: #006699;">int</span> startX, <span class="keyword" style="font-weight: bold; color: #006699;">int</span> startY, <span class="keyword" style="font-weight: bold; color: #006699;">int</span> dx, <span class="keyword" style="font-weight: bold; color: #006699;">int</span> dy) { </span> - <span style="color: black;"> startScroll(startX, startY, dx, dy, DEFAULT_DURATION); </span> - <span style="color: black;"> } </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;">void</span> startScroll(<span class="keyword" style="font-weight: bold; color: #006699;">int</span> startX, <span class="keyword" style="font-weight: bold; color: #006699;">int</span> startY, <span class="keyword" style="font-weight: bold; color: #006699;">int</span> dx, <span class="keyword" style="font-weight: bold; color: #006699;">int</span> dy, <span class="keyword" style="font-weight: bold; color: #006699;">int</span> duration) { </span> - <span style="color: black;"> mMode = SCROLL_MODE; </span> - <span style="color: black;"> mFinished = <span class="keyword" style="font-weight: bold; color: #006699;">false</span>; </span> - <span style="color: black;"> mDuration = duration; </span> - <span style="color: black;"> mStartTime = AnimationUtils.currentAnimationTimeMillis(); </span> - <span style="color: black;"> mStartX = startX; </span> - <span style="color: black;"> mStartY = startY; </span> - <span style="color: black;"> mFinalX = startX + dx; </span> - <span style="color: black;"> mFinalY = startY + dy; </span> - <span style="color: black;"> mDeltaX = dx; </span> - <span style="color: black;"> mDeltaY = dy; </span> - <span style="color: black;"> mDurationReciprocal = <span class="number" style="color: #c00000;">1</span>.0f / (<span class="keyword" style="font-weight: bold; color: #006699;">float</span>) mDuration; </span> - <span style="color: black;"> } </span> </div> <div> 这个所谓的startScroll方法里面全部是对上面那些变量赋值的,比如将当前模式置为SCROLL_MODE,设置持续时间,起点终点偏移,起始时间等等。这个方法似乎并没有触发start scroll,恩,的确是这样的。那么到底怎么触发scroll呢?我们将这个问题留到下一个部分分析。 </div> <div> 为了让大家理解这些变量的作用,我画了一张图。 </div> ![](http://img.blog.csdn.net/20141202145628031?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY2hkamo=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)</div> <div> <div> 解释下,图中我们假设从A点滚动到B点,那么,**如果知道mStartX,mStartY(由startScroll的startX和startY参数得到)和mDeltaX,mDeltaY(由startScroll的dx和dy参数得到)。那么,mFinalX和mFinalY就很容易得到了。此外在加上duration持续时间,那么我们就可以根据(当前时间-mStartTime)占duration的比例来算出当前位置,也即mCurrX和mCurrY。**我不会告诉你,这就是computeScrollOffset的作用!通过上面分析我们发现,**startScroll方法其实相当于一个预处理,为computeScrollOffset提供数据。** </div> <div> </div> <div> 下面我们查看computeScrollOffset源码: </div> <div class="dp-highlighter bg_java"> <div class="bar"> <div class="tools" style="color: silver;"> **[java]** [view plain](http://blog.csdn.net/chdjj/article/details/41678897#)[copy](http://blog.csdn.net/chdjj/article/details/41678897#)[![在CODE上查看代码片](https://code.csdn.net/assets/CODE_ico.png)](https://code.csdn.net/snippets/539577)[![派生到我的代码片](https://code.csdn.net/assets/ico_fork.svg)](https://code.csdn.net/snippets/539577/fork) <div> </div> </div> </div> - <span style="color: black;"><span class="keyword" style="font-weight: bold; color: #006699;">public</span> <span class="keyword" style="font-weight: bold; color: #006699;">boolean</span> computeScrollOffset() { </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">if</span> (mFinished) { </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">return</span> <span class="keyword" style="font-weight: bold; color: #006699;">false</span>; </span> - <span style="color: black;"> } </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">int</span> timePassed = (<span class="keyword" style="font-weight: bold; color: #006699;">int</span>)(AnimationUtils.currentAnimationTimeMillis() &#8211; mStartTime); </span> - <span style="color: black;"> </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">if</span> (timePassed < mDuration) { </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">switch</span> (mMode) { </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">case</span> SCROLL_MODE: </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">final</span> <span class="keyword" style="font-weight: bold; color: #006699;">float</span> x = mInterpolator.getInterpolation(timePassed * mDurationReciprocal); </span> - <span style="color: black;"> mCurrX = mStartX + Math.round(x * mDeltaX); </span> - <span style="color: black;"> mCurrY = mStartY + Math.round(x * mDeltaY); </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">break</span>; </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">case</span> FLING_MODE: </span> - <span style="color: black;"> &#8230; &#8230; </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">break</span>; </span> - <span style="color: black;"> } </span> - <span style="color: black;"> } </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">else</span> { </span> - <span style="color: black;"> mCurrX = mFinalX; </span> - <span style="color: black;"> mCurrY = mFinalY; </span> - <span style="color: black;"> mFinished = <span class="keyword" style="font-weight: bold; color: #006699;">true</span>; </span> - <span style="color: black;"> } </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">return</span> <span class="keyword" style="font-weight: bold; color: #006699;">true</span>; </span> - <span style="color: black;"> } </span> </div> <div> 看,是不是很好理解了。**首先判断是否结束(mFinished==true?),如果没有结束,那么程序继续向下跑,计算timePassed,即当前时间减去开始时间,如果小于mDuration,那么就可以根据比例计算出当前位置,当然了,这里使用了Interpolator来控制动画的效果,加速或者减速等等。如果已经超过了mDuration了,那么滚动应该停止了,所以将mFinished置为ture,下次调用computeScrollOffset就会返回false了。** </div> <div> ** ** </div> <div> 到这里,我们把Scroller中的重要部分都分析完了,但是我们发现这两个方法都没有涉及具体滚动。那么滚动到底是如何触发的呢?下面我们将对整个流程进行源码分析。 </div> <div> </div> <div> **四.滚动流程分析** </div> <div> ** ** </div> <div> 在第一部分中,我给出了一个Scroller的使用示例,在smoothScrollTo方法中我们调用了Scroller的startScroll,然后调用了invalidate方法刷新视图,这个滚动效果就是由invalidate触发的!我们知道,调用了invalidate方法将会引起整个view系统的重绘,所以我们得从View的绘制说起。有经验的同学都应该知道,View的绘制包括三个主要过程,分别是measure,layout和draw。下面我们看View类的draw方法源码: </div> <div> </div> <div class="dp-highlighter bg_java"> <div class="bar"> <div class="tools" style="color: silver;"> **[java]** [view plain](http://blog.csdn.net/chdjj/article/details/41678897#)[copy](http://blog.csdn.net/chdjj/article/details/41678897#)[![在CODE上查看代码片](https://code.csdn.net/assets/CODE_ico.png)](https://code.csdn.net/snippets/539577)[![派生到我的代码片](https://code.csdn.net/assets/ico_fork.svg)](https://code.csdn.net/snippets/539577/fork) <div> </div> </div> </div> - <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> draw(Canvas canvas) { </span> - <span style="color: black;"> &#8230; &#8230; </span> - <span style="color: black;"> <span class="comment" style="color: #008200;">/*</span> </span> - <span style="color: black;"><span class="comment" style="color: #008200;"> * Draw traversal performs several drawing steps which must be executed</span> </span> - <span style="color: black;"><span class="comment" style="color: #008200;"> * in the appropriate order:</span> </span> - <span style="color: black;"><span class="comment" style="color: #008200;"> *</span> </span> - <span style="color: black;"><span class="comment" style="color: #008200;"> * 1. Draw the background</span> </span> - <span style="color: black;"><span class="comment" style="color: #008200;"> * 2. If necessary, save the canvas&#8217; layers to prepare for fading</span> </span> - <span style="color: black;"><span class="comment" style="color: #008200;"> * 3. Draw view&#8217;s content</span> </span> - <span style="color: black;"><span class="comment" style="color: #008200;"> * 4. Draw children</span> </span> - <span style="color: black;"><span class="comment" style="color: #008200;"> * 5. If necessary, draw the fading edges and restore layers</span> </span> - <span style="color: black;"><span class="comment" style="color: #008200;"> * 6. Draw decorations (scrollbars for instance)</span> </span> - <span style="color: black;"><span class="comment" style="color: #008200;"> */</span> </span> - <span style="color: black;"> <span class="comment" style="color: #008200;">// Step 1, draw the background, if needed</span> </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">int</span> saveCount; </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">if</span> (!dirtyOpaque) { </span> - <span style="color: black;"> drawBackground(canvas); </span> - <span style="color: black;"> } </span> - <span style="color: black;"> &#8230; &#8230; </span> - <span style="color: black;"> <span class="comment" style="color: #008200;">// Step 2, save the canvas&#8217; layers</span> </span> - <span style="color: black;"> &#8230; &#8230; </span> - <span style="color: black;"> <span class="comment" style="color: #008200;">// Step 3, draw the content</span> </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">if</span> (!dirtyOpaque) onDraw(canvas); </span> - <span style="color: black;"> <span class="comment" style="color: #008200;">// Step 4, draw the children</span> </span> - <span style="color: black;"> dispatchDraw(canvas); </span> - <span style="color: black;"> <span class="comment" style="color: #008200;">// Step 5, draw the fade effect and restore layers</span> </span> - <span style="color: black;"> &#8230; &#8230; </span> - <span style="color: black;"> } </span> </div> draw方法将绘制分成了六步,其中第三步是调用onDraw去绘制内容,第四步是调用dispatchDraw去绘制子视图。我们看下dispatchDraw方法: <div class="dp-highlighter bg_java"> <div class="bar"> <div class="tools" style="color: silver;"> **[java]** [view plain](http://blog.csdn.net/chdjj/article/details/41678897#)[copy](http://blog.csdn.net/chdjj/article/details/41678897#)[![在CODE上查看代码片](https://code.csdn.net/assets/CODE_ico.png)](https://code.csdn.net/snippets/539577)[![派生到我的代码片](https://code.csdn.net/assets/ico_fork.svg)](https://code.csdn.net/snippets/539577/fork) <div> </div> </div> </div> - <span style="color: black;"><span class="comment" style="color: #008200;">/**</span> </span> - <span style="color: black;"><span class="comment" style="color: #008200;"> * Called by draw to draw the child views. This may be overridden</span> </span> - <span style="color: black;"><span class="comment" style="color: #008200;"> * by derived classes to gain control just before its children are drawn</span> </span> - <span style="color: black;"><span class="comment" style="color: #008200;"> * (but after its own view has been drawn).</span> </span> - <span style="color: black;"><span class="comment" style="color: #008200;"> * @param canvas the canvas on which to draw the view</span> </span> - <span style="color: black;"><span class="comment" style="color: #008200;"> */</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> dispatchDraw(Canvas canvas) { </span> - <span style="color: black;"> } </span> </div> 恩,没错,是个空方法,因为只有ViewGroup才有子视图,所以我们来到ViewGroup中,找到dispatchDraw: <div class="dp-highlighter bg_java"> <div class="bar"> <div class="tools" style="color: silver;"> **[java]** [view plain](http://blog.csdn.net/chdjj/article/details/41678897#)[copy](http://blog.csdn.net/chdjj/article/details/41678897#)[![在CODE上查看代码片](https://code.csdn.net/assets/CODE_ico.png)](https://code.csdn.net/snippets/539577)[![派生到我的代码片](https://code.csdn.net/assets/ico_fork.svg)](https://code.csdn.net/snippets/539577/fork) <div> </div> </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: #006699;">protected</span> <span class="keyword" style="font-weight: bold; color: #006699;">void</span> dispatchDraw(Canvas canvas) { </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">boolean</span> usingRenderNodeProperties = canvas.isRecordingFor(mRenderNode); </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">final</span> <span class="keyword" style="font-weight: bold; color: #006699;">int</span> childrenCount = mChildrenCount; </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">final</span> View[] children = mChildren; </span> - <span style="color: black;"> &#8230; &#8230; </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 < childrenCount; i++) { </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">int</span> childIndex = customOrder ? getChildDrawingOrder(childrenCount, i) : i; </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">final</span> View child = (preorderedList == <span class="keyword" style="font-weight: bold; color: #006699;">null</span>) </span> - <span style="color: black;"> ? children[childIndex] : preorderedList.get(childIndex); </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">if</span> ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != <span class="keyword" style="font-weight: bold; color: #006699;">null</span>) { </span> - <span style="color: black;"> more |= drawChild(canvas, child, drawingTime); </span> - <span style="color: black;"> } </span> - <span style="color: black;"> } </span> - <span style="color: black;"> } </span> </div> 代码也是极其长的,这里截取了一部分。我们看到这个方法中调用了drawChild方法,从名字上就可以看出这个方法是用来绘制子视图的,找到其实现: <div class="dp-highlighter bg_java"> <div class="bar"> <div class="tools" style="color: silver;"> **[java]** [view plain](http://blog.csdn.net/chdjj/article/details/41678897#)[copy](http://blog.csdn.net/chdjj/article/details/41678897#)[![在CODE上查看代码片](https://code.csdn.net/assets/CODE_ico.png)](https://code.csdn.net/snippets/539577)[![派生到我的代码片](https://code.csdn.net/assets/ico_fork.svg)](https://code.csdn.net/snippets/539577/fork) <div> </div> </div> </div> - <span style="color: black;"><span class="keyword" style="font-weight: bold; color: #006699;">protected</span> <span class="keyword" style="font-weight: bold; color: #006699;">boolean</span> drawChild(Canvas canvas, View child, <span class="keyword" style="font-weight: bold; color: #006699;">long</span> drawingTime) { </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">return</span> child.draw(canvas, <span class="keyword" style="font-weight: bold; color: #006699;">this</span>, drawingTime); </span> - <span style="color: black;"> } </span> </div> 这里调用了view的draw方法,当然,这个draw方法跟上面那个draw不一样,因为它有三个参数!那还等什么,我们回到View的代码中找到这个含有三个参数的draw方法: <div class="dp-highlighter bg_java"> <div class="bar"> <div class="tools" style="color: silver;"> **[java]** [view plain](http://blog.csdn.net/chdjj/article/details/41678897#)[copy](http://blog.csdn.net/chdjj/article/details/41678897#)[![在CODE上查看代码片](https://code.csdn.net/assets/CODE_ico.png)](https://code.csdn.net/snippets/539577)[![派生到我的代码片](https://code.csdn.net/assets/ico_fork.svg)](https://code.csdn.net/snippets/539577/fork) <div> </div> </div> </div> - <span style="color: black;"><span class="keyword" style="font-weight: bold; color: #006699;">boolean</span> draw(Canvas canvas, ViewGroup parent, <span class="keyword" style="font-weight: bold; color: #006699;">long</span> drawingTime) { </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">boolean</span> more = <span class="keyword" style="font-weight: bold; color: #006699;">false</span>; </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">final</span> <span class="keyword" style="font-weight: bold; color: #006699;">boolean</span> childHasIdentityMatrix = hasIdentityMatrix(); </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">final</span> <span class="keyword" style="font-weight: bold; color: #006699;">int</span> flags = parent.mGroupFlags; </span> - <span style="color: black;"> &#8230; &#8230; </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">int</span> sx = <span class="number" style="color: #c00000;"></span>; </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">int</span> sy = <span class="number" style="color: #c00000;"></span>; </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">if</span> (!hasDisplayList) { </span> - <span style="color: black;"> computeScroll();<span class="comment" style="color: #008200;">//终于找到了computeScroll</span> </span> - <span style="color: black;"> sx = mScrollX; </span> - <span style="color: black;"> sy = mScrollY; </span> - <span style="color: black;"> } </span> - <span style="color: black;"> &#8230; &#8230; </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">return</span> more; </span> - <span style="color: black;"> } </span> </div> <div> 在其中,我们找到了computeScroll方法!当然View中的computeScroll方法是空的,需要我们根据场景自己复写。 </div> <div> 比如,我发现,TextView中就有复写这个方法: </div> <div class="dp-highlighter bg_java"> <div class="bar"> <div class="tools" style="color: silver;"> **[java]** [view plain](http://blog.csdn.net/chdjj/article/details/41678897#)[copy](http://blog.csdn.net/chdjj/article/details/41678897#)[![在CODE上查看代码片](https://code.csdn.net/assets/CODE_ico.png)](https://code.csdn.net/snippets/539577)[![派生到我的代码片](https://code.csdn.net/assets/ico_fork.svg)](https://code.csdn.net/snippets/539577/fork) <div> </div> </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: #006699;">public</span> <span class="keyword" style="font-weight: bold; color: #006699;">void</span> computeScroll() { </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">if</span> (mScroller != <span class="keyword" style="font-weight: bold; color: #006699;">null</span>) { </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">if</span> (mScroller.computeScrollOffset()) { </span> - <span style="color: black;"> mScrollX = mScroller.getCurrX(); </span> - <span style="color: black;"> mScrollY = mScroller.getCurrY(); </span> - <span style="color: black;"> invalidateParentCaches(); </span> - <span style="color: black;"> postInvalidate(); <span class="comment" style="color: #008200;">// So we draw again</span> </span> - <span style="color: black;"> } </span> - <span style="color: black;"> } </span> - <span style="color: black;"> } </span> </div> <div> 好了,折腾了这么久,**<span style="color: #000080;">下面我们再回顾下这个过程:我们在自定义view中调用了startScroll方法为滚动设置了一些基本数据,然后通过invalidate由上而下刷新view视图,首先是根视图(通常是ViewGroup)的draw方法被调用,然后调用onDraw绘制视图内容,接着dispatchDraw方法被调用去绘制子视图,dispatchDraw方法会对每个子视图调用drawChild方法,而drawChild方法会调用该子View的draw方法(三个参数),在draw方法中会调用computeScroll方法进行滚动,而computeScroll方法是被复写的,视场景而定,通常是根据computeScrollOffset来判断是否需要滑动,如果需要的话,接着调用postInvalidate再由上至下重新绘制,如此一来便实现了平滑滚动的效果!</span>** </div> <div> ok,到这里总算是把Scroller讲清楚了! </div> <div> </div> <div> **五.实例分析** </div> <div> ** ** </div> <div> 为了便于理解,我这里写了一个小例子,通过打印日志的形式让大家理解上面所讲的内容。 </div> <div> 首先是几个自定义的view: </div> <div> </div> <div> MyLinearLayout: </div> <div class="dp-highlighter bg_java"> <div class="bar"> <div class="tools" style="color: silver;"> **[java]** [view plain](http://blog.csdn.net/chdjj/article/details/41678897#)[copy](http://blog.csdn.net/chdjj/article/details/41678897#)[![在CODE上查看代码片](https://code.csdn.net/assets/CODE_ico.png)](https://code.csdn.net/snippets/539577)[![派生到我的代码片](https://code.csdn.net/assets/ico_fork.svg)](https://code.csdn.net/snippets/539577/fork) <div> </div> </div> </div> - <span style="color: black;"><span class="keyword" style="font-weight: bold; color: #006699;">package</span> com.example.scrollerdemo; </span> - <span style="color: black;"><span class="keyword" style="font-weight: bold; color: #006699;">import</span> android.content.Context; </span> - <span style="color: black;"><span class="keyword" style="font-weight: bold; color: #006699;">import</span> android.graphics.Canvas; </span> - <span style="color: black;"><span class="keyword" style="font-weight: bold; color: #006699;">import</span> android.util.AttributeSet; </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.widget.LinearLayout; </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> MyLinearLayout <span class="keyword" style="font-weight: bold; color: #006699;">extends</span> LinearLayout </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;">static</span> <span class="keyword" style="font-weight: bold; color: #006699;">final</span> String TAG = <span class="string" style="color: blue;">&#8220;TEST&#8221;</span>; </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">public</span> MyLinearLayout(Context context) </span> - <span style="color: black;"> { </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">super</span>(context); </span> - <span style="color: black;"> } </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">public</span> MyLinearLayout(Context context, AttributeSet attrs) </span> - <span style="color: black;"> { </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">super</span>(context, attrs); </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> draw(Canvas canvas) </span> - <span style="color: black;"> { </span> - <span style="color: black;"> Log.i(TAG, <span class="string" style="color: blue;">&#8220;MyLinearLayout&#8212;>draw&#8221;</span>); </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">super</span>.draw(canvas); </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> onDraw(Canvas canvas) </span> - <span style="color: black;"> { </span> - <span style="color: black;"> Log.i(TAG, <span class="string" style="color: blue;">&#8220;MyLinearLayout&#8212;>onDraw&#8221;</span>); </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">super</span>.onDraw(canvas); </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> computeScroll() </span> - <span style="color: black;"> { </span> - <span style="color: black;"> Log.i(TAG, <span class="string" style="color: blue;">&#8220;MyLinearLayout&#8212;>computeScroll&#8221;</span>); </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">super</span>.computeScroll(); </span> - <span style="color: black;"> } </span> - <span style="color: black;">} </span> </div> MyView(实现了平滑滚动效果): <div class="dp-highlighter bg_java"> <div class="bar"> <div class="tools" style="color: silver;"> **[java]** [view plain](http://blog.csdn.net/chdjj/article/details/41678897#)[copy](http://blog.csdn.net/chdjj/article/details/41678897#)[![在CODE上查看代码片](https://code.csdn.net/assets/CODE_ico.png)](https://code.csdn.net/snippets/539577)[![派生到我的代码片](https://code.csdn.net/assets/ico_fork.svg)](https://code.csdn.net/snippets/539577/fork) <div> </div> </div> </div> - <span style="color: black;"><span class="keyword" style="font-weight: bold; color: #006699;">package</span> com.example.scrollerdemo; </span> - <span style="color: black;"><span class="keyword" style="font-weight: bold; color: #006699;">import</span> android.content.Context; </span> - <span style="color: black;"><span class="keyword" style="font-weight: bold; color: #006699;">import</span> android.graphics.Canvas; </span> - <span style="color: black;"><span class="keyword" style="font-weight: bold; color: #006699;">import</span> android.util.AttributeSet; </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.LinearLayout; </span> - <span style="color: black;"><span class="keyword" style="font-weight: bold; color: #006699;">import</span> android.widget.Scroller; </span> - <span style="color: black;"><span class="comment" style="color: #008200;">/**</span> </span> - <span style="color: black;"><span class="comment" style="color: #008200;"> * @author Rowandjj</span> </span> - <span style="color: black;"><span class="comment" style="color: #008200;"> * </span> </span> - <span style="color: black;"><span class="comment" style="color: #008200;"> */</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;">class</span> MyView <span class="keyword" style="font-weight: bold; color: #006699;">extends</span> LinearLayout </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;">static</span> <span class="keyword" style="font-weight: bold; color: #006699;">final</span> String TAG = <span class="string" style="color: blue;">&#8220;TEST&#8221;</span>; </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">private</span> Scroller mScroller; </span> - <span style="color: black;"> </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">public</span> MyView(Context context) </span> - <span style="color: black;"> { </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">super</span>(context); </span> - <span style="color: black;"> init(); </span> - <span style="color: black;"> } </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">public</span> MyView(Context context, AttributeSet attrs) </span> - <span style="color: black;"> { </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">super</span>(context, attrs); </span> - <span style="color: black;"> init(); </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> init() </span> - <span style="color: black;"> { </span> - <span style="color: black;"> mScroller = <span class="keyword" style="font-weight: bold; color: #006699;">new</span> Scroller(getContext()); </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;">void</span> testSmoothScroll() </span> - <span style="color: black;"> { </span> - <span style="color: black;"> <span class="comment" style="color: #008200;">//向右下方平滑滚动(向右偏移100,向下偏移100)</span> </span> - <span style="color: black;"> smoothScrollTo(-<span class="number" style="color: #c00000;">100</span>,-<span class="number" style="color: #c00000;">100</span>); </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> smoothScrollTo(<span class="keyword" style="font-weight: bold; color: #006699;">int</span> destX,<span class="keyword" style="font-weight: bold; color: #006699;">int</span> destY) </span> - <span style="color: black;"> { </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">int</span> scrollX = getScrollX(); </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">int</span> scrollY = getScrollY(); </span> - <span style="color: black;"> Log.d(TAG,<span class="string" style="color: blue;">&#8220;scrollX=&#8221;</span>+scrollX+<span class="string" style="color: blue;">&#8220;,scrollY=&#8221;</span>+scrollY); </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">int</span> deltaX = destX-scrollX; </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">int</span> deltaY = destY-scrollY; </span> - <span style="color: black;"> mScroller.startScroll(scrollX,scrollY,deltaX, deltaY, <span class="number" style="color: #c00000;">1000</span>); </span> - <span style="color: black;"> invalidate(); </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> draw(Canvas canvas) </span> - <span style="color: black;"> { </span> - <span style="color: black;"> Log.i(TAG,<span class="string" style="color: blue;">&#8220;MyView&#8212;&#8212;>draw run&#8221;</span>); </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">super</span>.draw(canvas); </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> onDraw(Canvas canvas) </span> - <span style="color: black;"> { </span> - <span style="color: black;"> Log.i(TAG,<span class="string" style="color: blue;">&#8220;MyView&#8212;&#8212;>onDraw run&#8221;</span>); </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">super</span>.onDraw(canvas); </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;">protected</span> <span class="keyword" style="font-weight: bold; color: #006699;">void</span> dispatchDraw(Canvas canvas) </span> - <span style="color: black;"> { </span> - <span style="color: black;"> Log.i(TAG,<span class="string" style="color: blue;">&#8220;MyView&#8212;&#8212;>dispatchDraw run&#8221;</span>); </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">super</span>.dispatchDraw(canvas); </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;">protected</span> <span class="keyword" style="font-weight: bold; color: #006699;">boolean</span> drawChild(Canvas canvas, View child, <span class="keyword" style="font-weight: bold; color: #006699;">long</span> drawingTime) </span> - <span style="color: black;"> { </span> - <span style="color: black;"> Log.i(TAG,<span class="string" style="color: blue;">&#8220;MyView&#8212;&#8212;>drawChild run&#8221;</span>); </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">return</span> <span class="keyword" style="font-weight: bold; color: #006699;">super</span>.drawChild(canvas, child, drawingTime); </span> - <span style="color: black;"> } </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> computeScroll() </span> - <span style="color: black;"> { </span> - <span style="color: black;"> Log.i(TAG,<span class="string" style="color: blue;">&#8220;MyView&#8212;&#8212;>computeScroll run&#8221;</span>); </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">if</span>(mScroller != <span class="keyword" style="font-weight: bold; color: #006699;">null</span>) </span> - <span style="color: black;"> { </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">if</span>(mScroller.computeScrollOffset()) </span> - <span style="color: black;"> { </span> - <span style="color: black;"> scrollTo(mScroller.getCurrX(),mScroller.getCurrY()); </span> - <span style="color: black;"> Log.d(TAG,<span class="string" style="color: blue;">&#8220;scrollX=&#8221;</span>+getScrollX()+<span class="string" style="color: blue;">&#8220;,scrollY=&#8221;</span>+getScrollY()); </span> - <span style="color: black;"> postInvalidate(); </span> - <span style="color: black;"> } </span> - <span style="color: black;"> } </span> - <span style="color: black;"> } </span> - <span style="color: black;">} </span> </div> MyImageView: <div class="dp-highlighter bg_java"> <div class="bar"> <div class="tools" style="color: silver;"> **[java]** [view plain](http://blog.csdn.net/chdjj/article/details/41678897#)[copy](http://blog.csdn.net/chdjj/article/details/41678897#)[![在CODE上查看代码片](https://code.csdn.net/assets/CODE_ico.png)](https://code.csdn.net/snippets/539577)[![派生到我的代码片](https://code.csdn.net/assets/ico_fork.svg)](https://code.csdn.net/snippets/539577/fork) <div> </div> </div> </div> - <span style="color: black;"><span class="keyword" style="font-weight: bold; color: #006699;">package</span> com.example.scrollerdemo; </span> - <span style="color: black;"><span class="keyword" style="font-weight: bold; color: #006699;">import</span> android.content.Context; </span> - <span style="color: black;"><span class="keyword" style="font-weight: bold; color: #006699;">import</span> android.graphics.Canvas; </span> - <span style="color: black;"><span class="keyword" style="font-weight: bold; color: #006699;">import</span> android.util.AttributeSet; </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.widget.ImageView; </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> MyImageView <span class="keyword" style="font-weight: bold; color: #006699;">extends</span> ImageView </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;">static</span> <span class="keyword" style="font-weight: bold; color: #006699;">final</span> String TAG = <span class="string" style="color: blue;">&#8220;TEST&#8221;</span>; </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">public</span> MyImageView(Context context) </span> - <span style="color: black;"> { </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">super</span>(context); </span> - <span style="color: black;"> } </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">public</span> MyImageView(Context context, AttributeSet attrs) </span> - <span style="color: black;"> { </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">super</span>(context, attrs); </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> onDraw(Canvas canvas) </span> - <span style="color: black;"> { </span> - <span style="color: black;"> Log.i(TAG,<span class="string" style="color: blue;">&#8220;myImageView&#8212;->onDraw run&#8221;</span>); </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">super</span>.onDraw(canvas); </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> computeScroll() </span> - <span style="color: black;"> { </span> - <span style="color: black;"> Log.i(TAG,<span class="string" style="color: blue;">&#8220;myImageView&#8212;->computeScroll run&#8221;</span>); </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">super</span>.computeScroll(); </span> - <span style="color: black;"> } </span> - <span style="color: black;">} </span> </div> <div> 我们根据上面三个自定义view做一个布局,外层是MyLinearLayout,中间是MyView,最里面是一个MyImageView. </div> <div> activity_main.xml: </div> <div class="dp-highlighter bg_java"> <div class="bar"> <div class="tools" style="color: silver;"> **[java]** [view plain](http://blog.csdn.net/chdjj/article/details/41678897#)[copy](http://blog.csdn.net/chdjj/article/details/41678897#)[![在CODE上查看代码片](https://code.csdn.net/assets/CODE_ico.png)](https://code.csdn.net/snippets/539577)[![派生到我的代码片](https://code.csdn.net/assets/ico_fork.svg)](https://code.csdn.net/snippets/539577/fork) <div> </div> </div> </div> - <span style="color: black;"><LinearLayout xmlns:android=<span class="string" style="color: blue;">&#8220;http://schemas.android.com/apk/res/android&#8221;</span> </span> - <span style="color: black;"> android:layout_width=<span class="string" style="color: blue;">&#8220;match_parent&#8221;</span> </span> - <span style="color: black;"> android:layout_height=<span class="string" style="color: blue;">&#8220;match_parent&#8221;</span> </span> - <span style="color: black;"> android:orientation=<span class="string" style="color: blue;">&#8220;vertical&#8221;</span> > </span> - <span style="color: black;"> <com.example.scrollerdemo.MyLinearLayout </span> - <span style="color: black;"> xmlns:tools=<span class="string" style="color: blue;">&#8220;http://schemas.android.com/tools&#8221;</span> </span> - <span style="color: black;"> android:layout_width=<span class="string" style="color: blue;">&#8220;match_parent&#8221;</span> </span> - <span style="color: black;"> android:layout_height=<span class="string" style="color: blue;">&#8220;match_parent&#8221;</span> </span> - <span style="color: black;"> android:background=<span class="string" style="color: blue;">&#8220;#ffaaff&#8221;</span> </span> - <span style="color: black;"> tools:context=<span class="string" style="color: blue;">&#8220;com.example.scrollerdemo.MainActivity&#8221;</span> > </span> - <span style="color: black;"> <com.example.scrollerdemo.MyView </span> - <span style="color: black;"> android:id=<span class="string" style="color: blue;">&#8220;@+id/mv&#8221;</span> </span> - <span style="color: black;"> android:layout_width=<span class="string" style="color: blue;">&#8220;200dp&#8221;</span> </span> - <span style="color: black;"> android:layout_height=<span class="string" style="color: blue;">&#8220;200dp&#8221;</span> </span> - <span style="color: black;"> android:layout_marginLeft=<span class="string" style="color: blue;">&#8220;50dp&#8221;</span> </span> - <span style="color: black;"> android:layout_marginTop=<span class="string" style="color: blue;">&#8220;100dp&#8221;</span> </span> - <span style="color: black;"> android:background=<span class="string" style="color: blue;">&#8220;@android:color/darker_gray&#8221;</span> </span> - <span style="color: black;"> android:orientation=<span class="string" style="color: blue;">&#8220;vertical&#8221;</span> > </span> - <span style="color: black;"> <com.example.scrollerdemo.MyImageView </span> - <span style="color: black;"> android:layout_width=<span class="string" style="color: blue;">&#8220;wrap_content&#8221;</span> </span> - <span style="color: black;"> android:layout_height=<span class="string" style="color: blue;">&#8220;wrap_content&#8221;</span> </span> - <span style="color: black;"> android:src=<span class="string" style="color: blue;">&#8220;@drawable/ic_launcher&#8221;</span> /> </span> - <span style="color: black;"> </com.example.scrollerdemo.MyView> </span> - <span style="color: black;"> </com.example.scrollerdemo.MyLinearLayout> </span> - <span style="color: black;"></LinearLayout> </span> </div> 下面是MainActivity的代码: <div class="dp-highlighter bg_java"> <div class="bar"> <div class="tools" style="color: silver;"> **[java]** [view plain](http://blog.csdn.net/chdjj/article/details/41678897#)[copy](http://blog.csdn.net/chdjj/article/details/41678897#)[![在CODE上查看代码片](https://code.csdn.net/assets/CODE_ico.png)](https://code.csdn.net/snippets/539577)[![派生到我的代码片](https://code.csdn.net/assets/ico_fork.svg)](https://code.csdn.net/snippets/539577/fork) <div> </div> </div> </div> - <span style="color: black;"><span class="keyword" style="font-weight: bold; color: #006699;">package</span> com.example.scrollerdemo; </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.view.View; </span> - <span style="color: black;"><span class="keyword" style="font-weight: bold; color: #006699;">import</span> android.view.View.OnClickListener; </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 class="keyword" style="font-weight: bold; color: #006699;">implements</span> OnClickListener </span> - <span style="color: black;">{ </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">private</span> MyView mv = <span class="keyword" style="font-weight: bold; color: #006699;">null</span>; </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;"> </span> - <span style="color: black;"> mv = (MyView) findViewById(R.id.mv); </span> - <span style="color: black;"> mv.setOnClickListener(<span class="keyword" style="font-weight: bold; color: #006699;">this</span>); </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> onClick(View v) </span> - <span style="color: black;"> { </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">switch</span> (v.getId()) </span> - <span style="color: black;"> { </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">case</span> R.id.mv:<span class="comment" style="color: #008200;">//点击时触发滚动效果</span> </span> - <span style="color: black;"> mv.testSmoothScroll(); </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">break</span>; </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">default</span>: </span> - <span style="color: black;"> <span class="keyword" style="font-weight: bold; color: #006699;">break</span>; </span> - <span style="color: black;"> } </span> - <span style="color: black;"> } </span> - <span style="color: black;">} </span> </div> 代码很简单,不必过多介绍,下面看显示效果: </div> <div> ![](http://img.blog.csdn.net/20141202145938259?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY2hkamo=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center) 打开logcat查看日志: ...

2014年12月3日 · 25 分钟 · 天边的星星

Android 文字环绕 图文混排 支持Span折叠

先直接上效果图 上图为实现目标,实现了Android图文混排,文字环绕,支持Span的识别,表情的嵌入,支持文字字体大小的设置等。 由于项目中需要用到图文混排技术,在此稍微研究了两天,出来一个效果还算不错的东西 图文混排技术,在不少Android应用中都已经实现,说穿了其实就是两个TextView加一个ImageView的布局罢了,代码里面实现下String的剪切就可以了,不过我这里的这个除了要实现混排效果外,还要支持Span,支持表情等,这就有点麻烦了。下面慢慢分解。先贴出RichTextImageView的布局。 <?xml version=”1.0″ encoding=”utf-8″?> <com.demonzym.richtextdemo.RichTextImageView xmlns:android=”http://schemas.android.com/apk/res/android” android:layout_width=”fill_parent” android:layout_height=”fill_parent” android:orientation=”vertical” android:id=”@+id/richview” > <LinearLayout android:id=”@+id/linearLayout1″ android:layout_width=”fill_parent” android:layout_height=”wrap_content” > <RelativeLayout android:id=”@+id/layout_preimage_isgif_left” android:layout_width=”wrap_content” android:layout_height=”wrap_content” android:layout_weight=”0″ android:visibility=”gone” > ...

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

Android onTouchEvent, onClick及onLongClick的调用机制

针对屏幕上的一个View控件,Android如何区分应当触发onTouchEvent,还是onClick,亦或是onLongClick事件? 在Android中,一次用户操作可以被不同的View按次序分别处理,并将完全响应了用户一次UI操作称之为消费了该事件(consume),那么Android是按什么次序将事件传递的呢?又在什么情况下判定为消费了该事件? 搞清楚这些问题对于编写出能正确响应UI操作的代码是很重要的,尤其当屏幕上的不同View需要针对此次UI操作做出各种不同响应的时候更是如此,一个典型例子就是用户在桌面上放置了一个Widget,那么当用户针对widget做各种操作时,桌面本身有的时候要对用户的操作做出响应,有时忽略。只有搞清楚事件触发和传递的机制才有可能保证在界面布局非常复杂的情况下,UI控件仍然能正确响应用户操作。 1. onTouchEvent onTouchEvent中要处理的最常用的3个事件就是:ACTION_DOWN、ACTION_MOVE、ACTION_UP。 这三个事件标识出了最基本的用户触摸屏幕的操作,含义也很清楚。虽然大家天天都在用它们,但是有一点请留意,ACTION_DOWN事件作为起始事件,它的重要性是要超过ACTION_MOVE和ACTION_UP的,如果发生了ACTION_MOVE或者ACTION_UP,那么一定曾经发生了ACTION_DOWN。 从Android的源代码中能看到基于这种不同重要性的理解而实现的一些交互机制,SDK中也有明确的提及,例如在ViewGroup的onInterceptTouchEvent方法中,如果在ACTION_DOWN事件中返回了true,那么后续的事件将直接发给onTouchEvent,而不是继续发给onInterceptTouchEvent。 2. onClick、onLongClick与onTouchEvent 曾经看过一篇帖子提到,如果在View中处理了onTouchEvent,那么就不用再处理onClick了,因为Android只会触发其中一个方法。这个理解是不太正确的,针对某个view,用户完成了一次触碰操作,显然从传感器上得到的信号是手指按下和抬起两个操作,我们可以理解为一次Click,也可以理解为发生了一次ACTION_DOWN和ACTION_UP,那么Android是如何理解和处理的呢? 在Android中,onClick、onLongClick的触发是和ACTION_DOWN及ACTION_UP相关的,在时序上,如果我们在一个View中同时覆写了onClick、onLongClick及onTouchEvent的话,onTouchEvent是最先捕捉到ACTION_DOWN和ACTION_UP事件的,其次才可能触发onClick或者onLongClick。主要的逻辑在View.java中的onTouchEvent方法中实现的: case MotionEvent.ACTION_DOWN: mPrivateFlags |= PRESSED; refreshDrawableState(); if ((mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) { postCheckForLongClick(); } break; case MotionEvent.ACTION_UP: if ((mPrivateFlags & PRESSED) != 0) { boolean focusTaken = false; if (isFocusable() && isFocusableInTouchMode() && !isFocused()) { focusTaken = requestFocus(); } if (!mHasPerformedLongPress) { if (mPendingCheckForLongPress != null) { removeCallbacks(mPendingCheckForLongPress); } if (!focusTaken) { performClick(); } } … break; 可以看到,Click的触发是在系统捕捉到ACTION_UP后发生并由performClick()执行的,performClick里会调用先前注册的监听器的onClick()方法: public boolean performClick() { … if (mOnClickListener != null) { playSoundEffect(SoundEffectConstants.CLICK); mOnClickListener.onClick(this); return true; ...

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