├── 2016 ├── 2016.10 │ ├── Android-新特性V1.0.doc │ ├── 手势识别Doc.md │ ├── Github学习指南.md │ ├── 什么是学习能力.md │ ├── [ApiDemos] AnimatorEvents 源码分析.md │ ├── Handler removeCallbacks 无效问题.md │ ├── ViewPager smoothScroll 速度控制.md │ ├── aidl 浅析.md │ ├── [Android] 设计模式-策略模式.md │ ├── [ApiDemos] ListFlipper 的简单剖析.md │ ├── [Android]设计模式-工厂模式2.md │ ├── 单例模式.md │ ├── Android 一些重要知识点解析整理.md │ └── Handler原理梳理.md ├── 2016.11 │ ├── 简单面试题.md │ ├── Android studio TraceView和lint工具的使用.md │ ├── IPC机制md.md │ ├── material_animations学习笔记.md │ ├── RxJava.md │ └── 常见内存泄漏场景以及解决办法.md └── 2016.12 │ ├── 沉浸式状态栏.md │ └── 了解注解.md ├── 2017 ├── 2017.2 │ ├── 阿里巴巴java开发手册.pdf │ ├── 中止一个线程的科学方法.md │ ├── 一直会动的跑马灯.md │ ├── VBA笔记.md │ └── 屏幕适配工具类.md ├── 2017.1 │ ├── AtomicInteger简介.md │ └── android studio常用快捷键.md ├── 2017.3 │ ├── EditText与Button联动.md │ ├── 可删除内容的EditText.md │ ├── 屏幕适配.md │ ├── 既可删除内容也可显示隐藏的EditText.md │ ├── Path实战之雷达图.md │ ├── 密码显示与隐藏EditText.md │ ├── Path学习笔记之基本操作.md │ └── 雷达图全部代码.md └── 2017.4 │ └── 连击特效全部代码.md ├── 读书笔记 ├── 《Android 源码设计模式解析与实战》学习笔记 │ ├── [Android]《Android 源码设计模式解析与实战》读书笔记 7.md │ ├── [Android]《Android 源码设计模式解析与实战》读书笔记 2.md │ ├── [Android]《Android 源码设计模式解析与实战》读书笔记 4.md │ ├── [Android]《Android 源码设计模式解析与实战》读书笔记 6.md │ ├── [Android]《Android 源码设计模式解析与实战》读书笔记 5.md │ ├── [Android]《Android 源码设计模式解析与实战》读书笔记 1.md │ └── [Android]《Android 源码设计模式解析与实战》读书笔记 3.md ├── 编程之法(全程kotlin实现) │ └── 旋转字符串.md └── 《Java8实战学习笔记》学习笔记 │ └── 通过行为参数化传递代码.md ├── .gitattributes ├── .gitignore └── README.md /2017/2017.2/阿里巴巴java开发手册.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jutao/AndroidNode/HEAD/2017/2017.2/阿里巴巴java开发手册.pdf -------------------------------------------------------------------------------- /2016/2016.10/Android-新特性V1.0.doc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jutao/AndroidNode/HEAD/2016/2016.10/Android-新特性V1.0.doc -------------------------------------------------------------------------------- /读书笔记/《Android 源码设计模式解析与实战》学习笔记/[Android]《Android 源码设计模式解析与实战》读书笔记 7.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 策略模式 3 | tags: 4 | - 设计模式 5 | - 学习笔记 6 | categories: 7 | - Android 8 | --- 9 | -------------------------------------------------------------------------------- /2016/2016.11/简单面试题.md: -------------------------------------------------------------------------------- 1 | # 泛型方法示例,求两个输入数字的最大值 2 | 3 | public int max(T t1, T t2) { 4 | return t1.intValue()>t2.intValue()?t1.intValue():t2.intValue(); 5 | } 6 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /2016/2016.12/沉浸式状态栏.md: -------------------------------------------------------------------------------- 1 | if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 2 | Window window = getWindow(); 3 | window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS 4 | | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION); 5 | window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN 6 | | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION 7 | | View.SYSTEM_UI_FLAG_LAYOUT_STABLE); 8 | window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); 9 | window.setStatusBarColor(Color.TRANSPARENT); 10 | //window.setNavigationBarColor(Color.TRANSPARENT); 11 | } -------------------------------------------------------------------------------- /2016/2016.11/Android studio TraceView和lint工具的使用.md: -------------------------------------------------------------------------------- 1 | # Lint工具的使用 # 2 | > Android lint工具是Android studio中集成的一个代码提示工具,它主要负责对你的代码进行优化提示,包括xml和Java文件,很强大。 3 | > 编写完代码及时进行lint测试,会让我们的代码变得非常规范而且避免代码冗余。让我们及时发现代码中隐藏的问题。 4 | > 举个例子:我们在代码中建立全局变量,而这个变量实际并不需要全局便利,lint在检测之后会提示我们改成局部变量,这对内存优化是一个很强大的促进手段。 5 | ## 用法 ## 6 | 7 | ![Lint用法](http://i.imgur.com/f5J0kuT.png) 8 | 9 | 点击 Inspect Code 10 | 11 | ![](http://i.imgur.com/5cs3B9v.png) 12 | 13 | 点击 OK 14 | 15 | 检查结果如下: 16 | 17 | ![](http://i.imgur.com/unumZy0.png) 18 | 19 | 非常强大,会给出我们各种修改意见,有的还真能学到不少东西。就比如说我这里的建议:用 merge 标签替换 FrameLayout。([merge标签的使用](http://www.tuicool.com/articles/jyyUV33)) 20 | 21 | 22 | [具体步骤](http://blog.csdn.net/qq_16131393/article/details/51172488 ) 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /2017/2017.1/AtomicInteger简介.md: -------------------------------------------------------------------------------- 1 | # 介绍 # 2 | AtomicInteger,一个提供原子操作的Integer的类。在Java语言中,++i和i++操作并不是线程安全的,在使用的时候,不可避免的会用到synchronized关键字。而AtomicInteger则通过一种线程安全的加减操作接口。 3 | 4 | # 代码演示 # 5 | 6 | public class AtomicIntegerDemo { 7 | public static void main(String[] args) { 8 | AtomicInteger mInteger=new AtomicInteger(1); 9 | //获取当前的值 10 | v(mInteger.get()); 11 | 12 | //取当前的值,并设置新的值 13 | v(mInteger.getAndSet(15)); 14 | 15 | 16 | //获取当前的值,并自增 17 | v(mInteger.getAndIncrement()); 18 | 19 | //获取当前的值,并自减 20 | v(mInteger.getAndDecrement()); 21 | 22 | //获取当前的值,并加上预期的值 23 | v(mInteger.getAndAdd(15)); 24 | 25 | 26 | v(mInteger.get()); 27 | } 28 | 29 | static void v(int i) 30 | { 31 | System.out.println("AtomicInteger : "+i); 32 | } 33 | 34 | } 35 | 36 | # 程序运行结果 # 37 | 38 | ![](http://i.imgur.com/CQHrTpS.png) -------------------------------------------------------------------------------- /2017/2017.2/中止一个线程的科学方法.md: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by JuTao on 2017/2/4. 3 | * 如何中止一个线程 4 | */ 5 | public class ThreadDone { 6 | public static void main(String[] args) throws InterruptedException { 7 | MyRunnable myRunnable=new MyRunnable(); 8 | Thread thread = new Thread(myRunnable); 9 | thread.start(); 10 | Thread.sleep(1000); 11 | //仅仅利用flag并不能使线程立刻中止,当run方法中有耗时操作时会等待执行完成才结束 12 | myRunnable.flag=false; 13 | thread.interrupt(); 14 | } 15 | 16 | private static class MyRunnable implements Runnable { 17 | 18 | //立刻同步到子线程中 19 | private volatile boolean flag = true; 20 | 21 | @Override public void run() { 22 | while (flag&&!Thread.interrupted()) { 23 | System.out.println("running"); 24 | try { 25 | Thread.sleep(8000); 26 | } catch (InterruptedException e) { 27 | //e.printStackTrace(); 28 | //thread.interrupt()执行后会立刻进入catch 29 | return; 30 | } 31 | } 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /2016/2016.11/IPC机制md.md: -------------------------------------------------------------------------------- 1 | # 进程间通讯的方式 2 | Bundle、文件共享、AIDL、Messenger、ContentProvider和Socket。 3 | 4 | # IPC简介 5 | IPC是 Inter-Process Communication 的缩写,含义为进程间通信或者跨进程通信,是指两个进程间进行数据交换的过程。 6 | 7 | ## 线程 8 | 线程是 CPU 调度的最小单元,同时线程是一种有限的系统资源 9 | 10 | ## 进程 11 | 进程一般指一个执行单元、在 PC 和移动设备上指一个程序或者一个应用。一个应用可以包含多个线程。 12 | 13 | 因此进程和线程是包含与被包含的关系。 14 | 15 | * 最简单的情况下,一个进程中可以只有一个线程,即主线程,在 Android 里面主线程也叫 UI 线程里才能操作界面元素。很多时候,一个进程中需要执行大量的耗时任务,如果这些任务放在主线程中去执行就会造成界面无法响应,严重影响用户体验,这种情况在 PC 和 移动系统中都存在,在 Android 中有一个特殊的名字叫 ANR(Application Not Responding),即应用无响应,解决这个问题就要用到线程,把一些耗时的操作放在线程中即可。 16 | 17 | * IPC 不是 Android 中所独有的,任何一个操作系统都需要有相应的 IPC 机制,比如 Windows 上可以通过剪切板、管道和邮槽来进行进程间的通信;Linux 上可以通过命名管道、共享内存、信号量等来进行进程间的通信。 18 | 19 | * 对于 Android 来说,它是一种基于 Linux 内核的移动操作系统,它的进程间通信方式并不能完全继承自 Linux,相反,它有自己的进程间通信方式,在 Android 中最有特色的进程间通信方式就是 Binder 了,通过 Binder可以轻松的实现进程间通信,除了 Binder,通过 Socket 也可以实现任意两个终端之间的通信,一个设备上的两个进程通过 Socket 通信也是可以的。 20 | 21 | ## 多进程的两种情况 22 | * 一个应用因为某些原因自身需要采用多进程模式来实现,原因可能有很多,比如某些模块由于特殊原因需要运行在单独的进程中,又或者加大一个应用可使用内存需要通过多进程来获取多份内存空间。Android 对单个应用所使用的最大内存做了限制,早期一些版本可能是 16MB,不同设备有不同的大小。 23 | * 当前应用需要向其他应用获取数据,由于是两个应用,所以必须采用跨进程的方式来获取所需的数据。 24 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /2017/2017.2/一直会动的跑马灯.md: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * 一直会动的跑马灯 4 | * @author TIAN 5 | */ 6 | public class FocuseTextView extends TextView { 7 | 8 | public FocuseTextView(Context context, AttributeSet attrs, int defStyle) { 9 | super(context, attrs, defStyle); 10 | initView(context); 11 | } 12 | 13 | public FocuseTextView(Context context, AttributeSet attrs) { 14 | super(context, attrs); 15 | initView(context); 16 | } 17 | 18 | public FocuseTextView(Context context) { 19 | super(context); 20 | initView(context); 21 | } 22 | 23 | private void initView(Context context) { 24 | // android:ellipsize="marquee" 25 | // android:focusable="true" 26 | // android:focusableInTouchMode="true" 27 | // android:marqueeRepeatLimit="marquee_forever" 28 | // android:singleLine="true" 29 | setEllipsize(TruncateAt.MARQUEE); 30 | setFocusable(true); 31 | setFocusableInTouchMode(true); 32 | setMarqueeRepeatLimit(-1); 33 | setSingleLine(true); 34 | } 35 | 36 | @Override 37 | @ExportedProperty(category = "focus") 38 | public boolean isFocused() { 39 | return true; 40 | } 41 | 42 | @Override 43 | protected void onFocusChanged(boolean focused, int direction, 44 | Rect previouslyFocusedRect) { 45 | if (focused) 46 | super.onFocusChanged(focused, direction, previouslyFocusedRect); 47 | } 48 | 49 | @Override 50 | public void onWindowFocusChanged(boolean hasWindowFocus) { 51 | if (hasWindowFocus) 52 | super.onWindowFocusChanged(hasWindowFocus); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /2016/2016.10/手势识别Doc.md: -------------------------------------------------------------------------------- 1 | mDetector=new GestureDetector(new GestureDetector.OnGestureListener() { 2 | /** 3 | * 手指按下执行一次 4 | * @param e 5 | */ 6 | @Override public boolean onDown(MotionEvent e) { 7 | return false; 8 | } 9 | 10 | /** 11 | * 短暂的按下操作 12 | * @param e 13 | */ 14 | @Override public void onShowPress(MotionEvent e) { 15 | 16 | } 17 | 18 | /** 19 | * 单击操作 20 | * @param motionEvent 21 | * @return 22 | */ 23 | @Override public boolean onSingleTapUp(MotionEvent motionEvent) { 24 | return false; 25 | } 26 | 27 | /** 28 | * 滚动监听 29 | * @param e1 手指按下事件 30 | * @param e2 当前的move事件 31 | * @param distanceX 上一次move事件的x - 当前move事件的x 32 | * @param distanceY 上一次move事件的y - 当前move事件的y 33 | * @return 34 | */ 35 | @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, 36 | float distanceY) { 37 | return false; 38 | } 39 | 40 | /** 41 | * 长按 42 | * @param e 43 | */ 44 | @Override public void onLongPress(MotionEvent e) { 45 | 46 | } 47 | 48 | /** 49 | * 手指离开屏幕,控件惯性效果时 50 | * @param e1 手指按下的事件 51 | * @param e2 当前的手指move事件 52 | * @param velocityX 当手指松开的瞬间,x轴的移动速率 像素/秒 53 | * @param velocityY 当手指松开的瞬间,y轴的移动速率 像素/秒 54 | * @return 55 | */ 56 | @Override 57 | public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { 58 | return false; 59 | } 60 | }); -------------------------------------------------------------------------------- /2017/2017.3/EditText与Button联动.md: -------------------------------------------------------------------------------- 1 | /** 2 | * *************************************** 3 | * author:琚涛 4 | * time:2017/3/1 5 | * description:用来监听一个多个EditText是否有数据以改变指定Button状态 6 | * **************************************** 7 | */ 8 | 9 | public class EditCheckManager extends SimpleTextWatcher { 10 | 11 | private EditText[] mEditTexts; 12 | private Button mBtn; 13 | 14 | private EditCheckManager() { 15 | } 16 | 17 | private EditCheckManager(Button btn, EditText... editTexts) { 18 | mEditTexts = editTexts; 19 | mBtn = btn; 20 | } 21 | 22 | public static EditCheckManager create(Button btn, EditText... editTexts) { 23 | 24 | return new EditCheckManager(btn, editTexts); 25 | } 26 | 27 | public void listener() { 28 | if (mBtn == null || mEditTexts == null) { 29 | return; 30 | } 31 | 32 | for (EditText editText : mEditTexts) { 33 | editText.addTextChangedListener(this); 34 | } 35 | 36 | mBtn.setEnabled(checkAllEdit()); 37 | } 38 | 39 | public void removeListener(){ 40 | for (EditText editText : mEditTexts) { 41 | editText.removeTextChangedListener(this); 42 | } 43 | } 44 | @Override 45 | public void onTextChanged(CharSequence s, int start, int before, int count) { 46 | mBtn.setEnabled(checkAllEdit()); 47 | } 48 | 49 | private boolean checkAllEdit() { 50 | for (EditText editText : mEditTexts) { 51 | if (TextUtils.isEmpty(editText.getText() + "")) { 52 | return false; 53 | } 54 | } 55 | return true; 56 | } 57 | } 58 | 59 | 60 | 调用方式 61 | 62 | EditCheckManager.create(button,edittext1,edittext2,edittext3,....).listener(); -------------------------------------------------------------------------------- /2017/2017.2/VBA笔记.md: -------------------------------------------------------------------------------- 1 | #if 语句 2 | 3 | ## if—then—else 语句# 4 | 这个语句相当于中文里面的“如果......那么......否则......”句式。 5 | 6 | 解释:如果逻辑表达式的结果为 true,则执行 Then 下的语句,如果逻辑表达式的结果为 False, 则执行 Else 下的语句。 7 | 8 | If [a1] = "" Then 9 | MsgBox "A1单元格没有输入任何内容!" 10 | Else 11 | MsgBox "A1单元格已经输入了内容!" 12 | End If 13 | 14 | ### 运行效果如下 15 | 16 | 单元格没有内容: 17 | 18 | ![单元格没有内容](http://i.imgur.com/UANmhrJ.png) 19 | 20 | 单元格有内容: 21 | 22 | ![单元格有内容](http://i.imgur.com/qRcxx1J.png) 23 | 24 | ## if—then—elseif语句 25 | 要判断A1单元格的数是否能被2、3、5其中之一整除,设计程序: 26 | 27 | If [a1] = "" Then 28 | MsgBox "A1单元格没有输入任何内容!" 29 | ElseIf [a1] Mod 2 = 0 Then 30 | MsgBox "A1单元格能被2整除!" 31 | ElseIf [a1] Mod 3 = 0 Then 32 | MsgBox "A1单元格能被3整除!" 33 | ElseIf [a1] Mod 5 = 0 Then 34 | MsgBox "A1单元格能被5整除!" 35 | End If 36 | 若 A1 输入 16,运行结果如下: 37 | 38 | ![值16运行结果](http://i.imgur.com/ggGY5Sg.png) 39 | 40 | # Go to 语句 # 41 | Go to 语句是将程序转到指定的标签的语句位置,然后继续往下执行。Go to 语句通常用来作错误处理。 42 | 43 | 下面我们用 Go to 语句来做一个1——100自然数的和: 44 | 45 | Dim lSum As Long,i As Long 46 | i=1 47 | x: '为go to 语句设置的标签,必须以英文状态下的冒号结尾 48 | lSum=lSum+i 49 | i=i+1 50 | If i<=100 51 | Then Goto x '如果i<=100,则程序跳到标签X处 52 | MsgBox "1到100的自然数和为"&lSum 53 | 54 | 运行结果: 55 | 56 | ![1到100相加结果](http://i.imgur.com/ERGBim2.png) 57 | 58 | # 快捷键 # 59 | ALT+F11 :打开Visual Basic编辑器 60 | 61 | CRTL+G :打开立即窗口 62 | 63 | 选中相应代码,点击 F1 查看帮助 64 | 65 | #常用基础 66 | 67 | ##选择多个单元格 68 | 69 | 给 A1 到 D10 的所有单元格赋值: 70 | 71 | Range("A1:D10")=66 72 | 73 | 或者直接: 74 | 75 | [A1:D10]=99 76 | 77 | ##选择单个单元格 78 | 给第一行第十列的单元格赋值: 79 | 80 | cells(1,10)="习大大万岁" 81 | 82 | ##工作簿的操作 83 | 输出当前表格第一个工作簿的名称: 84 | 85 | Msgbox Worksheets(1).name 86 | 87 | 输出当前表格名为 Sheet1 的工作簿的名称 88 | 89 | Msgbox Worksheets("Sheet1").name 90 | ##选中单元格 91 | [D1:F8].Select 92 | 93 | ##一维数组定义 94 | Dim myarr(5) As Integer 95 | 96 | ##二维数组定义 97 | Dim myarr(1 to 5,1 to 10) As Integer 98 | -------------------------------------------------------------------------------- /2016/2016.11/material_animations学习笔记.md: -------------------------------------------------------------------------------- 1 | # 前言 # 2 | 关于转场动画之类的动画效果平时了解的不多,最近在 Github 上 看到了一个开源项目:[Material-Animations](https://github.com/lgvalle/Material-Animations "Material-Animations") 做的非常好,忍不住照着写了一遍,项目不大,但是完全弄懂需要花不少时间,这篇笔记记录了一些我从该项目中学到的知识点。 3 | 4 | # 概述 # 5 | Android Transition Framework 主要有三种效果: 6 | 7 | 1.不同 Activity 之间切换时,Activityc 的内容 (contentView) 转场动画。 8 | 9 | 2.不同 Activity 之间切换时,如果使用了 Shared Element 动画,也可以使用 Transition FrameWork 来实现不同的过渡动画效果。 10 | 11 | 3.同一个 Activity 内 View 变化的过渡动画 (Scene)。 12 | 13 | 下面我们分模块来讨论这几种效果。 14 | 15 | # Activity 转场动画 # 16 | 首先要进行 ThemeStyle 设置: 17 | 18 | 26 | 27 | # DataBinding # 28 | Material-Animations 用到了 DataBinding,基本配置方式如下: 29 | > 环境搭建: 30 | > 31 | > Android 的 Gradle 插件版本不低于 1.5.0-alpha1: 32 | > 33 | > classpath 'com.android.tools.build:gradle:1.5.0' 34 | > 35 | > 然后修改对应模块(Module)的 build.grade: 36 | > 37 | > 38 | > android { 39 | > .... 40 | > dataBinding { 41 | > enabled = true 42 | > } 43 | > } 44 | > 注意:Android stuido 的版本一定要大于1.3,而且Android Studio目前对binding对象没有自动代码提示,只会在编译时进行检查。 45 | > 46 | > 就是这么简单,但是1.3及以前的版本,对于环境的搭建,可能就会麻烦一点(没事1.3的环境搭建方法,网上多得是)。 47 | 48 | 49 | # RecyclerView 优化技巧 # 50 | ## 使用 setHasFixedSize ## 51 | //如果item的内容不改变view布局大小,那使用这个设置可以提高RecyclerView的效率 52 | //这里我们的item是不会改变的,所以用这个属性来优化自身效率 53 | recyclerView.setHasFixedSize(true); 54 | 55 | # 零散的知识点 # 56 | ## Gravity.START 和 Gravity.LEFT 的区别 ## 57 | left/right是代表一种绝对的对齐 58 | 59 | start/end表示基于阅读顺序的对齐 60 | 61 | 目前存在的主要阅读方式: 从左向右(LTR)和从右向左(RTL); 62 | 63 | 当使用left/right的时候,无论是LTR还是RTL,总是左/右对齐的;而使用start/end,在LTR中是左/右对齐,而在RTL中则是右/左对齐。 64 | 注: left/right属于绝对对齐,而start/end会根据不同国家习惯改变。如阅读顺序是从左到右(LTR)的国家,start在左边,在阅读顺序是从右到左(RTL)的国家,start在右边。 65 | 66 | ## Toolbar 去原生 title ## 67 | //不显示自带title 68 | if (getSupportActionBar() != null) 69 | getSupportActionBar().setDisplayShowTitleEnabled(false); -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /读书笔记/编程之法(全程kotlin实现)/旋转字符串.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 旋转字符串 3 | tags: 4 | - 基础练习 5 | categories: 6 | - 算法练习 7 | - kotlin 8 | --- 9 | 10 | # 题目描述 11 | 给定一个字符串,要求把字符串前面的若干个字符移动到字符串的尾部,如把字符串“abcdef”前面的2个字符'a'和'b'移动到字符串的尾部,使得原字符串变成字符串“cdefab”。请写一个函数完成此功能,要求对长度为n的字符串操作的时间复杂度为 O(n),空间复杂度为 O(1)。 12 | # 分析与解法 13 | ## 解法一:暴力移位法 14 | 初看此题,可能最先想到的方法是按照题目所要求的,把需要移动的字符一个一个地移动到字符串的尾部,如此我们可以实现一个函数LeftShiftOne(char[] chars, int n) ,以完成移动一个字符到字符串尾部的功能,代码如下所示: 15 | 16 | ```kotlin 17 | fun leftRotateString(chars: CharArray, n: Int, m: Int) { 18 | var m = m 19 | while (m-- != 0) { 20 | leftShiftOne(chars, n) 21 | } 22 | } 23 | 24 | fun leftShiftOne(chars: CharArray, n: Int) { 25 | val t = chars[0] 26 | for (i in 1..n - 1) { 27 | chars[i - 1] = chars[i] 28 | } 29 | chars[n - 1] = t 30 | } 31 | ``` 32 | 33 | 下面,我们来分析一下这种方法的时间复杂度和空间复杂度。 34 | 35 | 针对长度为n的字符串来说,假设需要移动m个字符到字符串的尾部,那么总共需要 m*n 次操作,同时设立一个变量保存第一个字符,如此,时间复杂度为O(m * n),空间复杂度为O(1),空间复杂度符合题目要求,但时间复杂度不符合,所以,我们得需要寻找其他更好的办法来降低时间复杂度。 36 | 37 | ## 解法二:三步反转法 38 | 对于这个问题,换一个角度思考一下。 39 | 40 | 将一个字符串分成X和Y两个部分,在每部分字符串上定义反转操作,如X^T,即把X的所有字符反转(如,X="abc",那么X^T="cba"),那么就得到下面的结论:(X^TY^T)^T=YX,显然就解决了字符串的反转问题。 41 | 42 | 例如,字符串 abcdef ,若要让def翻转到abc的前头,只要按照下述3个步骤操作即可: 43 | 44 | 首先将原字符串分为两个部分,即X:abc,Y:def; 45 | 将X反转,X->X^T,即得:abc->cba;将Y反转,Y->Y^T,即得:def->fed。 46 | 反转上述步骤得到的结果字符串X^TY^T,即反转字符串cbafed的两部分(cba和fed)给予反转,cbafed得到defabc,形式化表示为(X^TY^T)^T=YX,这就实现了整个反转。 47 | 如下图所示: 48 | 49 | ![image.png](http://upload-images.jianshu.io/upload_images/3054656-aa22aec968215599.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 50 | 51 | 代码则可以这么写: 52 | ```kotlin 53 | /** 54 | * 将长度为n的字符串s的前m个字符移动到字符串尾部 55 | */ 56 | fun leftRotateString(chars: CharArray, n: Int, m: Int) { 57 | var m = m 58 | // 若要左移动大于n位,那么和%n 是等价的 59 | m %= n 60 | reverseString(chars, 0, m - 1) 61 | reverseString(chars, m, n - 1) 62 | reverseString(chars, 0, n - 1) 63 | } 64 | 65 | /** 66 | * 翻转字符数组from-to之间的字符串 67 | */ 68 | fun reverseString(chars: CharArray, from: Int, to: Int) { 69 | var from = from 70 | var to = to 71 | while (from < to) { 72 | val c = chars[from] 73 | chars[from++] = chars[to] 74 | chars[to--] = c 75 | } 76 | } 77 | ``` 78 | 这就是把字符串分为两个部分,先各自反转再整体反转的方法,时间复杂度为O(n),空间复杂度为O(1),达到了题目的要求。 79 | 80 | # 举一反三 81 | 1、链表翻转。给出一个链表和一个数k,比如,链表为1→2→3→4→5→6,k=2,则翻转后2→1→6→5→4→3,若k=3,翻转后3→2→1→6→5→4,若k=4,翻转后4→3→2→1→6→5,用程序实现。 82 | 83 | 2、编写程序,在原字符串中把字符串尾部的m个字符移动到字符串的头部,要求:长度为n的字符串操作时间复杂度为O(n),空间复杂度为O(1)。 例如,原字符串为”Ilovebaofeng”,m=7,输出结果为:”baofengIlove”。 84 | 85 | 3、单词翻转。输入一个英文句子,翻转句子中单词的顺序,但单词内字符的顺序不变,句子中单词以空格符隔开。为简单起见,标点符号和普通字母一样处理。例如,输入“I am a student.”,则输出“student. a am I”。 86 | -------------------------------------------------------------------------------- /读书笔记/《Android 源码设计模式解析与实战》学习笔记/[Android]《Android 源码设计模式解析与实战》读书笔记 2.md: -------------------------------------------------------------------------------- 1 | # 里氏替换原则 (LSP)# 2 | 定义:所有引用父类的地方,必须能使用子类的对象。简单地说就是将父类替换为他的子类是不会出现问题,反之,未必可以。那么里氏替换原则就是依赖于面向对象语言的继承与多态。核心原理是抽象。 3 | 4 | 这里列举一下继承的优缺点: 5 | 6 | 优点: 7 | 8 | (1)代码重用,减少创建类的成本,每个子类都拥有父类的方法与属性。 9 | 10 | (2)子类与父类基本相似,但与父类又有所区别。 11 | 12 | (3)提高代码的可扩展性。 13 | 14 | 缺点: 15 | 16 | (1)继承是侵入性的,只要继承就必须拥有父类所有的属性与方法。 17 | 18 | (2)可能造成子类代码冗余、灵活性降低,因为子类必须拥有父类的属性和方法。 19 | 20 | 开闭原则和里氏替换原则是生死相依的、不离不弃的。他们都强调了抽象这一重要的特性。 21 | 22 | ##示例代码 ## 23 | 24 | public class Window { 25 | public void show(View child){ 26 | child.draw(); 27 | } 28 | } 29 | 30 | public abstract class View { 31 | public abstract void draw(); 32 | public void measure(int width,int height){ 33 | //测量视图大小 34 | } 35 | } 36 | 37 | public class Button extends View { 38 | @Override 39 | public void draw() { 40 | // 绘制按钮 41 | } 42 | } 43 | 44 | public class TextView extends View { 45 | @Override 46 | public void draw() { 47 | // 绘制文本 48 | } 49 | } 50 | 51 | 上述示例中,Window依赖于 View,而 View 定义了一个视图抽象,measure 是各个子类共享的方法,子类通过覆写 View 的 draw 方法实现具有各自特色的功能,在这里这个功能就是绘制自身的内容。任何继承自 View 类的子类都可以设置给 show 方法,就是所说的里氏替换。通过里氏替换,就可以自定义各式各样、千变万化的 View,然后传递给 Window,Window 负责组织 View 并将 View 显示到屏幕上。 52 | 53 | # 依赖倒置原则(DIP) # 54 | 定义:指代一种特定的解耦方式,使得高层次的模块不依赖于低层次的模块的实现细节的目的。他有一下几个关键点: 55 | 56 | (1)高层模块不依赖于低层模块,应该都依赖其抽象。 57 | 58 | (2)抽象不依赖细节。 59 | 60 | (3)细节应依赖抽象。 61 | 62 | 解释:在Java中,抽象就是指接口或者抽象类,两者都是不能直接被实例化的;细节就是实现类,实现接口或者继承抽象类而产生的就是细节,也就是可以加上一个关键字new产生的对象。高层模块就是调用端,底层模块就是具体实现类。依赖倒置原则在Java中的表现就是:模块间通过抽象发生,实现类之间不发生直接依赖关系,其依赖关系是通过接口或者抽象类产生的。如果类与类直接依赖细节,那么就会直接耦合,那么当修改时,就会同时修改依赖者代码,这样限制了可扩展性。 63 | 64 | # 接口隔离原则(ISP) # 65 | 定义:类间的依赖关系应该建立在最小的接口上,将庞大、臃肿的接口拆分成更小的、更具体的接口。目的是系统的解耦,从而更容易重构、更改和重新部署。 66 | 示例: 67 | 68 | finally { 69 | if (fileOutputStream != null) { 70 | try { 71 | fileOutputStream.close(); 72 | } catch (IOException e) { 73 | e.printStackTrace(); 74 | } 75 | } 76 | } 77 | 这段代码我们经常见到,他的可读性非常的差。我们知道 Java 中有一个 Closeable 接口标识了可关闭对象,它只有一个 close 方法,有100多个类实现了这个接口,这意味着在关闭这100多个类的对象时我们都要写出上面那一段难看的代码来关闭它,这你能忍? 78 | 书中给出的解决方案如下: 79 | 既然都实现了Closeable接口,那我们只需要建一个方法来统一关闭这些对象即可,以下为工具类代码: 80 | public class Closeutils { 81 | private Closeutils() { 82 | } 83 | 84 | /** 85 | * 关闭Closeable对象 86 | */ 87 | public static void closeQuietly(Closeable closeable) { 88 | if (null != closeable) { 89 | try { 90 | closeable.close(); 91 | } catch (IOException e) { 92 | e.printStackTrace(); 93 | } 94 | } 95 | } 96 | } 97 | 此时关闭就只需要这样: 98 | 99 | finally { 100 | Closeutils.closeQuietly(fileOutputStream); 101 | } 102 | 代码非常简洁,而且只要继承了 Closeable 接口都可以用此方法关闭,我们只需要知道这个对象是可关闭的,其他的一概不关心,也就是接口隔离原则。 103 | 104 | # 迪米特原则(LOD) # 105 | 106 | 定义:一个类应该对自己需要耦合或者调用的类知道的最少,类的内部如何实现与调用者或者依赖者没有关系,调用者或依赖者只需知道他需要的方法,其他可以一概不管。这样使得系统具有更低的耦合与更好的可扩展性。 -------------------------------------------------------------------------------- /2016/2016.10/什么是学习能力.md: -------------------------------------------------------------------------------- 1 | # 1、学习能力是什么? # 2 | 3 | 大家觉得,学习能力不就是看书更快、更好的理解记忆、考试拿高分的能力吗? 4 | 5 | 到了职场,还固步自封在应试教育阶段,就有点呵呵了。 6 | 7 | 虽然面试官也会问,最近有看什么书啊,能给我们讲讲里面内容吗,但面试官绝对不只是想了解,你是不是爱看书、能不能背过书。 8 | 9 | 那到底什么是学习能力呢? 10 | 11 | 关于这个能力,我们用HayGroup素质理论来解释,就是专家精神(Expertise),或称专门知识,即一个人是否能拓展并习得使其业务增值的新兴专门知识。 12 | 13 | 所以,学习能力不仅是一种能力配置,我们反而更关注其深层次的对认知需求和成为领域专家的动机。 14 | 15 | 学习能力或称专家精神的概括性行为表现:紧跟本专业内新知识、新技术动态,并运用到实际问题解决,能在本领域的研究项目中发挥主要作用,乐于分享知识经验。 16 | 17 | 从行为表象上,也分了5级用于观察和测评: 18 | 19 | 1.对自己的专门知识领域兴趣浓厚:表现出对自己的技术专业里的工具、方法或技术上的新思路的好奇。 20 | 21 | 2.跟上自己专业知识领域的潮流:通过阅读、与同行交谈、听课或实验新思路,主动地跟上自己专业内工具、方法或技术新思潮的进展。 22 | 23 | 3.跟上业务变化的潮流:跟上对业务有潜在影响的新工具、新方法、新技术或新思路的变化潮流。 24 | 25 | 26 | 4.把知识和拥护当前需要联系起来:逐步加深对重大业务问题及其含义的理解,表现出深刻理解可能影响业务的解决方案,发现当前或新兴的技术对业务未来需求的适应性。 27 | 28 | 5.拓宽学习面:深钻各个专业的知识,能够预见到其对未来的实质性影响;从技术和业务的角度考虑新兴技术的应用将会产生的影响。 29 | 30 | 31 | # 2、学习能力如何考察 # 32 | 33 | 知道什么是学习能力了,我们再看看如何考察候选人的学习能力。 34 | 35 | 熟悉素质理论亲们,都知道素质理论一个经典概念就是冰山模型。冰山模型是素质理论构念的基础组成,每一个素质,都能用冰山模型,从内在的核心动机层面,分层剖析到外显的知识技能层面。 36 | 37 | ## 动机(motives) ## 38 | 从动机上,学习能力强的人,必定是一个认知需求水平比较高的个体。你可以问他几个常识逻辑,比如知不知道为什么人被毒蛇咬了会中毒?不知道,OK,那你给我讲讲你知道的但是我可能不知道的某个专业知识?一个认知需求高的个体,肯定是格物致知的人,必定在某些领域掌握更加精深或偏向原理性的基础知识,所以也许他的方向很偏,但总能分享一些这样的知识点,而且这些知识点不应该是松散的,而应该是聚焦莫一或某几个领域的。 39 | 40 | ## 个性特质(traits) ## 41 | 从个性特质上,学习能力强的个体和一般的个体也是有差异的,比如爱钻研、爱看书。学习本身对他来说会比较容易,但更应看到的是,学习过程本身对这类人也是一种享受。学习能力差的人,是模仿不出来学习能力强以及非常享受学习的样子的,就好比一个恐高的人不可能顺利完成高空钢丝行走一样,你可以问问候选人,有没有系统学习一门知识或能力,学习是一种什么样的体验。如果怎么都回答不上来(没有内容可说),或回答内容磕磕巴巴(排除口吃)⋯⋯就不用我说啥了吧 42 | 43 | ## 自我形象(self-image) ## 44 | 学习能力好的人,绝对不是学过什么就扔一边的人, 他们优秀的学习动机和能力能使得他们成为一个领域的专家(这个专家的圈子不一定很大,也许只是一个部门内),所以如果这个人说我是个学习能力很强的人,那你问问他,你在哪方面比较牛。他说,哎呀,抱歉,我觉得自己没有什么特别牛的地方啦~ 滚粗~此处排除候选人自谦的因素,但是过于自谦的人,咱就不要啦 45 | 46 | ## 社会角色(social role, values) ## 47 | 一个学习能力强的人,是不会自己憋在肚子里“财不外露”的,而且大部分学习能力强的人遇到同一领域的共同爱好者,会倾囊相倒。因此,知识的分享,也是学习能力强的人的一种表现,别给我说你朋友学习能力特强但是就一个人憋着生怕别人知道,传内不传外传男不传女,整得跟辟邪剑谱九阳真经似的,我还真没见过这种人。 48 | 49 | ## 经验(experience) ## 50 | 你曾经成功迅速学习过哪门知识,掌握过什么能力与技巧?分享一下你之前所需的知识与技能吧(这块跟上面分享心得是有差异的)。这块实际上你就不用听内容,你就看他表情和语气语调以及呼吸频率等非语言线索,真正学习能力强并通过学习对工作有过帮助的人,会自如地侃侃而谈,看他讲话的感觉,肯定特享受。换句话说,这时候他的自我效能感爆棚,感觉这一亩三分地,自己就是真正的主人。 51 | 52 | ## 知识(knowledge) ## 53 | 你知道你所从事职业的新兴思想潮流吗?你知道最新崛起的几位大家是谁吗?你能说说你最喜欢的职业领域几位大牛吗,能比较一下他们的成就和思想吗?作为面试官,那怕你面试的是个土木工程师而你对土木一窍不通也没关系,如果他在编,你就让他编,放心吧,一个人讲的时候的流畅程度和自信程度是装不出来的,如果没有点实在货,三句话就露怯。 54 | 55 | ## 技能(skills) ## 56 | 知易行难,知行合一。如果一个人只会看书本、掉书袋,那只能是书呆子,只有把新知识应用于工作实际,解决实际工作中的具体难题,才能算上超强而实用的学习能力。所以,你就问问他,自己学到了哪些知识,应用到工作当中去,并用STAR面试技巧,是专家还是砖家,3分钟原形毕露。 57 | 58 | #看完问问自己 # 59 | 60 | * 讲讲你知道的但是我可能不知道的某个专业知识 61 | 62 | * 有没有系统学习一门知识或能力,学习是一种什么样的体验 63 | 64 | * 你在哪方面比较牛 65 | 66 | * 你曾经成功迅速学习过哪门知识,掌握过什么能力与技巧?分享一下你之前所需的知识与技能 67 | 68 | * 接受新事物的能力。 69 | 70 | #面试被问的问题# 71 | 72 | * 我们不要培训出来的学生!你觉得为什么? 73 | 74 | * 外包 与 做产品的区别? 75 | 76 | * 展现 个人能力 的能力,为什么别人能看出来你不够聪明? 77 | 78 | * 为什么别人一眼就能看出来你不善于某一方面? 79 | 80 | * 别人为什么觉得你不能坚持? 81 | 82 | * 不聪明还乱选? 83 | 84 | * 内向是性格并不能作为不会说话的理由,逻辑能力,提前准备,逻辑思维能力强,不是自己说出来的,是别人看出来的,明白? 85 | 86 | * 想法多并不能作为优点,患得患失每个人都有,更何况像现在的年轻人? 87 | 88 | * 学习能力强。你连好大学都考不下,哪里强了?比起985,211你的学习能力强在哪里? 89 | 90 | * 为什么大学之前没有努力? 91 | 92 | * 上了大学之后你发现了自己的兴趣,然后开始努力了,是不是可以理解为你没有主见,当你有了一个新的兴趣之后,就为之努力? 93 | -------------------------------------------------------------------------------- /2017/2017.1/android studio常用快捷键.md: -------------------------------------------------------------------------------- 1 | # 最重要的快捷键 2 | 1. ctrl+shift+A:万能命令行 3 | 2. shift两次:查看资源文件 4 | 3. ctrl+alt+s 5 | 6 | # 新建工程第一步操作 7 | 1. module设置把空包分层去掉,compact empty middle package 8 | 2. 设置当前的工程是utf-8,设置的Editor-->File Encodings-->全部改成utf-8, 9 | # 注释 10 | 1. ctrl+/:单行注释 11 | 2. ctrl+shift+/:多行注释 12 | 13 | 14 | 15 | # 光标操作 16 | 1. ctrl+alt+enter:向上插入 17 | 2. shift+enter:向下插入 18 | 3. end:光标 19 | # 操作代码 20 | 1. ctrl+d:复制粘贴一行 21 | 2. ctrl+y:删除一行 22 | # shift+F6:重命名 # 23 | 4. ctrl+alt+shift+c:类全名复制 24 | 5. ctrl+O:复写代码 25 | 26 | # 格式代码及其他功能 27 | 1. ctrl+alt+L:格式代码 28 | 2. 在代码中使用alt+insert:Generate,可以get/set等操作 29 | 3. ctrl+alt+T:添加try/catch 30 | 4. ctrl+alt+M:抽取代码 31 | 5. ctrl+alt+F:变量抽取全局变量 32 | 1. 还需要设置前缀:Editor-->code style-->java-->code Genertion-->设置Field的前缘为m添加 33 | 6. ctrl+alt+v:方法体内值抽取成变量 34 | 7. ctrl+alt+p:把方法体内的变量抽取成方法内的参数 35 | 8. 保存成模板:ctrl+shift+L,这个是自定义的(save as live Template) 36 | 9. 选中内容:tab进行退格 37 | 10. shift+tab:反向退格 38 | 11. alt+shift+上下键:选中代码移动 39 | 12. ctrl+shift+上下键:可以移动当前方法体,如果移动一行代码只能在代码体内移动 40 | 13. ctrl+shift+U:代码大小写 41 | 14. ctrl+alt+c:抽取成全局静态常量 42 | 15. ctrl+shift+enter:补全代码(一行尾添加分号,如果是if等添加括号) 43 | 44 | # 进入代码 45 | 1. ctrl+鼠标:进入代码 46 | 2. ctrl+B:进入代码 47 | 3. ctrl+alt+home:查看布局与对应的类 48 | # 图形操作 49 | 1. xml显示布局时双击添加id 50 | 2. 在xml布局时使用preview视图 51 | 3. 在代码编辑时使用: 52 | 1. ctrl+shift+a:输入split选择分屏 53 | 54 | 4. 在xml布局中使用 : alt+shift+左右:切换布局视图 55 | 5. ctrl+shift+12:最大化窗口 56 | # 替换查找 57 | 1. ctrl+r:替换 58 | 2. ctrl+F:查找 59 | 3. ctrl+shift+F:全局查找 60 | 4. ctrl+shift+R:全局替换 61 | 5. ctrl+shift+i:快捷查看方法实现的内容 62 | 6. ctrl+p:查看参数 63 | 7. ctrl+Q:查看文档描述 64 | 8. shift+F1:查看api文档 65 | 8. ctrl+F12:查看类的方法 66 | 9. ctrl+H:查看类的继承关系 67 | 10. 查看变量的赋值情况: 68 | 1. shift+ctrl+a:输入analyze data flow to Here 69 | 11. ctrl+alt+H:查看方法在那里被调用了 70 | 12. ctrl+{}:可以定位方法体的括号 71 | 13. F3:查看选中的内容 72 | 14. shift+F3:反向查看内容 73 | 15. ctrl+alt+B:查询那些类实现了光标所在的接口 74 | 16. ctrl+U:查看父类 75 | 17. ctrl+E:最近编辑的文件列表 76 | 18. ctrl+alt+home:查看布局与对应的类 77 | 19. ctrl+alt+H:查看当前方法在那里进行调用 78 | # 运行编译 79 | 1. ctrl+F9:构建 80 | 2. shift+F10:运行 81 | 82 | # 工程目录操作 83 | 1. 新建文件及工程:选中要创建目录使用alt+insert 84 | 2. ctrl+shift+a:输入show in explorer-->打开相应目录 85 | 3. ctrl+alt+s:打开软件设置 86 | 4. ctrl+alt+shift+s:打开module设置 87 | 5. alt+1:当前目录区 88 | 6. alt+7:当前类的方法列表查看 89 | 7. ctrl+tab:切换目录及视图 90 | 8. alt+shift+c:查看工程最近更改的地方 91 | 9. ctrl+J:livetemp模板查看 92 | # 代码快捷操作 93 | 1. 没有操作完成操作可以先写todo进行,就可以在todo的窗口进行查看 94 | 2. F11定义书签 95 | 3. shift+F11:查看书签 96 | 4. ctrl+J:快捷调出模板 97 | 5. alt+点击断点:禁用断点 98 | 6. 调试状态下按下:alt查看变量能审查表达式的值 99 | 100 | # 组合快捷键 101 | 1. F2:定位错误 102 | 2. alt+enter:修正错误 103 | 104 | 3. alt+鼠标:进入列编辑模式 105 | 4. ctrl+w:选中单词 106 | 5. 或其他组合操作 107 | 108 | # 编辑的位置 109 | ctrl+alt+左右键:这个是定位到编辑的位置 110 | # ctrl+H 查看继承关系# 111 | 112 | # ctrl+shift+R 全局搜索替换# 113 | # ctrl+shift+F 全局搜索# 114 | -------------------------------------------------------------------------------- /读书笔记/《Android 源码设计模式解析与实战》学习笔记/[Android]《Android 源码设计模式解析与实战》读书笔记 4.md: -------------------------------------------------------------------------------- 1 | # 简介 # 2 | 3 | 这周继续写《Android源码设计模式解析与实战》读书笔记。本书的第三章介绍了 Builder(建造者)模式的使用方式以及在 Android 源码中的应用。 4 | 5 | # Builder 模式介绍 # 6 | 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。对于复杂的对象,为了在构建过程中对外部隐藏实现细节,可以使用 Builder 模式将部件和组件过程分离,使得构建过程和部件都可以自由扩展,两者之间的耦合度也降到最低。 7 | 8 | # 使用场景 # 9 | 1.相同的方法,不同的执行顺序,产生不同的事件结果时。 10 | 11 | 2.多个部件或零件,都可以装配到一个对象中,但是产生的运行结果又不相同时。 12 | 13 | 3.产品类非常复杂,或者产品类中的调用顺序不同产生了不同的作用,这个使用建造者模式非常适合。 14 | 15 | 4.当初始化一个对象特别复杂时,如参数多,且很多参数有默认值。 16 | 17 | # Builder 使用方法 # 18 | 我仿照 Android 源码中的 AlertDialog.Builder ,将书中的源码作了修改,更加符合日常使用习惯。代码如下: 19 | 20 | public class Computer { 21 | private ComputerConfig mConfig; 22 | 23 | private Computer() { 24 | } 25 | 26 | public static Computer getInstance() { 27 | return ComputerHolder.sInstance; 28 | } 29 | 30 | public void init(ComputerConfig config) { 31 | this.mConfig = config; 32 | } 33 | 34 | @Override 35 | public String toString() { 36 | return "主机:" + mConfig.mBoard + " 显示器:" + mConfig.mDisplay + " 操作系统:" 37 | + mConfig.mOS; 38 | } 39 | 40 | private static class ComputerHolder { 41 | private final static Computer sInstance = new Computer(); 42 | } 43 | } 44 | 45 | 46 | Computer 类的属性全部放在 ComputerConfig 类中 47 | 48 | public class ComputerConfig { 49 | String mBoard; 50 | String mDisplay; 51 | String mOS; 52 | 53 | private ComputerConfig(){} 54 | 55 | public static class Builder{ 56 | String mBoard; 57 | String mDisplay; 58 | String mOS; 59 | 60 | public Builder setmBoard(String mBoard) { 61 | this.mBoard = mBoard; 62 | return this; 63 | } 64 | 65 | public Builder setmDisplay(String mDisplay) { 66 | this.mDisplay = mDisplay; 67 | return this; 68 | } 69 | 70 | public Builder setmOS(String mOS) { 71 | this.mOS = mOS; 72 | return this; 73 | } 74 | 75 | private void apply(ComputerConfig config){ 76 | config.mBoard=this.mBoard; 77 | config.mDisplay=this.mDisplay; 78 | config.mOS=this.mOS; 79 | } 80 | 81 | public ComputerConfig create(){ 82 | ComputerConfig config=new ComputerConfig(); 83 | this.apply(config); 84 | return config; 85 | } 86 | } 87 | } 88 | 89 | 90 | 91 | public class Demo { 92 | public static void main(String[] args) { 93 | ComputerConfig config = new ComputerConfig.Builder().setmBoard("机甲战神") 94 | .setmDisplay("清华同方").setmOS("windows 10").create(); 95 | Computer computer = Computer.getInstance(); 96 | computer.init(config); 97 | System.out.println(computer.toString()); 98 | } 99 | } 100 | 101 | 运行结果: 102 | 103 | ![](http://i.imgur.com/NEb3D6i.png) 104 | 105 | 通过将 ComputerConfig 的构造函数私有化,用户只有通过 Builder 对象的 create 方法才能获取 ComputerConfig 实例并设置属性,这就是构建和表示相分离。Android 中最经典的 Builder 模式应用也就是 AlertDialog.Builder 了,我写的这个例子也是参照了源码中的写法,因此在本文中就不列举源码了。 106 | 107 | 108 | # 总结 # 109 | 优点: 110 | 111 | 1.良好的封装性,使用建造者模式可以使客户端不必知道产品内部组成细节。 112 | 113 | 2.建造者独立,容易扩展。 114 | 115 | 缺点: 116 | 117 | 1.会产生多余的Builder对象及Director对象,消耗内存。 118 | 119 | 120 | # 参考资料 # 121 | 122 | 《Android 源码设计模式解析与实战 》 123 | -------------------------------------------------------------------------------- /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/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 T getIOHandler(Class clz) { 109 | IOHandler handler = null; 110 | try { 111 | handler = (IOHandler) Class.forName(clz.getName()).newInstance(); 112 | } catch (Exception e) { 113 | e.printStackTrace(); 114 | } 115 | return (T) handler; 116 | } 117 | } 118 | 119 | **使用** 120 | 121 | public class Main { 122 | public static void main(String[] args) { 123 | IOHandler ioHandler1 = IOFactory.getIOHandler(FileHandler.class); 124 | System.out.println(ioHandler1.query("123")); 125 | IOHandler ioHandler2 = IOFactory.getIOHandler(XMLHandler.class); 126 | System.out.println(ioHandler2.query("123")); 127 | IOHandler ioHandler3 = IOFactory.getIOHandler(DBHandler.class); 128 | System.out.println(ioHandler3.query("123")); 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /2017/2017.3/可删除内容的EditText.md: -------------------------------------------------------------------------------- 1 | ## Java文件 2 | 3 | /** 4 | * *************************************** 5 | * author:琚涛 6 | * time:2017/3/1 7 | * description:点击右侧按钮清除内容的EditText 8 | * 利用drawableRight 指定图标 9 | * 也可以把右侧按钮的点击事件剥离开,有需要可以实现 10 | * **************************************** 11 | */ 12 | 13 | public class ClearableEditText extends EditText { 14 | 15 | private Drawable mRightDrawable; 16 | 17 | public ClearableEditText(Context context) { 18 | super(context); 19 | init(); 20 | } 21 | 22 | public ClearableEditText(Context context, AttributeSet attrs) { 23 | super(context, attrs); 24 | init(); 25 | } 26 | 27 | public ClearableEditText(Context context, AttributeSet attrs, int defStyleAttr) { 28 | super(context, attrs, defStyleAttr); 29 | init(); 30 | } 31 | 32 | private void init() { 33 | //获取右侧图标引用(顺序:左[0]上[1]右[2]下[3]) 34 | mRightDrawable = getCompoundDrawables()[2]; 35 | 36 | //添加焦点变化监听 37 | this.setOnFocusChangeListener(new FocusChangeListenerImpl()); 38 | //添加文字变化监听 39 | this.addTextChangedListener(new TextWatcherImpl()); 40 | //设置右侧图片不可见 41 | setClearDrawableVisible(false); 42 | } 43 | 44 | private void setClearDrawableVisible(boolean isVisible) { 45 | Drawable drawable; 46 | if (isVisible) { 47 | drawable = mRightDrawable; 48 | } else { 49 | drawable = null; 50 | } 51 | setCompoundDrawables(getCompoundDrawables()[0], getCompoundDrawables()[1], drawable, getCompoundDrawables()[3]); 52 | 53 | } 54 | 55 | private class FocusChangeListenerImpl implements OnFocusChangeListener { 56 | 57 | private boolean isVisible; 58 | 59 | @Override 60 | public void onFocusChange(View view, boolean b) { 61 | //获得焦点后,如果内容长度大于1,显示删除图标 62 | if (b) { 63 | isVisible = getText().toString().length() > 0; 64 | } else { 65 | isVisible = false; 66 | } 67 | setClearDrawableVisible(isVisible); 68 | 69 | 70 | } 71 | } 72 | 73 | private class TextWatcherImpl implements TextWatcher { 74 | @Override 75 | public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) { 76 | 77 | } 78 | 79 | @Override 80 | public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) { 81 | 82 | } 83 | 84 | @Override 85 | public void afterTextChanged(Editable editable) { 86 | //内容改变后,如果长度大于0,显示删除图标 87 | boolean isVisible = getText().toString().length() > 0; 88 | setClearDrawableVisible(isVisible); 89 | } 90 | } 91 | 92 | //用于监听手指抬起时的位置,如果在清除图标上就清除文字 93 | @Override 94 | public boolean onTouchEvent(MotionEvent event) { 95 | switch (event.getAction()) { 96 | case MotionEvent.ACTION_UP: 97 | boolean isClear = event.getX() > (getWidth() - getTotalPaddingRight()) && event.getX() < (getWidth() - getPaddingRight()); 98 | if (isClear) { 99 | setText(""); 100 | } 101 | break; 102 | default: 103 | break; 104 | } 105 | return super.onTouchEvent(event); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /读书笔记/《Android 源码设计模式解析与实战》学习笔记/[Android]《Android 源码设计模式解析与实战》读书笔记 5.md: -------------------------------------------------------------------------------- 1 | # 简介 # 2 | 3 | 本书第四章介绍了原型模式。 4 | 5 | # 原型模式介绍 # 6 | 用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。被复制的实例就是“原型”,这个原型是可定制的。 7 | 8 | #使用场景# 9 | 1.类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等,通过原型拷贝避免这些消耗。 10 | 11 | 2.通过 new 产生的一个对象需要非常繁琐的数据准备或者权限,这时可以使用原型模式。 12 | 13 | 3.一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用,即保护性拷贝。 14 | 15 | # 简单实现 # 16 | 17 | public class WordDocument implements Cloneable { 18 | // 文本 19 | private String mText; 20 | 21 | // 图片名列表 22 | private ArrayList mImages = new ArrayList(); 23 | 24 | public WordDocument() { 25 | System.out 26 | .println("-----------------WordDocument构造函数------------------"); 27 | } 28 | 29 | 30 | public String getmText() { 31 | return mText; 32 | } 33 | 34 | public void setmText(String mText) { 35 | this.mText = mText; 36 | } 37 | 38 | public ArrayList getmImages() { 39 | return mImages; 40 | } 41 | 42 | public void addImage(String img) { 43 | this.mImages.add(img); 44 | } 45 | 46 | /** 47 | * 打印文档内容 48 | */ 49 | public void showDocument() { 50 | System.out.println("------------word Content Start--------------"); 51 | System.out.println("Text:" + mText); 52 | System.out.println("Images List:"); 53 | for (String imgName : mImages) { 54 | System.out.println("image name:" + imgName); 55 | } 56 | System.out.println("------------word Content End--------------"); 57 | } 58 | 59 | } 60 | 61 | 62 | public class Client { 63 | public static void main(String[] args) { 64 | //1.构建文档对象 65 | WordDocument originDoc=new WordDocument(); 66 | 67 | //2.编辑文档,添加图片内容 68 | originDoc.setmText("这是一篇文档"); 69 | originDoc.addImage("图片 1"); 70 | originDoc.addImage("图片 2"); 71 | originDoc.addImage("图片 3"); 72 | 73 | originDoc.showDocument(); 74 | 75 | //以原始文档为原型,拷贝一份副本 76 | WordDocument doc2=originDoc.clone(); 77 | doc2.showDocument(); 78 | 79 | //修改文档副本 80 | doc2.setmText("修改"); 81 | doc2.addImage("哈哈.jpg"); 82 | doc2.showDocument(); 83 | 84 | originDoc.showDocument(); 85 | } 86 | } 87 | 88 | 运行结果如下: 89 | 90 | ![](http://i.imgur.com/N6wXcPC.png) 91 | 92 | 可以看到,originDoc 和 doc2 在 Text 修改之后输出是不一样的,但是在 doc2 中 doc2.addImage("哈哈.jpg") 之后,originDoc 也输出了 哈哈.jpg,说明这只是简单的浅拷贝,引用类型的的新对象 doc2 的 mImages 只是单纯地指向了 this.mImages 引用,并没有重新构造一个 mImages 对象。所以在原型模式里最需要注意的就是深拷贝和浅拷贝,使用深拷贝可以避免操作副本时影响原始对象的问题。我们在这里可以这么做: 93 | 94 | @Override 95 | protected WordDocument clone() { 96 | try { 97 | WordDocument doc = (WordDocument) super.clone(); 98 | doc.mText = this.mText; 99 | ; 100 | doc.mImages = (ArrayList) this.mImages.clone(); 101 | return doc; 102 | } catch (Exception e) { 103 | e.printStackTrace(); 104 | } 105 | return null; 106 | } 107 | 108 | 我们将 doc.mImages 指向 this.mImages 的一份拷贝,而不是 this.mImages 本身,所以在 doc2 添加图片不会影响 originDoc。结果如下: 109 | 110 | ![](http://i.imgur.com/iLG6DKH.png) 111 | 112 | 113 | # Android源码中的原型模式 # 114 | 115 | Uri uri = Uri.parse("smsto:110"); 116 | Intent intent = new Intent(Intent.ACTION_SEND,uri); 117 | intent.putExtra("sms_body", "The SMS text"); 118 | //克隆 119 | Intent intent2 = (Intent)intent.clone(); 120 | startActivity(intent2); 121 | 122 | 123 | # 总结 # 124 | 125 | 优点 126 | 127 | 1.原型模式是在内存中二进制流的拷贝,要比直接new一个对象性能好很多,特别是要在一个循环体内产生大量对象时,原型模式可能更好的体现其优点。 128 | 129 | 2.还有一个重要的用途就是保护性拷贝,也就是对某个对象对外可能是只读的,为了防止外部对这个只读对象的修改,通常可以通过返回一个对象拷贝的形式实现只读的限制。 130 | 131 | 缺点: 132 | 133 | 1.这既是它的优点也是缺点,直接在内存中拷贝,构造函数是不会执行的,在实际开发中应该注意这个潜在问题。优点是减少了约束,缺点也是减少了约束,需要大家在实际应用时考虑。 134 | 135 | 2.通过实现Cloneable接口的原型模式在调用clone函数构造实例时并不一定比通过new操作速度快,只有当通过new构造对象较为耗时或者说成本较高时,通过clone方法才能够获得效率上的提升。 136 | 137 | # 参考资料 # 138 | 《Android 源码设计模式解析与实战 》 -------------------------------------------------------------------------------- /读书笔记/《Android 源码设计模式解析与实战》学习笔记/[Android]《Android 源码设计模式解析与实战》读书笔记 1.md: -------------------------------------------------------------------------------- 1 | # 简介 # 2 | 这周入手了《Android源码设计模式解析与实战》,将花一段时间去阅读并做上读书笔记。本书的第一章介绍了面向对象的六大原则,这篇文章先介绍前两条:单一职责原则和开闭原则,并观察书中所举的例子,一个写的不错的图片加载器,来看看作者是怎么用代码诠释着两大原则的。 3 | 4 | # 单一职责原则(SRP) # 5 | 6 | > 单一职责所表达出的用意就是 "单一" 二字。如何划分一个类、一个函数的职责,每个人都有自己的看法,这需要根据个人经验、具体的业务逻辑而定。但是,它也有一些基本的指导原则,例如,两个完全不一样的功能就不应该放在一个类中。一个类中应该是一组相关性很高的函数、数据的封装。 7 | 8 | 试想一下,如果所有的功能写在一个类里,那么这个类会越来越大,越来越复杂,越不易修改维护。那么根据功能,各自独立拆分出来,岂不是逻辑会清晰些。比如作者给的例子是一个 ImageLoder 类,和一个 ImageCache 类。 9 | 10 | public class ImageCache{ 11 | //只负责图片缓存逻辑 12 | } 13 | 14 | public class ImageLoader { 15 | //只负责图片的加载逻辑 16 | } 17 | 18 | # 开闭原则 (OCP)# 19 | > 软件中的对象(类、模块、函数等)应该对于扩展是开放的,但是对于修改是封闭的。当软件需要变化时,我们应该尽量通过扩展的方式实现变化,而不是通过修改原有的代码来实现。因为直接的修改,可能会影响已有的正常代码。不利于出现错误时排除问题。当然实际开发中,修改原有代码与扩展代码是同时存在的。但应尽量以扩展为主。 20 | 21 | 为了使程序更利于扩展,作者将之前的 ImageCache改造成了一个接口 22 | 23 | /** 24 | * 图片缓存接口 25 | */ 26 | public interface ImageCache { 27 | public Bitmap get(String url); 28 | 29 | public void put(String url, Bitmap bmp); 30 | } 31 | 定义了这样一个接口后,无论是想用内存缓存、SD卡缓存还是双缓存都只需要实现该接口即可。我们看看这几个缓存的实现: 32 | 33 | /** 34 | * 内存缓存MemoryCache类 35 | */ 36 | public class MemoryCache implements ImageCache { 37 | 38 | private LruCache mMemeryCache; 39 | 40 | public MemoryCache() { 41 | initLruCache(); 42 | } 43 | 44 | //初始化LRU缓存 45 | private void initLruCache() { 46 | //计算可使用的最大内存 47 | final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024); 48 | 49 | //取出1/4的内存作为缓存 50 | final int cacheSize = maxMemory / 4; 51 | 52 | mMemeryCache = new LruCache(cacheSize) { 53 | @Override protected int sizeOf(String key, Bitmap value) { 54 | return value.getRowBytes() * value.getHeight() / 1024; 55 | } 56 | }; 57 | } 58 | 59 | @Override public Bitmap get(String url) { 60 | return mMemeryCache.get(url); 61 | } 62 | 63 | @Override public void put(String url, Bitmap bmp) { 64 | mMemeryCache.put(url, bmp); 65 | } 66 | } 67 | 68 | 69 | /** 70 | * 将图片缓存到SD卡中 71 | */ 72 | public class DiskCache implements ImageCache { 73 | 74 | static String cacheDir = "sdcard/cache/"; 75 | 76 | //从缓存中获取图片 77 | public Bitmap get(String url) { 78 | return BitmapFactory.decodeFile(cacheDir + url); 79 | } 80 | 81 | //将图片存到内存中 82 | public void put(String url, Bitmap bmp) { 83 | FileOutputStream fileOutputStream = null; 84 | 85 | try { 86 | fileOutputStream = new FileOutputStream(cacheDir + url); 87 | 88 | //30 是压缩率,表示压缩70%; 如果不压缩是100,表示压缩率为0 89 | bmp.compress(Bitmap.CompressFormat.PNG, 100, fileOutputStream); 90 | } catch (FileNotFoundException e) { 91 | e.printStackTrace(); 92 | } finally { 93 | if (fileOutputStream != null) { 94 | try { 95 | fileOutputStream.close(); 96 | } catch (IOException e) { 97 | e.printStackTrace(); 98 | } 99 | } 100 | } 101 | } 102 | } 103 | 104 | 105 | /** 106 | * 双缓存 107 | */ 108 | public class DoubleCache implements ImageCache { 109 | ImageCache mMemoryCache = new MemoryCache(); 110 | DiskCache mDiskCache = new DiskCache(); 111 | 112 | //先从内存缓存中获取图片,如果没有就从SD卡中获取 113 | public Bitmap get(String url) { 114 | Bitmap bitmap = mMemoryCache.get(url); 115 | if (bitmap == null) { 116 | bitmap = mDiskCache.get(url); 117 | } 118 | return bitmap; 119 | } 120 | 121 | public void put(String url, Bitmap bmp) { 122 | mMemoryCache.put(url, bmp); 123 | mDiskCache.put(url, bmp); 124 | } 125 | } 126 | 127 | 128 | 在 ImageLoder 中只需要做如下一个小小的改动 129 | 130 | public void setmImageCache(ImageCache cache) { 131 | mImageCache = cache; 132 | } 133 | 134 | 用户可以通过 setmImageCache(ImageCache cache) 方法注入不同的缓存实现,这样不仅能够使 ImageLoder 更简单、健壮,也使得 ImageLoder 的可扩展性、灵活性更高。三个缓存图片的具体实现完全不同,但他们都实现了 ImageCache 接口,就都可以通过 setmImageCache 方法注入到 ImageLoder 中,这样 ImageLoder 就实现了千变万化的缓存策略。 -------------------------------------------------------------------------------- /2016/2016.10/aidl 浅析.md: -------------------------------------------------------------------------------- 1 | # Demo 地址 # 2 | [https://github.com/jutao/aidl](https://github.com/jutao/aidl "AIDL服务端") 3 | [AIDL客户端](https://github.com/jutao/aidlclient) 4 | 5 | # 什么是AIDL # 6 | 在 Android中,每一个应用程序独自拥有一个虚拟机,这样做虽然保证了进程之内数据的安全性,保证一个应用程序的数据不受其他应用程序的影响,也保证了一个应用程序挂掉了不至于影响其他应用程序。但是这样也造成了一个应用程序和另外一个应用程序没办法直接进行通讯。AIDL 的作用就是使来自不同应用的客户端跨进程通信访问你的 Service。 7 | AIDL 是 Android Interface Definition Language 的缩写,就是安卓内部通信接口描述语言。关于 AIDL的描述和用法我主要参考了Google 的官方API,找到一个中文的,链接:[http://www.android-doc.com/guide/components/aidl.html](http://www.android-doc.com/guide/components/aidl.html "AIDL")。需要补充的是,API里描述AIDL支持Java语言中的所有基本数据类型,但是经过查证和实验,实际上 AIDL 是不支持Short类型的。 8 | 9 | # AIDL用法 # 10 | ## 创建 AIDL 接口 ## 11 | ![](http://i.imgur.com/hIeNjic.png) 12 | 如果是 Eclipse 的话需要创建 File 并且不要忘记自己打上后缀 .aidl。 13 | 14 | ## 写 AIDL 接口 ## 15 | AIDL 的用法基本和写普通 Java 接口相同,需要注意的是包名一定要自己检查一下,还有导包也要自己写一下。 16 | 17 | package com.example.jutao.aidl; 18 | interface IServiceAidl { 19 | //计算两个数的和 20 | int add(int num1,int num2); 21 | } 22 | 写完之后需要注意,如果你写的 AIDL 接口正确,那么 Ecipse 是会自动编译的,而 Android Studio 需要手动编译,编译按钮如下图所示: 23 | ![](http://i.imgur.com/NnSnU2a.png) 24 | 编译通过后,Android Studio 所生成的文件在 25 | ![](http://i.imgur.com/VTAkxIR.png) 26 | 27 | ## 写 Service ## 28 | //当客户端绑定到该服务的时候 29 | @Override public IBinder onBind(Intent intent) { 30 | //当别人绑定服务的时候,就会得到AIDL接口 31 | return iBinder; 32 | } 33 | IBinder iBinder = new IServiceAidl.Stub() { 34 | @Override public int add(int num1, int num2) throws RemoteException { 35 | Log.d("TAG", "收到服务端请求,求出" + num1 + "和" + num2 + "的和"); 36 | return num1 + num2; 37 | } 38 | }; 39 | 40 | 41 | 42 | 43 | 47 | 48 | ## 写客户端 ## 49 | 客户端的主要功能是用户通过界面输入两个数字,点击远程计算按钮后通过服务端代码计算出结果返回给客户端并显示。 50 | ![](http://i.imgur.com/zoqkeKX.png) 51 | 点击按钮后 52 | ![](http://i.imgur.com/JAs8dhp.png) 53 | 需要注意的是,客户端也需要有一模一样的 AIDL 包,连包名都要一模一样!! 54 | 55 | //1、获取服务端 56 | Intent intent = new Intent(); 57 | //Android 5.0之后不支持隐式意图,必须是显式意图来启动绑定服务 58 | intent.setComponent( 59 | new ComponentName("com.example.jutao.aidl", "com.example.jutao.aidl.RemoteService")); 60 | //第三个参数是一个flag,绑定时自动启动 61 | bindService(intent, conn, Context.BIND_AUTO_CREATE); 62 | conn的定义: 63 | 64 | ServiceConnection conn = new ServiceConnection() { 65 | //绑定服务时 66 | @Override public void onServiceConnected(ComponentName name, IBinder service) { 67 | //拿到了远程的服务 68 | iServiceAidl = IServiceAidl.Stub.asInterface(service); 69 | } 70 | 71 | //当服务断开时 72 | @Override public void onServiceDisconnected(ComponentName name) { 73 | //回收资源 74 | iServiceAidl = null; 75 | } 76 | }; 77 | 78 | # AIDL 自定义类型 # 79 | ![](http://i.imgur.com/xnkwiMg.png) 80 | AIDL 默认支持的数据类型如上图所示,虽然支持List类型,但是需要 在List前注明输入List还是输出List,下面的例子会讲到。 81 | 首先 person 类要实现 Parcelable 接口,详细代码可以在我开头贴的Demo里看。 82 | 83 | import com.example.jutao.aidl.Person; 84 | 85 | interface PersonAidl { 86 | List add(in Person person); 87 | } 88 | parcelable Person; 89 | 90 | 91 | 92 | public class PersonService extends Service { 93 | private ArrayList persons; 94 | 95 | public PersonService() { 96 | } 97 | 98 | @Override public IBinder onBind(Intent intent) { 99 | persons = new ArrayList(); 100 | 101 | return iBinder; 102 | } 103 | 104 | private IBinder iBinder = new PersonAidl.Stub() { 105 | 106 | @Override public List add(Person person) throws RemoteException { 107 | persons.add(person); 108 | return persons; 109 | } 110 | }; 111 | } 112 | 113 | 114 | ![](http://i.imgur.com/78H8w2N.png) 115 | 可以看到,我每次输出的都是 persons 这一List,这是通过服务端返回的,说明我传输过去的值已经被服务端接收并存储。 -------------------------------------------------------------------------------- /2017/2017.2/屏幕适配工具类.md: -------------------------------------------------------------------------------- 1 | public class DimensUtils { 2 | public static void gen() { 3 | 4 | File file = new File("./app/src/main/res/values/dimens.xml"); 5 | BufferedReader reader = null; 6 | StringBuilder sw480 = new StringBuilder(); 7 | StringBuilder sw600 = new StringBuilder(); 8 | StringBuilder sw720 = new StringBuilder(); 9 | StringBuilder sw800 = new StringBuilder(); 10 | StringBuilder w820 = new StringBuilder(); 11 | 12 | 13 | try { 14 | System.out.println("生成不同分辨率:"); 15 | reader = new BufferedReader(new FileReader(file)); 16 | String tempString; 17 | int line = 1; 18 | // 一次读入一行,直到读入null为文件结束 19 | 20 | while ((tempString = reader.readLine()) != null) { 21 | 22 | if (tempString.endsWith("dp")) { 23 | //tempString = tempString.replaceAll(" ", ""); 24 | String start = tempString.substring(0, tempString.indexOf(">") + 1); 25 | String end = tempString.substring(tempString.lastIndexOf("<") - 2); 26 | float num = Float.parseFloat(tempString.substring(tempString.indexOf(">") + 1, tempString.indexOf("") - 2)); 27 | 28 | sw480.append(start).append((int) Math.round(num * 0.6)).append(end).append("\n"); 29 | sw600.append(start).append((int) Math.round(num * 0.75)).append(end).append("\n"); 30 | sw720.append(start).append((int) Math.round(num * 0.9)).append(end).append("\n"); 31 | sw800.append(tempString).append("\n"); 32 | w820.append(tempString).append("\n"); 33 | 34 | } else { 35 | sw480.append(tempString).append("\n"); 36 | sw600.append(tempString).append("\n"); 37 | sw720.append(tempString).append("\n"); 38 | sw800.append(tempString).append("\n"); 39 | w820.append(tempString).append("\n"); 40 | } 41 | line++; 42 | } 43 | reader.close(); 44 | System.out.println(""); 45 | System.out.println(sw480); 46 | System.out.println(""); 47 | System.out.println(sw600); 48 | 49 | System.out.println(""); 50 | System.out.println(sw720); 51 | System.out.println(""); 52 | System.out.println(sw800); 53 | 54 | String sw480file = "./app/src/main/res/values-sw480dp-land/dimens.xml"; 55 | String sw600file = "./app/src/main/res/values-sw600dp-land/dimens.xml"; 56 | String sw720file = "./app/src/main/res/values-sw720dp-land/dimens.xml"; 57 | String sw800file = "./app/src/main/res/values-sw800dp-land/dimens.xml"; 58 | String w820file = "./app/src/main/res/values-w820dp/dimens.xml"; 59 | writeFile(sw480file, sw480.toString()); 60 | writeFile(sw600file, sw600.toString()); 61 | writeFile(sw720file, sw720.toString()); 62 | writeFile(sw800file, sw800.toString()); 63 | writeFile(w820file, w820.toString()); 64 | } catch (IOException e) { 65 | e.printStackTrace(); 66 | } finally { 67 | if (reader != null) { 68 | try { 69 | reader.close(); 70 | } catch (IOException e1) { 71 | e1.printStackTrace(); 72 | } 73 | } 74 | } 75 | } 76 | 77 | public static void writeFile(String file, String text) { 78 | PrintWriter out = null; 79 | try { 80 | out = new PrintWriter(new BufferedWriter(new FileWriter(file))); 81 | out.println(text); 82 | } catch (IOException e) { 83 | e.printStackTrace(); 84 | } 85 | 86 | out.close(); 87 | } 88 | 89 | public static void main(String[] args) { 90 | gen(); 91 | } 92 | } -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /2017/2017.3/屏幕适配.md: -------------------------------------------------------------------------------- 1 | 用 gradle 所写,效率较高,可以生成不同屏幕密度的dimens。 2 | 3 | public class DimenFactory extends DefaultTask { 4 | 5 | int[] mDimens = [360, 384, 411, 480, 540, 600, 720, 800]; 6 | int mFromDimen = 360; 7 | 8 | String mDimenFileName = "values/dimens.xml"; 9 | 10 | String mResFolder = project.getProjectDir().getPath() + "/src/main/res/"; 11 | 12 | @TaskAction 13 | def create() { 14 | createDimenFromDimenFile(); 15 | } 16 | 17 | def createDimenFromDimenFile() { 18 | String path = mResFolder + mDimenFileName; 19 | Map pairs = new HashMap<>(); 20 | try { 21 | File fXmlFile = new File(path); 22 | DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance(); 23 | DocumentBuilder dBuilder = dbFactory.newDocumentBuilder(); 24 | Document doc = dBuilder.parse(fXmlFile); 25 | doc.getDocumentElement().normalize(); 26 | 27 | NodeList nList = doc.getElementsByTagName("dimen"); 28 | for (int temp = 0; temp < nList.getLength(); temp++) { 29 | Node nNode = nList.item(temp); 30 | if (nNode.getNodeType() == Node.ELEMENT_NODE) { 31 | Element eElement = (Element) nNode; 32 | pairs.put(eElement.getAttribute("name"), eElement.getTextContent()) 33 | } 34 | } 35 | Map dps = new HashMap<>(); 36 | Map sps = new HashMap<>(); 37 | 38 | for (Map.Entry entry : pairs) { 39 | String mVal = entry.getValue(); 40 | mVal = mVal.replaceAll("dp", "").replaceAll("sp", ""); 41 | double val = Double.valueOf(mVal); 42 | if (entry.getValue().contains("dp")) { 43 | dps.put(entry.getKey(), val); 44 | } else { 45 | sps.put(entry.getKey(), val); 46 | } 47 | } 48 | 49 | dps = new TreeMap<>(dps); 50 | sps = new TreeMap<>(sps); 51 | for (int dimen : mDimens) { 52 | String folder = mResFolder + "values-sw" + (int) dimen + "dp"; 53 | String fileName = folder + "/dimens.xml"; 54 | new File(folder).mkdir(); 55 | new File(fileName).createNewFile(); 56 | PrintWriter printWriter = new PrintWriter(fileName); 57 | printWriter.println(""); 58 | printWriter.println(""); 59 | for (Map.Entry entry : dps) { 60 | double ratio = entry.getValue() / mFromDimen; 61 | double sdp = ratio * dimen; 62 | printWriter.printf("\t%ddp\r\n", entry.getKey(), Math.round(sdp)); 63 | } 64 | for (Map.Entry entry : sps) { 65 | printWriter.printf("\t%dsp\r\n", entry.getKey(), Math.round(entry.getValue())); 66 | } 67 | printWriter.println(""); 68 | printWriter.close(); 69 | } 70 | } catch (Exception e) { 71 | println e.getMessage(); 72 | } 73 | } 74 | 75 | 76 | void addToMap(String path, Map map) { 77 | File file = new File(path); 78 | if (!file.exists()) return; 79 | if (file.isDirectory()) { 80 | File[] files = file.listFiles(); 81 | for (File f : files) { 82 | addToMap(f.getAbsolutePath(), map); 83 | } 84 | } else { 85 | String content = fileToString(path); 86 | println path; 87 | map.put(path, content); 88 | } 89 | } 90 | 91 | static String fileToString(String path) { 92 | try { 93 | File f = new File(path); 94 | FileInputStream inp = new FileInputStream(f); 95 | byte[] bf = new byte[(int) f.length()]; 96 | inp.read(bf); 97 | return new String(bf, "UTF-8"); 98 | } catch (FileNotFoundException e) { 99 | e.printStackTrace(); 100 | } catch (IOException e) { 101 | e.printStackTrace(); 102 | } 103 | return null; 104 | } 105 | } 106 | 107 | task createDimen(type: DimenFactory) {} 108 | 109 | createDimen {} 110 | 111 | 运行方式: 112 | 113 | 1. 在 Terminal 下输入 gradlew createDimen 114 | 2. 在目标 gradle 下添加 apply from: 'autodimension.gradle'(你创建的名字) 115 | 116 | 参照: 117 | 118 | [有关于value-swdp](http://www.jianshu.com/p/141193f1610b) 119 | 120 | [UI设计师不可不知的安卓屏幕知识](http://www.zcool.com.cn/article/ZNjI3NDQ=.html#commentMaoDian) -------------------------------------------------------------------------------- /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 |