├── 2016 ├── 2016.10 │ ├── Android 一些重要知识点解析整理.md │ ├── Android-新特性V1.0.doc │ ├── Github学习指南.md │ ├── Handler removeCallbacks 无效问题.md │ ├── Handler原理梳理.md │ ├── ViewPager smoothScroll 速度控制.md │ ├── [Android] 设计模式-策略模式.md │ ├── [Android]设计模式-工厂模式2.md │ ├── [ApiDemos] AnimatorEvents 源码分析.md │ ├── [ApiDemos] ListFlipper 的简单剖析.md │ ├── aidl 浅析.md │ ├── 什么是学习能力.md │ ├── 从今天开始抛弃Fragment吧!.md │ ├── 单例模式.md │ └── 手势识别Doc.md ├── 2016.11 │ ├── Android studio TraceView和lint工具的使用.md │ ├── IPC机制md.md │ ├── RxJava.md │ ├── WebView学习笔记.md │ ├── material_animations学习笔记.md │ ├── 常见内存泄漏场景以及解决办法.md │ └── 简单面试题.md └── 2016.12 │ ├── 了解注解.md │ ├── 守望先锋动画效果.md │ └── 沉浸式状态栏.md ├── 2017 ├── 2017.1 │ ├── AtomicInteger简介.md │ └── android studio常用快捷键.md ├── 2017.2 │ ├── VBA笔记.md │ ├── 一直会动的跑马灯.md │ ├── 中止一个线程的科学方法.md │ ├── 屏幕适配工具类.md │ └── 阿里巴巴java开发手册.pdf ├── 2017.3 │ ├── EditText与Button联动.md │ ├── Path学习笔记之Bezier.md │ ├── Path学习笔记之基本操作.md │ ├── Path实战之雷达图.md │ ├── 可删除内容的EditText.md │ ├── 密码显示与隐藏EditText.md │ ├── 屏幕适配.md │ ├── 既可删除内容也可显示隐藏的EditText.md │ ├── 自定义时间选择器.md │ └── 雷达图全部代码.md └── 2017.4 │ └── 连击特效全部代码.md ├── .gitattributes ├── .gitignore ├── README.md └── 读书笔记 ├── 《Android 源码设计模式解析与实战》学习笔记 ├── [Android]《Android 源码设计模式解析与实战》读书笔记 1.md ├── [Android]《Android 源码设计模式解析与实战》读书笔记 2.md ├── [Android]《Android 源码设计模式解析与实战》读书笔记 3.md ├── [Android]《Android 源码设计模式解析与实战》读书笔记 4.md ├── [Android]《Android 源码设计模式解析与实战》读书笔记 5.md ├── [Android]《Android 源码设计模式解析与实战》读书笔记 6.md └── [Android]《Android 源码设计模式解析与实战》读书笔记 7.md ├── 《Java8实战学习笔记》学习笔记 └── 通过行为参数化传递代码.md └── 编程之法(全程kotlin实现) └── 旋转字符串.md /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # Files for the Dalvik VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # Generated files 12 | bin/ 13 | gen/ 14 | 15 | # Gradle files 16 | .gradle/ 17 | build/ 18 | 19 | # Local configuration file (sdk path, etc) 20 | local.properties 21 | 22 | # Proguard folder generated by Eclipse 23 | proguard/ 24 | 25 | # Log Files 26 | *.log 27 | 28 | # ========================= 29 | # Operating System Files 30 | # ========================= 31 | 32 | # OSX 33 | # ========================= 34 | 35 | .DS_Store 36 | .AppleDouble 37 | .LSOverride 38 | 39 | # Thumbnails 40 | ._* 41 | 42 | # Files that might appear on external disk 43 | .Spotlight-V100 44 | .Trashes 45 | 46 | # Directories potentially created on remote AFP share 47 | .AppleDB 48 | .AppleDesktop 49 | Network Trash Folder 50 | Temporary Items 51 | .apdisk 52 | 53 | # Windows 54 | # ========================= 55 | 56 | # Windows image file caches 57 | Thumbs.db 58 | ehthumbs.db 59 | 60 | # Folder config file 61 | Desktop.ini 62 | 63 | # Recycle Bin used on file shares 64 | $RECYCLE.BIN/ 65 | 66 | # Windows Installer files 67 | *.cab 68 | *.msi 69 | *.msm 70 | *.msp 71 | 72 | # Windows shortcuts 73 | *.lnk 74 | -------------------------------------------------------------------------------- /2016/2016.10/Android 一些重要知识点解析整理.md: -------------------------------------------------------------------------------- 1 | ## Android中的异步任务机制 2 | 3 | * [Android中AsyncTak的使用与源码分析](http://blog.csdn.net/bboyfeiyu/article/details/8973058) 4 | 5 | * [Android AsyncTask 完全解析,带你从源码的角度彻底理解](http://blog.csdn.net/guolin_blog/article/details/11711405) 6 | 7 | * [Android 异步消息处理机制完全解析,带你从源码角度彻底理解](http://blog.csdn.net/guolin_blog/article/details/9991569) 8 | 9 | * [Android 异步消息处理机制 让你深入理解 Looper、Handler、Message三者关系](http://blog.csdn.net/lmj623565791/article/details/38377229) 10 | 11 | * [Android消息循环分析](http://blog.isming.me/2014/04/02/android-message-loop-analyze/) 12 | 13 | 14 | 15 | ## Android Activity 16 | 17 | * [developer 官网](http://developer.android.com/guide/topics/manifest/activity-element.html#lmode) (强烈推荐 dev guide) 18 | 19 | * [Android的启动模式(android:launchMode)](http://blog.csdn.net/lincyang/article/details/6826021) 20 | 21 | * [Activity 的启动模式(android:launchMode)](http://blog.csdn.net/feng88724/article/details/6412638) 22 | 23 | * [Android Activity 的四种启动模式](http://blog.csdn.net/android_tutor/article/details/6310015) 24 | 25 | * [管理Activity的生命周期](http://blog.isming.me/2014/03/25/manage-activityde-lifecycle/) 26 | 27 | * [Android入门:Activity四种启动模式](http://www.cnblogs.com/meizixiong/archive/2013/07/03/3170591.html) 28 | 29 | 30 | 31 | ## Android Intent 32 | 33 | * [Android中Intent传递对象的两种方法(Serializable,Parcelable)](http://blog.csdn.net/android_tutor/article/details/5740845) 34 | 35 | *评论 29,85,96* 36 | 37 | * 序列化 [Serializable与Parcelable传递对象详解(activity间传递对象方式)](http://blog.csdn.net/hudan2714/article/details/7856130)* 38 | 39 | * [Intent匹配规则以及解析框架深入分析](http://blog.csdn.net/qinjuning/article/details/7384906) 40 | 41 | * [基础总结篇之九:Intent应用详解](http://blog.csdn.net/liuhe688/article/details/7162988) 42 | 43 | * [android Intent机制详解](http://blog.csdn.net/t12x3456/article/details/7688154) 44 | 45 | 46 | 47 | ## Android线程间通信 48 | 49 | * [Android并发编程-线程间通信的三种基本方式](http://blog.csdn.net/manoel/article/details/38964563) 50 | 51 | * [Android Service与Activity之间通信的几种方式](http://blog.csdn.net/xiaanming/article/details/9750689) 52 | 53 | *评论 19* 54 | 55 | * [Android中程序与Service交互的方式——交互方式](http://blog.csdn.net/yihongyuelan/article/details/7216188) 56 | 57 | * [Android的进程与线程](http://android.jobbole.com/80640/) 58 | 59 | * [Andorid Binder进程间通信总结](http://blog.csdn.net/jltxgcy/article/details/30993741) 60 | 61 | 62 | 63 | ## Android UI 及布局 64 | 65 | * [Android 中的像素单位](http://www.cnblogs.com/bluestorm/archive/2012/10/04/2711508.html) 66 | 67 | * [你有多熟悉listview](http://www.cnblogs.com/noTice520/archive/2011/12/05/2276379.html) 68 | 69 | * [Android Fragment完全解析,关于碎片你所需知道的一切](http://blog.csdn.net/guolin_blog/article/details/8881711) 70 | 71 | * [Android中将布局文件/View添加至窗口过程分析 ---- 从setContentView()谈起](http://blog.csdn.net/qinjuning/article/details/7226787) 72 | 73 | * [Android中的尺寸单位](http://www.cnblogs.com/bluestorm/archive/2012/10/04/2711508.html) 74 | 75 | * [Android中View绘制流程以及invalidate()等相关方法分析](http://blog.csdn.net/qinjuning/article/details/7110211) 76 | 77 | 78 | 79 | ## Android 网络访问 80 | 81 | * [Android Volley 完全解析系列](http://blog.csdn.net/guolin_blog/article/details/17482095) 82 | 83 | * [Android中网络操作使用总结](http://blog.isming.me/2014/05/11/use-network-in-android/) 84 | 85 | * [HTTP协议之状态码详解](http://v5browser.iteye.com/blog/1769789) 86 | 87 | * [Volley是否支持https](http://www.jiangwenrou.com/volley%E6%98%AF%E5%90%A6%E6%94%AF%E6%8C%81https.html) 88 | 89 | 90 | ## OOM 问题 91 | 92 | * [Android 高效加载大图、多图解决方案,有效避免程序 OOM](http://blog.csdn.net/guolin_blog/article/details/9316683) 93 | 94 | 95 | 96 | ## ANR 问题 97 | 98 | * [什么是 ANR ? 如何避免它?](http://blog.csdn.net/Zengyangtech/article/details/6025671) 99 | 100 | 101 | 102 | ## Android 事件分发机制 103 | 104 | * [Android 中的事件处理](http://blog.csdn.net/dawanganban/article/details/19285977) 105 | 106 | * [Android事件分发机制完全解析,带你从源码的角度彻底理解(上)](http://blog.csdn.net/guolin_blog/article/details/9097463) 107 | 108 | * [Android事件分发机制完全解析,带你从源码的角度彻底理解(下)](http://blog.csdn.net/guolin_blog/article/details/9153747) 109 | 110 | * [Adnroid View 事件分发机制 源码解析](http://blog.csdn.net/lmj623565791/article/details/38960443) 111 | 112 | * [Android ViewGroup事件分发机制](http://blog.csdn.net/lmj623565791/article/details/39102591) 113 | 114 | * [Andriod 从源码的角度详解View,ViewGroup的Touch事件的分发机制](http://blog.csdn.net/xiaanming/article/details/21696315) 115 | 116 | 117 | 118 | ## Android 进程间通信 119 | 120 | * [Android aidl Binder框架浅析](http://blog.csdn.net/lmj623565791/article/details/38461079) 121 | 122 | * [Android AIDL 实例解析](http://blog.csdn.net/bboyfeiyu/article/details/39003759) 123 | 124 | * [Android 进程间传递复杂数据(AIDL)](http://blog.csdn.net/dawanganban/article/details/17524581) 125 | 126 | * [Android IPC进程间通讯机制学习笔记](http://www.cnblogs.com/bluestorm/archive/2011/11/05/2298125.html) 127 | 128 | * [Android中的跨进程通信的实现(一)——远程调用过程和 AIDL](http://foocoder.com/blog/androidzhong-de-kua-jin-cheng-tong-xin-de-shi-xian-%28%5B%3F%5D-%29-yuan-cheng-diao-yong-guo-cheng-he-aidl.html/) 129 | 130 | 131 | 132 | ## Android底层及Linux 133 | 134 | * [Android系统开发-linux进程基本概念](http://blog.csdn.net/dawanganban/article/details/38854817) 135 | 136 | * [Android的虚拟机Dalvik 介绍](http://blog.csdn.net/Android_Tutor/article/details/5334228) 137 | 138 | * [ANDROID内存优化(大汇总——全)](http://blog.csdn.net/a396901990/article/details/38904543) 139 | 140 | * [Android系统启动过程](http://blog.csdn.net/jltxgcy/article/details/46669147) 141 | 142 | 143 | 144 | ## Android 数据存储与数据解析 145 | 146 | * [android解析XML总结(SAX、Pull、Dom三种方式)](http://www.cnblogs.com/JerryWang1991/archive/2012/02/24/2365507.html) 147 | 148 | 149 | 150 | ## Android 架构与设计模式 151 | 152 | * [一种在android中实现MVP模式的新思路](https://github.com/bboyfeiyu/android-tech-frontier/tree/master/androidweekly/%E4%B8%80%E7%A7%8D%E5%9C%A8android%E4%B8%AD%E5%AE%9E%E7%8E%B0MVP%E6%A8%A1%E5%BC%8F%E7%9A%84%E6%96%B0%E6%80%9D%E8%B7%AF) 153 | 154 | * [一种更清晰的Android架构](https://github.com/bboyfeiyu/android-tech-frontier/tree/master/androidweekly/%E4%B8%80%E7%A7%8D%E6%9B%B4%E6%B8%85%E6%99%B0%E7%9A%84Android%E6%9E%B6%E6%9E%84) 155 | 156 | 157 | 158 | ## 其他 159 | 160 | * [一个经典例子让你彻彻底底理解java回调机制](http://blog.csdn.net/xiaanming/article/details/8703708) 161 | 162 | * [Java 并发面试题](http://blog.csdn.net/geolo/article/details/8670900) 163 | 164 | *题可以看看,至于答案,就要仔细斟酌了* 165 | 166 | * [从 MVC 框架看 MVC 架构的设计](http://kb.cnblogs.com/page/502983/) 167 | 168 | * [Android四大基本组件介绍与生命周期](http://www.cnblogs.com/bravestarrhu/archive/2012/05/02/2479461.html) 169 | 170 | * [Android6.0 新特性详解](http://leanote.com/blog/post/561658f938f41126b2000298?hmsr=toutiao.io&utm_medium=toutiao.io&utm_source=toutiao.io) 171 | 172 | * [国内一线互联网公司内部面试题库](https://github.com/JackyAndroid/AndroidInterview-Q-A/blob/master/README-CN.md) 173 | 174 | * [Android 学习资料收集](https://github.com/Freelander/Android_Data) 175 | 176 | * [Android 学习笔记](https://github.com/GeniusVJR/LearningNotes) 177 | 178 | * [编程学习笔记](https://github.com/CharonChui/AndroidNote) 179 | -------------------------------------------------------------------------------- /2016/2016.10/Android-新特性V1.0.doc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jutao/AndroidNode/192c2fd063e3a5cfe67713521f9720bc1b4d53bb/2016/2016.10/Android-新特性V1.0.doc -------------------------------------------------------------------------------- /2016/2016.10/Github学习指南.md: -------------------------------------------------------------------------------- 1 | # 目录 2 | - [Git](#git) 3 | - [Git视频](#git视频) 4 | - [Github](#github) 5 | - [Github系列教程](#github系列教程) 6 | 7 | # Git 8 | 官方文档: 9 | 10 | |Pro Git(必看)|网址| 11 | |-----|------| 12 | |英文|https://git-scm.com/book/en/v2| 13 | |中文|https://git-scm.com/book/zh/v2| 14 | 15 | 廖雪峰Git教程: 16 | 17 | 在线学习:http://learngitbranching.js.org/?NODEMO 18 | 19 | git简明指南:http://rogerdudler.github.io/git-guide/index.zh.html 20 | 21 | ## Git UI 22 | git for Windows: https://code.google.com/p/msysgit/downloads/list?q=full+installer+official+git 23 | 24 | tortoisegit: https://code.google.com/p/tortoisegit/ 25 | 26 | Sourcetree: https://www.sourcetreeapp.com/ 27 | 28 | ## Git Flow(成功的Git分支模型) 29 | A successful Git branching model: http://nvie.com/posts/a-successful-git-branching-model/ 30 | 31 | 翻译:http://www.juvenxu.com/2010/11/28/a-successful-git-branching-model/ 32 | 33 | 工具:https://github.com/nvie/gitflow 34 | 35 | git-flow 备忘清单: http://danielkummer.github.io/git-flow-cheatsheet/index.zh_CN.html 36 | 37 | ## Git视频 38 | Learn git in 20 minutes: 39 | 40 | Git Video Tutorial1-4: 41 | 42 | # Github 43 | Github Pull Requests: 44 | 45 | |使用GitHub进行团队合作|网址| 46 | |------|-----| 47 | |英文| | 48 | |中文| | 49 | 50 | 如何发现优秀的开源项目: 51 | 52 | GitHub秘籍: 53 | 54 | 如何高效利用GitHub:http://www.yangzhiping.com/tech/github.html 55 | 56 | 怎样使用 GitHub(文科妹纸):https://www.zhihu.com/question/20070065/answer/79557687 57 | 58 | ## Github系列教程 59 | |从0开始学习 GitHub 系列|网站| 60 | |----|---| 61 | |初识 GitHub|| 62 | |加入Github|| 63 | |Git速成|| 64 | |向Github提交代码|| 65 | |Git进阶|| 66 | 67 | 68 | -------------------------------------------------------------------------------- /2016/2016.10/Handler removeCallbacks 无效问题.md: -------------------------------------------------------------------------------- 1 | # 问题起因 # 2 | > 点击按钮就调用 handler.post(runnable); 就能启动定时器,这里是每隔1s打印线程名字,从打印中我们可以知道,他并没有另开线程,而是运行在 UI 线程当中,当你要取消定时器的时候,只需要调用 handler.removeCallbacks(runnable) 就可以了。 3 | > 上面中有一个问题,有时候你会发现removeCallbacks有时候会失效,不能从消息队列中移除,看下面的代码 4 | > 5 | > public class TimerActivity extends Activity { 6 | > Handler handler = new Handler(); 7 | > Runnable runnable = new Runnable() { 8 | > 9 | > @Override 10 | > public void run() { 11 | > System.out.println("update..."); 12 | > handler.postDelayed(runnable, 1000); 13 | > } 14 | > }; 15 | > 16 | > @Override 17 | > protected void onCreate(Bundle savedInstanceState) { 18 | > super.onCreate(savedInstanceState); 19 | > setContentView(R.layout.layout_roll_view); 20 | > 21 | > Button mButtonStart = (Button) findViewById(R.id.button1); 22 | > Button mButtonStop = (Button) findViewById(R.id.button2); 23 | > 24 | > mButtonStart.setOnClickListener(new OnClickListener() { 25 | > 26 | > @Override 27 | > public void onClick(View v) { 28 | > handler.post(runnable); 29 | > } 30 | > }); 31 | > 32 | > mButtonStop.setOnClickListener(new OnClickListener() { 33 | > 34 | > @Override 35 | > public void onClick(View v) { 36 | > handler.removeCallbacks(runnable); 37 | > } 38 | > }); 39 | > } 40 | > 41 | > } 42 | 43 | # Handler removeCallbacks 无效问题# 44 | > 当Activity进入后台运行后再转入前台运行,removeCallbacks 无法将 updateThread 从 message queue 中移除。 45 | > 46 | > 这是为什么呢? 47 | > 48 | > 在 Activity 由前台转后台过程中,线程是一直在运行的,但是当 Activity 转入前台时会重新定义 Runnable runnable;也就是说此时从message queue 移除的 runnable 与原先加入 message queue中的 runnable 并非是同一个对象。如果把 runnable 定义为静态的则removeCallbacks 不会失效,对于静态变量在内存中只有一个拷贝(节省内存),JVM只为静态分配一次内存,在加载类的过程中完成静态变量的内存分配。 49 | 50 | 也就是说 removeCallbacks 有时会出现移除失效的问题主要原因出在 runnable 对象上。 51 | 52 | 但是尽管我用了 removeCallbacksAndMessages 方法,依然有时会出现失效的现象,输出内存地址比较过后,发现 handler 对象也会发生变化。 53 | 54 | # 解决方案 # 55 | 在 Application中建立容器存储 handler 和 runnable 对象,关闭时使用容器来关闭。 56 | 57 | public static Map sHandlerMap=new HashMap<>(); 58 | public static Map sRunnableMap=new HashMap<>(); 59 | 60 | public static void stop() { 61 | for (int i=0;i"); 79 | 80 | Looper.prepareMainLooper(); 81 | 82 | //省略 83 | } 84 | 85 | 可以看到上面的代码调用了一个 Looper.prepareMainLooper() 方法,我一开始以为只要我在子线程也调用 Looper.prepareMainLooper() 方法,就可以在子线程修改 UI 了,但是报错如下: 86 | 87 | java.lang.IllegalStateException: The main Looper has already been prepared. 88 | 也就是说,我的想法还不算太荒谬, Looper.prepareMainLooper() 方法就是区分子线程和主线程的关键所在。我们进去看看它都做了些什么: 89 | 90 | public static void prepareMainLooper() { 91 | prepare(false); 92 | synchronized (Looper.class) { 93 | if (sMainLooper != null) { 94 | throw new IllegalStateException("The main Looper has already been prepared."); 95 | } 96 | sMainLooper = myLooper(); 97 | } 98 | } 99 | 原来如此,虽然主线程最终也调用了 prepare() 方法,但是给的值是 false,我们之前调用 prepare() 方法,默认值为 true,是不是感觉恍然大悟呢,不得不赞叹源码写的真是巧妙啊!! 100 | 101 | 到这里,我相信大家都和我一样,明白了为什么要先调用 Looper.prepare() 方法才可以创建 Handler 对象。我想过自己重写一个Handler 对象试试看能不能跳过这一步,但是你会发现,没有 Looper对象,你创建了也是白搭呀,至于为什么白搭,继续看下面的分析。 102 | 103 | # Handler 消息发送 # 104 | 105 | new Thread(new Runnable() { 106 | @Override public void run() { 107 | Message message = new Message(); 108 | message.what=1; 109 | Bundle bundle = new Bundle(); 110 | bundle.putString("data", "data"); 111 | message.setData(bundle); 112 | handler.sendMessage(message); 113 | } 114 | }).start(); 115 | 这段代码相信大家都非常熟悉了,那它到底把 Message 发到哪里去了呢?Handler 给我们提供了很多方法来发送消息,有 post 的,也有 send 的。通过观察源码,你会发现,除了 enqueueMessage() 方法,其他所有发送消息的方法最后都会走到 sendMessageAtTime() 方法中,但是他们最终都会调用 MessageQueue 类中的 enqueueMessage() 方法,这肯定就是入队方法了: 116 | 117 | boolean enqueueMessage(Message msg, long when) { 118 | if (msg.isInUse()) { 119 | throw new AndroidRuntimeException(msg + " This message is already in use."); 120 | } 121 | if (msg.target == null) { 122 | throw new AndroidRuntimeException("Message must have a target."); 123 | } 124 | 125 | synchronized (this) { 126 | if (mQuitting) { 127 | RuntimeException e = new RuntimeException( 128 | msg.target + " sending message to a Handler on a dead thread"); 129 | Log.w("MessageQueue", e.getMessage(), e); 130 | return false; 131 | } 132 | 133 | msg.when = when; 134 | Message p = mMessages; 135 | boolean needWake; 136 | if (p == null || when == 0 || when < p.when) { 137 | // New head, wake up the event queue if blocked. 138 | msg.next = p; 139 | mMessages = msg; 140 | needWake = mBlocked; 141 | } else { 142 | // Inserted within the middle of the queue. Usually we don't have to wake 143 | // up the event queue unless there is a barrier at the head of the queue 144 | // and the message is the earliest asynchronous message in the queue. 145 | needWake = mBlocked && p.target == null && msg.isAsynchronous(); 146 | Message prev; 147 | for (;;) { 148 | prev = p; 149 | p = p.next; 150 | if (p == null || when < p.when) { 151 | break; 152 | } 153 | if (needWake && p.isAsynchronous()) { 154 | needWake = false; 155 | } 156 | } 157 | msg.next = p; // invariant: p == prev.next 158 | prev.next = msg; 159 | } 160 | 161 | // We can assume mPtr != 0 because mQuitting is false. 162 | if (needWake) { 163 | nativeWake(mPtr); 164 | } 165 | } 166 | return true; 167 | } 168 | 169 | 通过源码可知,这个消息队列实际上是按照发送延时时间,也就是 when 来降序排序的,这样我们发送的消息就按照发送时间排好队了,但是他们排好队要去哪里呢,也就是出队操作在哪执行呢?我们再来分析 ActivityThread 中的 main 方法: 170 | 171 | public static void main(String[] args) { 172 | 173 | //省略部分源码 174 | 175 | Looper.prepareMainLooper(); 176 | 177 | ActivityThread thread = new ActivityThread(); 178 | thread.attach(false); 179 | 180 | if (sMainThreadHandler == null) { 181 | sMainThreadHandler = thread.getHandler(); 182 | } 183 | 184 | AsyncTask.init(); 185 | 186 | if (false) { 187 | Looper.myLooper().setMessageLogging(new 188 | LogPrinter(Log.DEBUG, "ActivityThread")); 189 | } 190 | 191 | Looper.loop(); 192 | 193 | throw new RuntimeException("Main thread loop unexpectedly exited"); 194 | } 195 | 你会发现,Looper.prepareMainLooper() 或者 Looper.prepare() 方法总是和 Looper.loop() 方法对应,有你必有它,那么我可以合理的怀疑这个 Looper.loop() 方法很有可能就是执行出队操作的方法: 196 | 197 | public static void loop() { 198 | final Looper me = myLooper(); 199 | if (me == null) { 200 | throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); 201 | } 202 | final MessageQueue queue = me.mQueue; 203 | 204 | // Make sure the identity of this thread is that of the local process, 205 | // and keep track of what that identity token actually is. 206 | Binder.clearCallingIdentity(); 207 | final long ident = Binder.clearCallingIdentity(); 208 | 209 | for (;;) { 210 | Message msg = queue.next(); // might block 211 | if (msg == null) { 212 | // No message indicates that the message queue is quitting. 213 | return; 214 | } 215 | 216 | // This must be in a local variable, in case a UI event sets the logger 217 | Printer logging = me.mLogging; 218 | if (logging != null) { 219 | logging.println(">>>>> Dispatching to " + msg.target + " " + 220 | msg.callback + ": " + msg.what); 221 | } 222 | 223 | msg.target.dispatchMessage(msg); 224 | 225 | if (logging != null) { 226 | logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); 227 | } 228 | 229 | // Make sure that during the course of dispatching the 230 | // identity of the thread wasn't corrupted. 231 | final long newIdent = Binder.clearCallingIdentity(); 232 | if (ident != newIdent) { 233 | Log.wtf(TAG, "Thread identity changed from 0x" 234 | + Long.toHexString(ident) + " to 0x" 235 | + Long.toHexString(newIdent) + " while dispatching to " 236 | + msg.target.getClass().getName() + " " 237 | + msg.callback + " what=" + msg.what); 238 | } 239 | 240 | msg.recycle(); 241 | } 242 | } 243 | 可以看到,这段代码从 13 行 开始进入了一个死循环,Message msg = queue.next() 就是我们要找的出队方法,而且还是一个阻塞方法,它的简单逻辑就是如果当前 MessageQueue 中存在 mMessages(即待处理消息),就将这个消息出队,然后让下一条消息成为 mMessages,否则就进入一个阻塞状态,一直等到有新的消息入队。接下来比较重要的代码就是 msg.target.dispatchMessage(msg),这个 target 其实就是 Handler 发送消息的 Handler 对象,观察 handler 调用入队方法的必经之路 enqueueMessage() 方法可知: 244 | 245 | private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { 246 | msg.target = this; 247 | if (mAsynchronous) { 248 | msg.setAsynchronous(true); 249 | } 250 | return queue.enqueueMessage(msg, uptimeMillis); 251 | } 252 | 253 | 看上面代码的第二行。 254 | 255 | 所以我们可以知道,loop() 方法把最新出队的 message 又传给了 Handler 对象 的 dispatchMessage() 方法,所以我们肯定要观察 dispatchMessage() 方法了。 256 | 257 | public void dispatchMessage(Message msg) { 258 | if (msg.callback != null) { 259 | handleCallback(msg); 260 | } else { 261 | if (mCallback != null) { 262 | if (mCallback.handleMessage(msg)) { 263 | return; 264 | } 265 | } 266 | handleMessage(msg); 267 | } 268 | } 269 | 270 | dispatchMessage() 方法的逻辑为:如果 msg.callback 不为空(callback 一般是通过 Handler 的 post 系列方法设置的,是一个 Runnable 对象),则执行 message.callback.run() 方法。否则判断 mCallback 如果不为空,则调用 mCallback 的 handleMessage()方法,否则直接调用 Handler 的 handleMessage() 方法,并将消息对象作为参数传递过去。 271 | 272 | # 总结 # 273 | 上面就是 Handler 的一个完整的从信息发送到执行的流程。流程图如下: 274 | ![](http://i.imgur.com/SGuBgaN.png) -------------------------------------------------------------------------------- /2016/2016.10/ViewPager smoothScroll 速度控制.md: -------------------------------------------------------------------------------- 1 | # 问题 # 2 | 大家都知道 ViewPager 可以通过 mViewPager.setCurrentItem(index, true) 来始切换动画时进行平缓的滑动,但是如果我们的需求是控制滑动时间的话,会发现 ViewPager 好像并没有提供这一个方法。我们可以查看ViewPager 的源码: 3 | 4 | public void setCurrentItem(int item, boolean smoothScroll) { 5 | mPopulatePending = false; 6 | setCurrentItemInternal(item, smoothScroll, false); 7 | } 8 | 发现 setCurrentItem 走的是一个 setCurrentItemInternal(item, smoothScroll, false) 方法 9 | 10 | void setCurrentItemInternal(int item, boolean smoothScroll, boolean always) { 11 | setCurrentItemInternal(item, smoothScroll, always, 0); 12 | } 13 | 而这个方法所调用的 void setCurrentItemInternal(int item, boolean smoothScroll, boolean always, int velocity) 方法貌似是可以指定速度的,但是先别高兴的太早,这个方法居然不是 public 的。。也就是说我们调用不了。但是这也难不倒我们,用反射就轻松解决了。 有兴趣你可以用反射调用这个方法试试看,反正我是试过了,速度好像并没有什么明显的变化,再仔细看了看源码,我们发现最终走的 smoothScrollTo 方法中有一句 14 | 15 | duration = Math.min(duration, MAX_SETTLE_DURATION) 16 | 17 | 然而我们发现 ViewPager 中有一个常量 private static final int MAX_SETTLE_DURATION = 600; // ms 18 | 19 | 也就是说 duration 无论如何都不会超过 600ms 的,那我们偏要大于 600ms 呢? 20 | 21 | 毕竟道高一尺魔高一丈,我们发现有了 duration 后,会调用 mScroller.startScroll(sx, sy, dx, dy, duration) 方法,终究还是要走 mScroller 的,那我们用反射修改 mScroller不就行了吗? 22 | 23 | # 效果前后展示 # 24 | ![](http://i.imgur.com/9rJD03b.gif) 25 | 26 | 27 | ![已经减速.gif](http://upload-images.jianshu.io/upload_images/3054656-8ae1bc06d367e234.gif?imageMogr2/auto-orient/strip) 28 | 29 | # 解决方案 # 30 | 重写一个 Scroller 类: 31 | 32 | /** 33 | * 利用这个类来修正ViewPager的滑动速度 34 | * 我们重写 startScroll方法,忽略传过来的 duration 属性 35 | * 而是采用我们自己设置的时间 36 | */ 37 | public class FixedSpeedScroller extends Scroller { 38 | 39 | public int mDuration=1500; 40 | public FixedSpeedScroller(Context context) { 41 | super(context); 42 | } 43 | 44 | public FixedSpeedScroller(Context context, Interpolator interpolator) { 45 | super(context, interpolator); 46 | } 47 | 48 | public FixedSpeedScroller(Context context, Interpolator interpolator, boolean flywheel) { 49 | super(context, interpolator, flywheel); 50 | } 51 | 52 | @Override public void startScroll(int startX, int startY, int dx, int dy) { 53 | startScroll(startX,startY,dx,dy,mDuration); 54 | } 55 | 56 | @Override public void startScroll(int startX, int startY, int dx, int dy, int duration) { 57 | //管你 ViewPager 传来什么时间,我完全不鸟你 58 | super.startScroll(startX, startY, dx, dy, mDuration); 59 | } 60 | 61 | public int getmDuration() { 62 | return mDuration; 63 | } 64 | 65 | public void setmDuration(int duration) { 66 | mDuration = duration; 67 | } 68 | } 69 | 70 | 利用反射把我们的 FixedSpeedScroller 类设置给 ViewPager 71 | 72 | /** 73 | * 通过反射来修改 ViewPager的mScroller属性 74 | */ 75 | try { 76 | Class clazz=Class.forName("android.support.v4.view.ViewPager"); 77 | Field f=clazz.getDeclaredField("mScroller"); 78 | FixedSpeedScroller fixedSpeedScroller=new FixedSpeedScroller(this,new LinearOutSlowInInterpolator()); 79 | fixedSpeedScroller.setmDuration(2000); 80 | f.setAccessible(true); 81 | f.set(mViewPager,fixedSpeedScroller); 82 | } catch (Exception e) { 83 | e.printStackTrace(); 84 | } 85 | 86 | 这个时候再设置 mViewPager.setCurrentItem(index, true) 的时候应该就可以看到缓慢滑动的效果了。 -------------------------------------------------------------------------------- /2016/2016.10/[Android] 设计模式-策略模式.md: -------------------------------------------------------------------------------- 1 | # 最简单的商场收银软件 # 2 | 3 | 如果要做一款收银软件,营业员根据客户所购买商品单价和数量向客户收费,这非常容易。 4 | 5 | Demo 如下: 6 | 7 | ![](http://i.imgur.com/XBz9iW5.png) 8 | 9 | 点击确定后的代码逻辑如下: 10 | 11 | private void doEnter() { 12 | String stringTotal = tv_total.getText().toString().trim(); 13 | double total; 14 | if (stringTotal != null && stringTotal != "") { 15 | total = Double.valueOf(stringTotal); 16 | } else { 17 | total = 0.0d; 18 | } 19 | 20 | String stringDJ = et_dj.getText().toString().trim(); 21 | String stringSL = et_sl.getText().toString().trim(); 22 | if (stringDJ != null && !stringDJ.equals("") && stringSL != null && !stringSL.equals("")) { 23 | Log.d("TAG", "123" + stringDJ + "123"); 24 | double price = Double.valueOf(stringDJ); 25 | int number = Integer.valueOf(stringSL); 26 | double totalPrice = price * number; 27 | total = total + totalPrice; 28 | tv_total.setText(String.valueOf(total)); 29 | String text = tv_detail.getText().toString() 30 | + "单价: " 31 | + price 32 | + " 数量:" 33 | + number 34 | + " 合计:" 35 | + totalPrice 36 | + "\n"; 37 | tv_detail.setText(text); 38 | } 39 | } 40 | 41 | # 增加打折功能后的收银软件 42 | 可是如果商场搞促销,需要打折该怎么办,不可能每次都要修改代码然后重新安装,用下拉框可能会比较方便。 43 | Demo 如下: 44 | 45 | ![](http://i.imgur.com/sLqCANA.png) 46 | 47 | 添加的代码如下: 48 | 49 | switch (sp_jsfs.getSelectedItemPosition()){ 50 | case 1: 51 | totalPrice*=0.8; 52 | break; 53 | case 2: 54 | totalPrice*=0.7; 55 | break; 56 | case 3: 57 | totalPrice*=0.5; 58 | break; 59 | } 60 | 这样看似解决了问题,但是需求不断增加,比如满300返50之类,这样的代码未免显得太过重复。接下来我们试着用简单工厂模式来解决问题试试。 61 | 62 | # 简单工厂实现 # 63 | > 面向对象的编程,并不是类越多越好,类的划分是为了封装,但分类的基础是抽象,具有相同属性和功能的对象抽象集合才是类。 64 | 65 | 打一折和九折只是形式的不同,抽象分析出来,所有打折算法都是一样的,所以打折算法应该是一个类。返现算法也是一个类。 66 | 67 | MainActivity 改动如下 68 | CashSuper cSuper= CashFactor.createCashAccept(sp_jsfs.getSelectedItemPosition()); 69 | totalPrice=cSuper.acceptCash(totalPrice); 70 | 详细代码可以去最后上传的Demo里看 71 | 简单工厂模式虽然也能解决问题,但只是解决对象创建的问题,而且由于工厂本身包括了所有的收费方式,商场是可能经常性地更改打折和返利额度,每次维护或扩展收费方式都要改动这个工厂,以至代码要重新编译部署,这是很糟糕的,所以我们需要另一种新的设计模式--策略模式。 72 | # 策略模式 # 73 | ## 什么是策略模式 ## 74 | > 策略模式就是定义一系列算法,把他们独立封装起来,并且这些算法之间可以相互替换。策略模式主要是管理一堆有共性的算法,客户端可以根据需要,很快切换这些算法,并且保持可扩展性。 75 | > 策略模式的本质:分离算法,选择实现。 76 | 77 | ## 如何运用到收银系统中## 78 | 商场收银如何促销,用打折还是返利,其实都是一些算法,用工厂来生成算法对象,这没有错,但算法本身只是一种策略,最重要的是这些算法是随时都可能互相替换的,这是变化点,而封装变化点是我们面向对象的一种很重要的思维方式。 79 | 80 | 以下是策略模式 UML 图 81 | ![](http://i.imgur.com/WxFL8fa.png) 82 | 83 | 接下来我们将策略模式运用到收银系统中 84 | 首先创建一个 CashContext 代码如下: 85 | 86 | public class CashContext { 87 | private CashSuper cs; 88 | 89 | public CashContext(CashSuper cs) { 90 | this.cs = cs; 91 | 92 | } 93 | public double GetResule(double money){ 94 | return cs.acceptCash(money); 95 | } 96 | } 97 | 然后改动 MainActivity 如下: 98 | 99 | CashContext cc = null; 100 | switch (sp_jsfs.getSelectedItemPosition()) { 101 | case 0: 102 | cc = new CashContext(new CashNormal()); 103 | break; 104 | case 1: 105 | cc = new CashContext(new CashReturn(300, 100)); 106 | break; 107 | case 2: 108 | cc = new CashContext(new CashRebate(0.8)); 109 | break; 110 | case 3: 111 | cc = new CashContext(new CashRebate(0.7)); 112 | break; 113 | case 4: 114 | cc = new CashContext(new CashRebate(0.5)); 115 | break; 116 | } 117 | totalPrice = cc.GetResule(totalPrice); 118 | 119 | 120 | 这时候,你会发现,我们又像原来一样在 MainActivity 中写了判断,可以试着将之前的工厂模式和策略模式结合吗? 121 | 122 | ## 策略模式与简单工厂结合## 123 | 将 CashContext 类的构造方法修改如下: 124 | 125 | public CashContext(int type) { 126 | switch (type) { 127 | case 0: 128 | cs=new CashNormal(); 129 | break; 130 | case 1: 131 | cs=new CashReturn(300,100); 132 | break; 133 | case 2: 134 | cs = new CashRebate(0.8); 135 | break; 136 | case 3: 137 | cs = new CashRebate(0.7); 138 | break; 139 | case 4: 140 | cs = new CashRebate(0.5); 141 | break; 142 | } 143 | } 144 | MainActivity 代码修改如下: 145 | 146 | CashContext cc = new CashContext(sp_jsfs.getSelectedItemPosition()); 147 | totalPrice = cc.GetResule(totalPrice); 148 | total = total + totalPrice; 149 | 150 | 这样客户端只需要认识一个类 CashContext就可以了,耦合度进一步降低了。 151 | 不过这样一旦需求变化依旧需要修改 switch ,其实想要更好的实现可以用反射方法,具体用法下次再做讨论。 152 | 153 | # Demo # 154 | 155 | [策略模式 Demo](https://github.com/jutao/strategymodel) 156 | 157 | # 参考文献 # 158 | 159 | 《大话设计模式》 160 | 161 | [安卓设计模式--策略模式](http://mobile.51cto.com/ahot-418972.htm) 162 | -------------------------------------------------------------------------------- /2016/2016.10/[Android]设计模式-工厂模式2.md: -------------------------------------------------------------------------------- 1 |

工厂模式

2 | 3 |

关于工厂模式的基本概念和使用方法,工厂模式 中已经有了不错的介绍了。

4 | 5 |

反射和工厂模式的结合

6 | 7 |

@黛千秋 在她介绍工厂模式的例子中写了一个关于工厂模式的例子,在这借用一下。

8 | 9 |

以下代码为工厂模式结合反射实例,这样可以不用帮每种饮料都创建一个工厂类: 10 | 其他基本代码都一样这里不贴,先是创建一个饮料工厂抽象类

11 | 12 |
    public abstract class DrinkFactory {
 13 |         public abstract T createDrink(Class clz);   
 14 |     }
 15 | 
16 | 17 |

接下来是抽象方法的实现类:

18 | 19 |
    public class DrinkFactoryimpl extends DrinkFactory {
 20 | 
 21 |         @Override
 22 |         public  T createDrink(Class clz) {
 23 |             Drink drink = null;
 24 |             try {
 25 |                 drink = (Drink) Class.forName(clz.getName()).newInstance();
 26 |             } catch (Exception e) {
 27 |                 e.printStackTrace();
 28 |             }
 29 | 
 30 |             return (T) drink;
 31 |         }
 32 | 
 33 |     }
 34 | 
35 | 36 |

最后是客户端方法:

37 | 38 |
    public class Console {
 39 |         public static void main(String[] args) {
 40 |             DrinkFactory factory=new DrinkFactoryimpl();
 41 | 
 42 |             Drink d1=factory.createDrink(Cola.class);
 43 |             d1.kind();
 44 | 
 45 |             Drink d2=factory.createDrink(Coffee.class);
 46 |             d2.kind();
 47 | 
 48 |             Drink d3=factory.createDrink(Fanta.class);
 49 |             d3.kind();
 50 | 
 51 |         }
 52 |     }
 53 | 
54 | 55 |

运行结果:

56 | 57 |

58 | 59 |

Java 中的工厂模式实现

60 | 61 |

List 和 Set都继承于 Collection 接口,而 Collection 接口继承于 Iterable 接口.

62 | 63 |
    public interface Iterable {
 64 |         Iterator iterator();
 65 |     }
 66 | 
67 | 68 |

以上是 Iterable 接口内的一个方法,也就是迭代器。我们使用 ArrayList 的时候常常这样使用:

69 | 70 |
    ArrayList.iterator();
 71 | 
72 | 73 |

方法来返回一个迭代器对象。我们来看一下 ArrayList 的源码:

74 | 75 |
    public Iterator iterator() {
 76 |         return new Itr();
 77 |     }
 78 | 
 79 |     private class Itr implements Iterator {
 80 |         int cursor;       // index of next element to return
 81 |         int lastRet = -1; // index of last element returned; -1 if no such
 82 |         int expectedModCount = modCount;
 83 | 
 84 |         public boolean hasNext() {
 85 |             return cursor != size;
 86 |         }
 87 | 
 88 |         @SuppressWarnings("unchecked")
 89 |         public E next() {
 90 |             checkForComodification();
 91 |             int i = cursor;
 92 |             if (i >= size)
 93 |                 throw new NoSuchElementException();
 94 |             Object[] elementData = ArrayList.this.elementData;
 95 |             if (i >= elementData.length)
 96 |                 throw new ConcurrentModificationException();
 97 |             cursor = i + 1;
 98 |             return (E) elementData[lastRet = i];
 99 |         }
100 | 
101 |         public void remove() {
102 |             if (lastRet < 0)
103 |                 throw new IllegalStateException();
104 |             checkForComodification();
105 | 
106 |             try {
107 |                 ArrayList.this.remove(lastRet);
108 |                 cursor = lastRet;
109 |                 lastRet = -1;
110 |                 expectedModCount = modCount;
111 |             } catch (IndexOutOfBoundsException ex) {
112 |                 throw new ConcurrentModificationException();
113 |             }
114 |         }
115 | 
116 |         @Override
117 |         @SuppressWarnings("unchecked")
118 |         public void forEachRemaining(Consumer consumer) {
119 |             Objects.requireNonNull(consumer);
120 |             final int size = ArrayList.this.size;
121 |             int i = cursor;
122 |             if (i >= size) {
123 |                 return;
124 |             }
125 |             final Object[] elementData = ArrayList.this.elementData;
126 |             if (i >= elementData.length) {
127 |                 throw new ConcurrentModificationException();
128 |             }
129 |             while (i != size && modCount == expectedModCount) {
130 |                 consumer.accept((E) elementData[i++]);
131 |             }
132 |             // update once at end of iteration to reduce heap write traffic
133 |             cursor = i;
134 |             lastRet = i - 1;
135 |             checkForComodification();
136 |         }
137 | 
138 |         final void checkForComodification() {
139 |             if (modCount != expectedModCount)
140 |                 throw new ConcurrentModificationException();
141 |         }
142 |           }
143 | 
144 | 145 |

ArrayList 中的 itertor 方法其实就相当于一个工厂方法,专门为 new 对象而生。

146 | 147 |

Android 中的工厂方法模式实现

148 | 149 |
    public class AActivity extends Activity {
150 |       @Override protected void onCreate(Bundle savedInstanceState) {
151 |         super.onCreate(savedInstanceState);
152 |         setContentView(new LinearLayout(this));
153 |       }
154 |     }
155 | 
156 | 157 |

构造一个线性布局 LinerarLayout 对象并设置为当前 Activity 的根布局,Oncreate 方法就相当于一个工厂方法,为什么呢,我们知道,所有控件都是View的子类,上面代码中,AActivity 的 onCreate 方法构造一个 View 对象,并设置为当前界面的 ContentView 返回给 framework 处理,如果现在又有一个 BActivity,这时我们又在其 onCreate 方法中通过 setContentView 方法设置另外不同的View,这是不是就是一个工厂模式的结构呢?

158 | 159 |

参考资料

160 | 161 |

《Android 源码设计模式解析与实战》

-------------------------------------------------------------------------------- /2016/2016.10/[ApiDemos] AnimatorEvents 源码分析.md: -------------------------------------------------------------------------------- 1 | # AnimatorEvents 效果 # 2 | AnimatorEvents 是 ApiDemos 弹球动画效果之一,他的效果为绘制小球并移动。效果如下图所示: 3 | 4 | 起始: 5 | 6 | ![](http://i.imgur.com/TX0SeM5.png) 7 | 8 | 结束: 9 | 10 | ![](http://i.imgur.com/YrFSwuT.png) 11 | 12 | 下面来一步步分析如何从创建小球到实现小球的移动的。 13 | 14 | # 创建小球 # 15 | 首先创建一个存储小球属性的类 ShapeHolder,然后开始绘制小球,绘制代码如下: 16 | 17 | private ShapeHolder createBall(float x, float y) { 18 | OvalShape circle = new OvalShape(); 19 | circle.resize(50f, 50f); 20 | ShapeDrawable drawable = new ShapeDrawable(circle); 21 | ShapeHolder shapeHolder = new ShapeHolder(drawable); 22 | shapeHolder.setX(x - 25f); 23 | shapeHolder.setY(y - 25f); 24 | //下面是通过随机的方法去生成红绿蓝三个值.,从而组合成ARGB的值,设置为该圆的颜色 25 | int red = (int)(Math.random() * 255); 26 | int green = (int)(Math.random() * 255); 27 | int blue = (int)(Math.random() * 255); 28 | int color = 0xff000000 | red << 16 | green << 8 | blue; 29 | //每个ShapeDrawable对象都有自己的paint,直接getPaint()就能获取了 30 | Paint paint = drawable.getPaint(); //new Paint(Paint.ANTI_ALIAS_FLAG); 31 | int darkColor = 0xff000000 | red/4 << 16 | green/4 << 8 | blue/4; 32 | //给上面生成的ARGB值,添加圆的中心到边缘颜色从深到浅的渐变效果 33 | RadialGradient gradient = new RadialGradient(37.5f, 12.5f, 34 | 50f, color, darkColor, Shader.TileMode.CLAMP); 35 | paint.setShader(gradient); 36 | //到了这一步,圆的颜色效果已经确定了 37 | shapeHolder.setPaint(paint); 38 | return shapeHolder; 39 | } 40 | 41 | # 小球移动 # 42 | 设置小球移动的代码如下: 43 | 44 | private void createAnimation() { 45 | if (animation == null) { 46 | //创建小球纵向移动属性动画 47 | ObjectAnimator yAnim = ObjectAnimator.ofFloat(ball, "y", 48 | ball.getY(), getHeight() - 50f).setDuration(1500); 49 | yAnim.setRepeatCount(0); 50 | yAnim.setRepeatMode(ValueAnimator.REVERSE); 51 | yAnim.setInterpolator(new AccelerateInterpolator(2f)); 52 | yAnim.addUpdateListener(this); 53 | yAnim.addListener(this); 54 | 55 | //创建横向移动属性动画 56 | ObjectAnimator xAnim = ObjectAnimator.ofFloat(ball, "x", 57 | ball.getX(), ball.getX() + 300).setDuration(1000); 58 | xAnim.setStartDelay(0); 59 | xAnim.setRepeatCount(0); 60 | xAnim.setRepeatMode(ValueAnimator.REVERSE); 61 | xAnim.setInterpolator(new AccelerateInterpolator(2f)); 62 | 63 | ObjectAnimator alphaAnim = ObjectAnimator.ofFloat(ball, "alpha", 1f, .5f). 64 | setDuration(1000); 65 | AnimatorSet alphaSeq = new AnimatorSet(); 66 | alphaSeq.play(alphaAnim); 67 | 68 | animation = new AnimatorSet(); 69 | ((AnimatorSet) animation).playTogether(yAnim, xAnim); 70 | animation.addListener(this); 71 | } 72 | } 73 | 74 | # 整体逻辑 # 75 | 上面的两段代码为 AnimatorEvents 的核心代码,接下来只需要在点击对应的按钮时调用动画开始、暂停等方法,并调整对应状态的透明度即可。 -------------------------------------------------------------------------------- /2016/2016.10/[ApiDemos] ListFlipper 的简单剖析.md: -------------------------------------------------------------------------------- 1 | # ApiDemos 中的 ListFlipper # 2 | ListFlipper 是 ApiDemos 中的一个简单的动画特效示例,它直接继承了 Activity,使用起来还是比较简单的。他的作用是让一个界面旋转并且切换到另外一个界面上,是一个比较常见的切换动画。 3 | 4 | # XML 布局 # 5 | ApiDemos 中的 ListFlipper 只是一个简单的示例,它的 XML 布局也非常简单。就是一个线性布局, Button 用来控制触发动画效果,两个 ListView ,其中一个 ListView 的 visibility 属性为 gone. 6 | 7 | 11 | 12 |