先升级Node.js到最新版

这里推荐使用nvm安装最新版node

升级Hexo

1
2
3
4
5
6
7
8
9
10
11
12
13
# 安装hexo-cli命令行工具
$ npm install -g hexo-cli

# 安装npm-check
$ npm install -g npm-check
# 检查npm依赖包是否有更新
$ npm-check

# 安装npm包更新工具
$ npm install -g npm-upgrade
# 更新package.json、node-modules
$ npm-upgrade -g
$ npm-upgrade -save
阅读全文 »

tar

解压

1
2
3
4
# 解压到当前目录
$ tar -zxvf apache-kylin-2.6.4-bin-cdh57.tar.gz
# 解压到指定目录(指定命令选项`-C`),如果指定目录不存在,需要先新建目录
$ tar -zxvf apache-kylin-2.6.4-bin-cdh57.tar.gz -C ../Applications

压缩

tar可以保留文件的权限信息

1
$ tar -zcvf kylin-2.5.2.tar.gz kylin

zip

解压

1
unzip -o -d /home/sunny myfile.zip

把myfile.zip文件解压到 /home/sunny/
-o:不提示的情况下覆盖文件;
-d:-d /home/sunny 指明将文件解压缩到/home/sunny目录下;

相关资料
为什么 Linux 要用 tar.gz,很少用 7Z 或 ZIP?

Gradle 常用配置

签名

当程序中使用了第三方的功能,比如分享、地图时,要求程序必须是已签名的
Gradle中配置签名后,可以使我们的Debug也带上签名,而不需要使用Build/Generate Signer Apk去编译带签名的APK

  • build.gradle
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 在 Android {} 节点下增加
signingConfigs {
signConfig {
storeFile file ('../key.jks')
storePassword '123456'
keyAlias 'xdj'
keyPassword '123456'
}
}
buildTypes {
release {
signingConfig signingConfigs.signConfig
}
debug {
signingConfig signingConfigs.signConfig // 配置debug包的签名
}
}
  • 获得签名文件信息
1
2
// 终端命令
keytool -list -v -keystore ./key.jks

输出结果
返回

Tips
还可通过在AndroidStudio自带终端中运行 ./gradlew signingReport命令获取

  • 获得APK签名信息
1
2
// 终端命令
jarsigner -verify -certs -verbose ./app/build/outputs/apk/app-googleplay-debug.apk

输出结果
返回结果

Tips:
${keyAlias}CN=Android Debug 说明APK未签名

多渠道打包

以友盟为例

  • build.gradle
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
defaultConfig {
manifestPlaceHolders=[UMENG_CHANNEL_VALUE:'Umeng'] // 默认渠道为Umeng
}
productFlavors {
googleplay {
manifestPlaceHolders=[UMENG_CANNEL_VALUE:'Google Play']
}
wandoujia{
manifestPlaceholders = [UMENG_CHANNEL_VALUE: "Wandoujia"]
// minSdkVersion 19
// applicationId 'com.rx.demo'
// targetSdkVersion 22
// versionCode 360
// versionName '1.1.1'
// signingConfig signingConfig.wandoujiaConfig
}
xiaomi{
}
tencent{
}
}
  • manifest
1
2
<!-- 替换为您应用的推广渠道名称,channel id自定义。-->
<meta-data android:name="UMENG_CHANNEL" android:value="${UMEMG_CHANNEL_VALUE}"/>
  • 编译命令
    ./gradlew assemble: 编译所有渠道的Debug和Release
    ./gradlew assembleWandoujia: 编译豌豆荚的Debug和Release
    ./gradlew assembleWandoujiaDebug: 编译豌豆荚的Debug
    ./gradlew build:

    Tips
    貌似Android Studio上有多渠道打包的插件,但最终还是需要使用命令行

###自定义APK名称

  • build.gradle
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 根节点下增加
def releaseTime() {
return new Date().format("yyyy-MM-dd", TimeZone.getTimeZone("UTC"))
}

// Android {} 节点下增加
buildTypes {
applicationVariants.all { variant ->
variant.outputs.each { output ->
def outputFile = output.outputFile
// && outputFile.name.indexOf('debug') == -1
if (outputFile != null && outputFile.name.endsWith('.apk')
&& outputFile.name.indexOf('debug') == -1) {
// 输出apk名称为app-googleplay-release-v1.0-build1-2015-12-16
def fileName = outputFile.name.replace(".apk", "-v${defaultConfig.versionName}-build${defaultConfig.versionCode}-" +
"${releaseTime()}.apk")
output.outputFile = new File(outputFile.parent, fileName)
}
}
}
}

导入包

导入aar、jar、modle

1
2
3
4
5
6
7
8
9
10
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar']) // 导入libs文件夹中所有的jar包
compile 'group:name:version'
compile 'group:name:1.1.+'
compile(group:name:version) {
exclude module: 'support-v4' // 发生冲突时,排除v4
}
compile(name:'xxx', ext:'aar') // aar包是用来替换jar的文件类型
compile project(:modle)
}

Tips
+ 表示最新的版本号,曾经遇到过在Windows下导入包是输入的时准确的版本号1.0.0没有问题,到了Mac上的就提示找不到包,后来改成 1.0.+就好了

导入 *.so

  • Android Studio 默认路径:Project视图模式下./app/src/main/jniLibs
    这是我目前使用的方法,直接把so包放入文件夹即可,不需要其他操作
    so包默认存放路径
  • 自定义路径
    Eclipse中的习惯是讲so包放入libs文件夹中,这在Android Studio中也可以通过配置Gradle实现
    build.gradle
    1
    2
    3
    4
    5
    6
    7
    // 在 Android{} 节点下增加
    sourceSets {
    main {
    // jniLibs.srcDirs=['./libs']
    jniLibs.srcDirs=['./libs/jniLibs']
    }
    }

其他配置

  • 下载源码及文档
1
2
3
4
5
6
7
8
9
10
11
12
13
idea {
module {
downloadSources = true
downloadJavadoc = false
}
}

eclipse {
classpath {
downloadSources = true
downloadJavadoc = false
}
}

完整build.gradle

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
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
89
90
91
92
93
94
95
96
97
98
99
100
101
apply plugin: 'com.android.application'

def releaseTime() {
return new Date().format("yyyy-MM-dd", TimeZone.getTimeZone("UTC"))
}

android {
compileSdkVersion 22
buildToolsVersion "23.0.1"

signingConfigs {
signConfig {
storeFile file ("../key.jks")
storePassword "123456"
keyAlias "xdj"
keyPassword "123456"
}
}

defaultConfig {
applicationId "com.xdj.rxjavademo"
minSdkVersion 15
targetSdkVersion 22
versionCode 1
versionName "1.0.0"

manifestPlaceholders=[UMENG_CHANNEL_VALUE: 'Umeng']
}

buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.signConfig
zipAlignEnabled true
// shrinkResources true // 清除无用的resource
}
debug {
buildConfigField "boolean", "LOG_DEBUG", "false" // 设置是否打印LOG,貌似没效果
signingConfig signingConfigs.signConfig
}
applicationVariants.all { variant ->
variant.outputs.each { output ->
def outputFile = output.outputFile
// && outputFile.name.indexOf('debug') == -1
if (outputFile != null && outputFile.name.endsWith('.apk')
&& outputFile.name.indexOf('debug') == -1) { // 不对Debug进行重命名
// 输出apk名称为app-googleplay-release-2015-12-16-v1.0-build1
def fileName = outputFile.name.replace(".apk", "-${releaseTime()}" +
"-v${defaultConfig.versionName}-build${defaultConfig.versionCode}" +
".apk")
output.outputFile = new File(outputFile.parent, fileName)
}
}
}
}

productFlavors {
googleplay {
manifestPlaceholders = [UMENG_CHANNEL_VALUE: "Google Play"]
}
xiaomi {
// manifestPlaceholders = [UMENG_CHANNEL_VALUE: "Xiao Mi"]
}
_360 {
// manifestPlaceholders = [UMENG_CHANNEL_VALUE: "360"]
// minSdkVersion 19
// applicationId 'com.rx.demo'
// targetSdkVersion 22
// versionCode 360
// versionName '1.1.1'
// signingConfig signingConfig._360Config
}
tencent {
// manifestPlaceholders = [UMENG_CHANNEL_VALUE: "Tencent"]
}
baidu {
// manifestPlaceholders = [UMENG_CHANNEL_VALUE: "Baidu"]
}
}

sourceSets {
main {
jniLibs.srcDirs=['./libs/jniLibs']
}
}
}

dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:22.2.1'
compile 'com.android.support:design:22.2.1'
compile 'io.reactivex:rxjava:1.1.0'
compile 'io.reactivex:rxandroid:1.1.0'
// compile ('cn.trinea.android.view.autoscrollviewpager:android-auto-scroll-view-pager:1.1.2') {
// exclude module: 'support-v4'
// }
compile 'com.android.support:recyclerview-v7:22.2.1'
}

gradle bin镜像地址

https://mirrors.aliyun.com/macports/distfiles/gradle/

首次以U盘方式安装Ubuntu16.04时,系统在开机时会让你选择进入Ubuntu还是Windows,后重新安装Ubuntu14.04的时候,就都是直接进入Ubuntu而没有选择项了。

修复引导

执行此命令后便会修复引导

1
$ sudo update-grub

修改默认系统

  1. 打开/etc/default/grub

    1
    $ sudo vim /etc/default/grub
  2. 修改默认系统
    GRUB_DEFAULT=4
    我的Windows是第五个选择,所以值设置4默认便是选择Windows

  3. 执行更新

    1
    $ sudo update-grub

资料来源

http://kwangka.github.io/2015/11/23/dualbootgrub/

TextInputEditText样式设置

TextInputEditText是在Design包中MD风格的EditText

样式调整

1
2
3
4
5
6
7
8
<style name="InputStyle">
<!-- 底部线 默认颜色 -->
<item name="colorControlNormal">@android:color/white</item>
<!-- 底部线 EditText激活时颜色 -->
<item name="colorControlActivated">@android:color/white</item>
<!-- 设置光标 -->
<item name="android:textCursorDrawable">@drawable/line</item>
</style>

android:textCursorDrawable@null时光标颜色与textColor相同,但是目测宽度只有1dp左右,太细了,所以最终还是选择了自定义光标

1
2
3
4
5
6
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@android:color/black" />
<size android:width="2dip" />
</shape>

取消底部线

如果要取消TextInputEditText底部线,在EditText中我们设置background=@null,但是TextInputEditText不同,我们需要将TextInputLayoutTextInputEditbackgound都设置为@null

1
2
3
4
5
6
7
8
9
10
11
12
13
<android.support.design.widget.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@null">

<android.support.design.widget.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@null"
android:hint="请输入手机号码"
android:padding="8dp"/>

</android.support.design.widget.TextInputLayout>

首先先来看下基本的套路,第一次看完事件分发后通常是懵逼,但我觉得还是应该先看下。

Android 中当系统捕获事件时,都是由最外层的View依次向下传递,当然,事件不能总是传递到最后一子View才结束,我们在每一级消息传递时都会对消息做出响应以决定继续分发或者拦截。

Touch事件相关方法 方法功能 View ViewGroup Activity
dispatchTouchEvent 事件调度 o
onInterceptTouchEvent 事件拦截 × ×
onTouchEvent 事件响应

Tips

  1. 表格中的View指的是不包含任何子ViewView,例如TextViewButton
  2. View本身是有dispatchTouchEvent方法的,但是由于没有子View,也就没有可分发的目标了
  3. 下文中提到的事件消费,均表示事件终结,及在这之后,既不会发送分发,也不会上传

事件调度 dispatchTouchEvent

return true; 事件已被消费,事件结束。
retrun false; 事件不再继续分发,呼叫上层控件对其进行消费。
return super.dispatchTouchEvent(ev); 事件分发,将被传递到onInterceptTouchEvent进行处理,(如果是Activity,则直接向下传递)

Tips
事件分发本身也对事件进行消费

事件拦截 onInterceptTouchEvent

此方法只在ViewGroup中存在
return true; 对事件进行拦截,并交由本层控件的onTouchEvent对事件进行处理
retrun false; 不对事件进行拦截,向下分发,传递给子ViewdispatchTouchEvent处理
return super.onInterceptTouchEvent(ev); 默认等同于 return true; (既然是拦截器,默认功能当然是拦截啦)

事件响应 onTouchEvent

return true; 事件已被消费,事件终结
return false; 事件处理后又上传到上层View,交由上层的ViewonTouchEvent进行处理
return super.onTouchEvent(ev); 默认等同于 return false;

ViewGroup 事件流程图

由于ViewGroup的事件是最全面的,所以图中只画了ViewGroup
Flow

Thanks
Android 事件分发机制详解 (这网址现在已经不能用了,心好累)
Processon 免费在线绘图

===

Note
中发现Activity 默认只会将Down向下传递,而MoveUp会在onTouchEvent中消费,必须要将Down消费后,MoveUp才会向下传递

正文

创建对象

  • 老板 Activity
    1. 主要是派发任务,也就是dispatchTouchEvent
    2. 秘书 onTouchEvent(老板总不能所有事都亲历亲为,所以我们还得有个秘书)
  • 管理 ViewGroup
    1. 项目经理 dispatchTouchEvent 也是派发任务
    2. 部门经理 onInterceptTouchEvent 用于评估项目经理提出的功能可行性及工时
    3. 技术经理 onTouchEvent 公司技术骨干,有难度的活找他们。
  • 码农 View
    1. onTouchEvent 由于码农是在公司的最低层,所以咱们没得选
    2. dispatchTouchEvent 虽然这方法咱现在没用,但咱也总不能当一辈子码农啊

明确功能

  • dispatchTouchEvent
    1. return true 对应角色自己就将任务完成了
    2. return false 对应角色自己无法下决定,于是请求上级出面解决
    3. return dispatchTouchEvent 任务正常派发
  • onInterceptTouchEvent
    1. return true/onInterceptTouchEvent 对应角色认为任务不合理,将其驳回
    2. return false 任务合理,不驳回
  • onTouchEvent
    1. return true 任务执行完成,但没必要汇报上级
    2. return false/super.onTouchEvent 本职工作已完成,但你自己并不能决定任务是否已经完成,需要上级验收通过才算真正完成

案例1

某天一位潜在客户来找到老板,说希望实现一个淘宝,预算5W,老板一听,虎躯一震,直接回绝客户,然后就没有下文了。

在这个案例中,老板自己就事情给处理了。

1
Activity/老板 dispatchTouchEvent return true.

案例2

  1. 某天一位潜在客户来找到老板,说希望实现一个淘宝,预算5000W,但是要1个月必须上线第一版,老板一听,时间紧了点,但加加班还是可以搞定的,很快便签好了合同,并将任务派发给了项目经理并特别要求这个项目一定要做好,
  2. 项目经理立马就将老板的指示传递给了部门经理
  3. 部分经理并没有提出异议,于是便将任务细化,派发给码农
  4. 码农表示自己一个月时间肯定做不完啊,于是便向技术经理需求帮助
  5. 一个月后,技术经理将第一版交到了老板的秘书手里
  6. 秘书将第一版拿给了老板,并且最终通过了客户验收
1
2
3
4
5
6
Activity/老板 dispatchTouchEvent return dispatchTouchEvent.
ViewGroup/项目经理 dispatchTouchEvent return dispatchTouchEvent.
ViewGroup/部门经理 onInterceptTouchEvent return false.
View/码农 onTouchEvent return false.
ViewGroup/技术经理 onTouchEvent return false
Activity/老板秘书 onTouchEvent

案例3

  1. 案例2中的客户想老板反映,希望网站的平均并发量可以达到100000/s,老板变将这个任务交给了项目项目经理
  2. 项目经理由于对技术不是很熟悉,于是便直接将任务交给部门经理评估
  3. 部门经理收到任务后,认为此需求不合理,找到老板秘书后表示阿里双11的峰值瞬时并发量也不过50000/s
  4. 老板秘书向老板传到了部门经理的意见
1
2
3
4
Activity/老板 dispatchTouchEvent return dispatchTouchEvent.
ViewGroup/项目经理 dispatchTouchEvent return dispatchTouchEvent.
ViewGroup/部门经理 onInterceptTouchEvent return true/onInterceptTouchEvent.
Activity/老板秘书 onTouchEvent
0%