Why Git? Learn It!

总览

  • 为什么要用 Git?
  • Git + SourceTree + Gitlab 的使用演示
  • 提醒和推荐

为什么要用 Git?

  • 一个工具、一套方案,解决源码管理的问题

对于版本控制,我们需要什么?

  • 代码历史追溯
    • 某一次提交的改动(SVN,Git)
    • 某一个功能的改动过程(好的提交习惯更重要,在多人协作情况下提交历史更清晰)
  • 协同开发
    • 用分支来隔离上线与开发中的代码
    • 多人多线并行开发:1 人对 N 线,N 人对 1 线都有可能

秒极定律
一件事情如果能控制在 10s 内完成,人们就会极频繁的使用它。
举栗子:

  • docker 被推崇,从虚拟机到 Vagrant 到 docker,碰到了秒级定律
  • 持续集成,快速迭代

秒级的代码管理?

  • 本地代码飞快变化至任意历史提交记录:SVN 30s-2min;Git 秒级
    • 工作状态快速切换
  • 代码提交速度飞快:SVN 受限于网络速度;Git commit 秒级,push 受限于网络速度
  • 代码创建、切换、合并分支飞快:SVN 速度慢,很麻烦;Git 赞!
    • 勇于原型探索,历史清晰,易调整方向,易新建,易弃用
    • 与 code review 结合

SVN 比 Git 好在哪里

  • 更简单,符合原有自主文件夹管理版本的心理预期
    • Git 暴露概念过多,学习曲线陡峭,这点不如 hg
  • SVN 有目录级的权限控制
    • 适合大中型中间件软件公司:目录划分权限,职能长期固定
  • 也许还有其它,我不清楚

Git 真实的影响力

I’m an egotistical bastard, and I name all my projects after myself. First Linux, now Git.
—— Linus

  • 在 2016 问这个问题对互联网来说已是大势所趋
    • npm + pip 均托管在 Github
    • OpenJDK 使用 Hg(Mercurial)
    • Github 早已一统开源天下,Java 概莫能外
  • SVN 与 Git 二分天下,但 Git 占据的是互联网明星创业公司和 BAT 的优秀团队,为何?
  • 会 Git 找工作是加分项
  • 学习版本控制的另一种思维方式(学习一门语言就是学习一类思维方式)

一些问题

  • Git 不能直接解决目前后端的开发环境
    • code 规范的 branch 流程
    • 依赖,理清 maven 依赖和配合方式
    • 秒级上线(测试、预发布、正式)
    • 相应(尽可能少人力介入的)自动化测试、监控

Git + SourceTree + Gitlab 的使用演示

Git 使用流程示意图

local repo

  • 用 SourceTree 初始化一个本地项目
  • 小步提交,介绍 staged 的用法
    • stage or unstage or discard
    • stage 某一行代码
  • 一次提交
  • 介绍 master 分支及 branchs 界面
    • 留意 commit 编号
  • 创建一个分支 feature/login
  • 新建 login.java 文件,index.html 加一行代码
  • commit,观察分支树变化
  • 切换分支,观察代码变化
  • 创建一个分支 feature/share
  • 新建 share.py 文件,index.html 改一行代码
  • commit,观察分支树变化
  • 切回 feature/login,临时处理事情,再切回 feature/share
  • feature/share 分支删几行代码,再提交一次
  • feature/share merge to master 分支,观察分支树变化
  • feature/login merge to master 分支,处理冲突
  • 初始化 Git-flow
  • 新建 feature/school 分支,commit once
  • 用 Git-flow-gui 完成 feature/school 分支
  • 留意刚才的所有提交都在本地,思考与 SVN 区别(很像,只是分支极灵活)
  • Q&A

Gitlab

  • 注册帐号,ssh-key 授权(略)
  • 介绍 Gitlab 结构:team-project
  • 新建项目 test-repo
  • 留意空项目向导,复制代码库 url
  • 本地配置 remote-url,观察 remotes 变化
  • push,注意是 push 多条分支,留意 local-remote 分支对应关系
  • 再看 remotes 变化,分支树变化
    • remotes branchs 与 local branchs 一致
    • 分支树中多了 origin/*
  • 观察 Gitlab 界面变化
  • 介绍 Gitlab 单个项目界面
  • Q&A

来一次典型的开发过程(包含 code review)

  • 保证在 develop 分支,pull 最新代码
  • 网页端提交 readme 文件,造成 origin 变化,介绍 fetch,注意 branchs 变化
  • 重新 pull
  • 用 Git-flow-gui 建立 feature/news 分支
  • 产生两次 commit(包含 rename 文件)
  • 留意分支树变化,注意 Gitlab-web 端不存在刚才的提交,push,再对比
  • Gitlab-web 端新建 merge-request, 留意 source-branch, target-branch, title, ass
  • 被指派者 review 代码,web 端回复建议
  • 根据建议产生新的 commit,同时 push
  • 刷新 web 端观察变化:查看一次 commit,查看全部 changes
  • 被指派者回复 LGTM(look good to me),点击 merge
  • 回到 SourceTree,观察分支树变化,fetch 再看
  • 切回 develop 分支
  • Q&A
    • 有冲突 merge 按钮会不可点击,merge develop to work_branch ,在 develop 分支外解决冲突,commit&push,merge 按钮就可点击了
    • 可方便的与自动化测试等结合起来

提醒和推荐

一些提醒(坑)

  • 初次使用 Git,注意设置 git config --global email&name
  • Windows 下的 Git 使用的确有些不便(不过我不熟悉,也不清楚具体问题)
  • 授权方式推荐 ssh-key
  • 忽略文件的配置
  • 着手代码前思考终极问题:『我在哪里,要去何处?』(先认清所在分支,pull)
  • commit 已经 push 到远端了,这个时候不要想着再去修改了
  • 学习新东西,坑总是有的,多填了也就会了

一些推荐

  • 强调思路,记住秒级定律,效率为王
  • 小步提交,每小时至少提交一次
    • 一次任务的实现过程:整体设计,框架(类),接口,单元测试,实现
  • 完整提交,完整提交,必须完整提交!不该有任一个提交项目不可运行。
  • 保证非工作期间,工作区间干净
  • 不想提交但要切换分支处理事物用 stash 处理
  • Github 经常被 X
    • coding.net 代码私有库托管可以用。找开源代码,永远是 Github
    • Google 也是被 X。珍爱生命,自配 VPN
  • 国内 Git 私有库托管推荐 coding.net,国外推荐 bitbucket(支持hg) 和 Gitlab

其它

  • 依赖(迷信)工具不可取,但工具会影响思维方式,而思维方式非常重要

举例:
SVN 的思维方式决定了 diff patch 的开源合作方式。交流不顺畅,实现思路难以程现,这种合作方式很长时间里都是高端人士的特权。
Git + Github 催生了 fork,成就了最大的程序员社交网站,也极力促进了开源社区的发展。

移动周分享-第41期

Python 脚本入门知识

@(归纳中)

  • 如何写脚本
    • 和写程序一模一样….
    • 所以我讲一些周边知识…
  • virtualenv
    • python沙盒
    • 某些依赖2.7 有些依赖3.0
    • 创建 : virtualenv test_env
    • 创建纯净的环境 :virtualenv test_env —no-site-packages
    • 启动 :source ./bin/activate
    • 关闭 :deactivate
  • setuptools
    • 打包
    • setup.py
    • python setup.py bdist_egg
  • pip
    • 安装打包工具
  • PyPI
    • the Python Package Index : Python Package Index

The Bash Shell 变量(初级)

@(Share)[shell]

[TOC]

前言

我有一个想法,希望通过学习脚本来实现blog的自动创建及发布等操作,
zm推荐shell,pj建议js

常见bash熟悉的特性:

1
2
3
4
5
6
7
8
9
10
11
12
#$:(关于本 shell 的 PID)
#钱字号本身也是个变量喔!这个咚咚代表的是『目前这个 Shell 的线程代号』,亦即是所谓的 PID (Process ID)。 更多的程序观念,我们会在第四篇的时候提及。想要知道我们的 shell 的 PID ,就可以用:『 echo $$ 』即可!出现的数字就是你的 PID 号码。
XcodeYangdeMBP2:~ xcodeyang$ echo $$
10337
#?:(关于上个运行命令的回传值)
#问号也是一个特殊的变量?在 bash 里面这个变量可重要的很! 这个变量是:『上一个运行的命令所回传的值』, 上面这句话的重点是『上一个命令』与『回传值』两个地方。当我们运行某些命令时, 这些命令都会回传一个运行后的代码。一般来说,如果成功的运行该命令, 则会回传一个 0 值,如果运行过程发生错误,就会回传『错误代码』才对!一般就是以非为 0 的数值来取代。
XcodeYangdeMBP2:~ xcodeyang$ echo$
-bash: echo$: command not found

XcodeYangdeMBP2:~ xcodeyang$ echo $?
127 # <== error
XcodeYangdeMBP2:~ xcodeyang$ echo $?
0 # <== success
  • 命令编修能力 (history):
1
2
XcodeYangdeMBP2:~ xcodeyang$ echo $HISTSIZE
500
  • 命令与文件补全功能:

    [tab] 按键的好处

    • 一下和两下的区别
    • 命令与文件路径
1
2
3
4
5
6
7
8
XcodeYangdeMBP2:~ xcodeyang$ x
xar xcodebuild xgettext5.18.pl xmllint xslt-config
xargs xcodeproj xip xpath xsltproc
xattr xcrun xjc xpath5.16 xsubpp
xattr-2.6 xed xml2-config xpath5.18 xsubpp5.16
xattr-2.7 xgettext.pl xml2man xsanctl xsubpp5.18
xcode-select xgettext5.16.pl xmlcatalog xscertadmin xxd
XcodeYangdeMBP2:~ xcodeyang$ x
  • 命令别名配置功能: (alias)
1
2
3
4
5
6
7
XcodeYangdeMBP2:~ xcodeyang$ alias xcodedaniel='ls'
XcodeYangdeMBP2:~ xcodeyang$ cd Documents/
XcodeYangdeMBP2:Documents xcodeyang$ xcodedaniel
GItHub Welcome.itmz bloodsugar 技术部分享
XcodeYangdeMBP2:Documents xcodeyang$ ls
GItHub Welcome.itmz bloodsugar 技术部分享
XcodeYangdeMBP2:Documents xcodeyang$

可变性与方便性

举例:MAIL 变量不同用户设置
yzp -> /var/spool/mail/yzp
cy -> /var/spool/mail/cy
pj -> /var/spool/mail/pj
zm -> /var/spool/mail/zm

1
2
XcodeYangdeMBP2:~ xcodeyang$ echo $USER
xcodeyang

变量的取用与配置:echo, 变量配置守则, unset

变量的取用就如同上面的范例,利用 echo 就能够读出,只是需要在变量名称前面加上 $ , 或者是以 ${变量} 的方式来取用都可以

变量的配置守则

  • 变量与变量内容以一个等号『=』来连结,如下所示:

    『yname=zhipingyang』

  • 等号两边不能直接接空格符,如下所示为错误:

    『myname = zhipingyang』或『myname=zhiping yang』

  • 变量名称只能是英文字母与数字,但是开头字符不能是数字,如下为错误:

    『2myname=zhipingyang』

  • 变量内容若有空格符可使用双引号『”』或单引号『’』将变量内容结合起来,但
    双引号内的特殊字符如 $ 等,可以保有原本的特性,如下所示:

    『var=”lang is $LANG”』则『echo $var』可得『lang is en_US』

  • 单引号内的特殊字符则仅为一般字符 (纯文本),如下所示:

    『var=’lang is $LANG’』则『echo $var』可得『lang is $LANG』

  • 可用跳脱字符『 \ 』将特殊符号(如 [Enter], $, \, 空格符, ‘等)变成一般字符;

    XcodeYangdeMBP2:~ xcodeyang$ myname=hello\ world

  • 在一串命令中,还需要藉由其他的命令提供的信息,可以使用反单引号『命令』或 『$(命令)』。特别注意,那个 ` 是键盘上方的数字键 1 左边那个按键,而不是单引号! 例如想要取得核心版本的配置:

    『version=$(uname -r)』再『echo $version』可得『2.6.18-128.el5』

  • 若该变量为扩增变量内容时,则可用 “$变量名称” 或 ${变量} 累加内容,如下所示:

    『PATH=”$PATH”:/home/bin』

  • 若该变量需要在其他子程序运行,则需要以 export 来使变量变成环境变量:

    『export PATH』

  • 取消变量的方法为使用 unset :『unset 变量名称』例如取消 myname 的配置:

    『unset myname』

设置myname变量

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
# 例子一
XcodeYangdeMBP2:~ xcodeyang$ echo $myname
# <==这里并没有任何数据~因为这个变量尚未被配置!是空的!
XcodeYangdeMBP2:~ xcodeyang$ myname = Daniel
-bash: myname: command not found
XcodeYangdeMBP2:~ xcodeyang$ myname=Daniel
XcodeYangdeMBP2:~ xcodeyang$ echo $myname
Daniel
XcodeYangdeMBP2:~ xcodeyang$ bash
bash-3.2$ echo $myname
# <==这里并没有任何数据~因为这个变量是自定义变量(局部变量)
bash-3.2$ exit
exit
XcodeYangdeMBP2:~ xcodeyang$ export myname #修改成环境变量(全局变量)
XcodeYangdeMBP2:~ xcodeyang$ bash
bash-3.2$ echo $myname
Daniel
bash-3.2$ unset myname
bash-3.2$ echo $myname
bash-3.2$ exit
exit
XcodeYangdeMBP2:~ xcodeyang$ echo $myname
Daniel
XcodeYangdeMBP2:~ xcodeyang$
# 例子二
XcodeYangdeMBP2:~ xcodeyang$ echo $HOME
/Users/xcodeyang
XcodeYangdeMBP2:~ xcodeyang$ HOME=$HOME/home/bin
XcodeYangdeMBP2:xcodeyang xcodeyang$ echo $HOME
/Users/xcodeyang/home/bin
# myname=$mynameyes
# 如果没有双引号,name 的内容是 $nameyes 这个变量!
# tip:配错使用control+c取消继续,下面是正确的
# myname="$myname"yes
# myname=${myname}yes <==以此例较佳!
# 猜测一下,那个是对/错
myname="myname's name"
myname='myname's name'
myname=myname\'s\ name

1
2
3
4
5
6
7
8
9
XcodeYangdeMBP2:~ xcodeyang$ echo $name
yzp
XcodeYangdeMBP2:~ xcodeyang$ name="$name is daniel"
XcodeYangdeMBP2:~ xcodeyang$ echo $name
yzp is daniel
XcodeYangdeMBP2:~ xcodeyang$ name='$name is daniel'
XcodeYangdeMBP2:~ xcodeyang$ echo $name
$name is daniel
XcodeYangdeMBP2:~ xcodeyang$

环境变量的功能: env 与常见环境变量说明, set, export

bash 可不只有环境变量喔,还有一些与 bash 操作接口有关的变量,以及用户自己定义的变量存在的. set 除了环境变量之外, 还会将其他在 bash 内的变量通通显示出来哩!信息太多

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
XcodeYangdeMBP2:~ xcodeyang$ newname=daniel
XcodeYangdeMBP2:~ xcodeyang$ name=xcodeyang
XcodeYangdeMBP2:~ xcodeyang$ export name
XcodeYangdeMBP2:~ xcodeyang$ env #<==看这里
TERM_PROGRAM=Apple_Terminal #使用{大写的字母}来配置的变量一般为系统内定需要的变量
SHELL=/bin/bash
TERM=xterm-256color
TMPDIR=/var/folders/1c/hw10tv792_92fz5cswgmzqmc0000gn/T/
Apple_PubSub_Socket_Render=/private/tmp/com.apple.launchd.Zcy3n3bgYL/Render
TERM_PROGRAM_VERSION=361.1
TERM_SESSION_ID=A7EE49FA-7C48-49A4-BA91-8144FACFB146
name=xcodeyang #<==看这里
USER=xcodeyang
SSH_AUTH_SOCK=/private/tmp/com.apple.launchd.Sey2Soe8fS/Listeners
__CF_USER_TEXT_ENCODING=0x1F5:0x0:0x0
PATH=/Users/xcodeyang/.rbenv/shims:/Users/xcodeyang/.rbenv/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/git/bin
PWD=/Users/xcodeyang
XPC_FLAGS=0x0
RBENV_SHELL=bash
XPC_SERVICE_NAME=0
SHLVL=1
HOME=/Users/xcodeyang
LOGNAME=xcodeyang
LC_CTYPE=UTF-8
_=/usr/bin/env
XcodeYangdeMBP2:~ xcodeyang$

变量的有效范围

环境变量=全局变量
自定义变量=局部变量

为什么环境变量的数据可以被子程序所引用呢?这是因为内存配置的关系!理论上是这样的:

  • 当启动一个 shell,操作系统会分配一记忆区块给 shell 使用,此内存内之变量可让子程序取用
  • 若在父程序利用 export 功能,可以让自定义变量的内容写到上述的记忆区块当中(环境变量);
  • 当加载另一个 shell 时 (亦即启动子程序,而离开原本的父程序了),子 shell 可以将父 shell 的环境变量所在的记忆区块导入自己的环境变量区块当中。

变量键盘读取、数组与宣告: read, declare, array

我们上面提到的变量配置功能,都是由命令列直接配置的,那么,可不可以让用户能够经由键盘输入? 什么意思呢?是否记得某些程序运行的过程当中,会等待使用者输入 “yes/no” 之类的信息啊?

read

1
2
3
4
5
6
7
8
9
10
11
12
13
14
XcodeYangdeMBP2:~ xcodeyang$ echo $name
zhipingyang
XcodeYangdeMBP2:~ xcodeyang$ read name
%^$$uhda asuh_uha!@#~ #关键是没有之前那样的语法格式限制
XcodeYangdeMBP2:~ xcodeyang$ echo $name
%^$$uhda asuh_uha!@#~
XcodeYangdeMBP2:~ xcodeyang$
#-p :后面可以接提示字符!
#-t :后面可以接等待的『秒数!』这个比较有趣~不会一直等待使用者啦!
XcodeYangdeMBP2:~ xcodeyang$ read -p "Please keyin your age: " -t 30 agenum
Please keyin your age: 33
XcodeYangdeMBP2:~ xcodeyang$ echo $agenum
33
XcodeYangdeMBP2:~ xcodeyang$

declare / typeset

declare 或 typeset 是一样的功能,就是在『宣告变量的类型』

-a :将后面名为 variable 的变量定义成为数组 (array) 类型
-i :将后面名为 variable 的变量定义成为整数数字 (integer) 类型
-x :用法与 export 一样,就是将后面的 variable 变成环境变量;
-r :将变量配置成为 readonly 类型,该变量不可被更改内容,也不能 unset

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
#示例1:
XcodeYangdeMBP2:~ xcodeyang$ sum=10+20+30
XcodeYangdeMBP2:~ xcodeyang$ echo $sum
10+20+30 #<==默认类型是string
XcodeYangdeMBP2:~ xcodeyang$ declare -i sum=10+20+30
XcodeYangdeMBP2:~ xcodeyang$ echo $sum
60
XcodeYangdeMBP2:~ xcodeyang$ declare -i sum=1/3
XcodeYangdeMBP2:~ xcodeyang$ echo $sum
0 # <==bash 环境中的数值运算,默认最多仅能到达整数形态
XcodeYangdeMBP2:~ xcodeyang$
#示例2

XcodeYangdeMBP2:~ xcodeyang$ declare -ixr sum=20+30 #只读整型环境变量
XcodeYangdeMBP2:~ xcodeyang$ bash
bash-3.2$ echo $sum
50 #子程序里依然可以访问
bash-3.2$ exit
exit
XcodeYangdeMBP2:~ xcodeyang$ sum=30
-bash: sum: readonly variable #不可修改
XcodeYangdeMBP2:~ xcodeyang$
#示例3: 建议直接以 ${数组} 的方式来读取
XcodeYangdeMBP2:~ xcodeyang$ var[1]=$SHELL
XcodeYangdeMBP2:~ xcodeyang$ var[2]=$sum
XcodeYangdeMBP2:~ xcodeyang$ var[3]="helloworld"
XcodeYangdeMBP2:~ xcodeyang$ echo "${var[1]}, ${var[2]}, ${var[3]}"
/bin/bash, 50, helloworld
XcodeYangdeMBP2:~ xcodeyang$

移动周分享-第40期

linux 重定向命令

1. 什么是数据流重定向

  • Linux 默认提供了三个特殊设备,用于终端的显示和输出,分别为

    1. stdin(标准输入,对应于你在终端的输入)
    2. stdout(标准输出,对应于终端的输出)
    3. stderr(标准错误输出,对应于终端的输出)
  • 任何一条linux 命令执行,它会是这样一个过程:

一个命令执行了:

先有一个输入:输入可以从键盘,也可以从文件得到

命令执行完成:成功了,会把成功结果输出到屏幕:standard output默认是屏幕

命令执行有错误:会把错误也输出到屏幕上面:standard error默认也是指的屏幕

2.输出重定向

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
>:以覆盖的方法将『正确的数据』输出到指定的文件或装置上

1> :以覆盖的方法将『正确的数据』输出到指定的文件或装置上

1>>:以累加的方法将『正确的数据』输出到指定的文件或装置上

2> :以覆盖的方法将『错误的数据』输出到指定的文件或装置上

2>>:以累加的方法将『错误的数据』输出到指定的文件或装置上

&> :以覆盖的方法将『正确和错误的数据』输出到指定的文件或装置上

&>>:以累加的方法将『正确和错误的数据』输出到指定的文件或装置上

> sus.txt 2> err.txt:以覆盖的方法将『正确的数据』输出到sus.txt,将『错误的数据』输出到err.txt

>> sus.txt 2>> err.txt:以累加的方法将『正确的数据』输出到sus.txt,将『错误的数据』输出到err.txt

eg:

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

Administrator@jungou-PC /cygdrive/d/20151221
$ ls

#以覆盖的方法将『正确的数据』输出到指定的文件或装置上
Administrator@jungou-PC /cygdrive/d/20151221
$ echo "new" > 1.txt

Administrator@jungou-PC /cygdrive/d/20151221
$ ls
1.txt

Administrator@jungou-PC /cygdrive/d/20151221
$ cat 1.txt
new

#以累加的方法将『正确的数据』输出到指定的文件或装置上
Administrator@jungou-PC /cygdrive/d/20151221
$ echo "add" >> 1.txt

Administrator@jungou-PC /cygdrive/d/20151221
$ cat 1.txt
new
add

Administrator@jungou-PC /cygdrive/d/20151221
$ ech 2.txt
-bash: ech: 未找到命令

#以覆盖的方法将『错误的数据』输出到指定的文件或装置上
Administrator@jungou-PC /cygdrive/d/20151221
$ ech 2> 2.txt

Administrator@jungou-PC /cygdrive/d/20151221
$ cat 2.txt
-bash: ech: 未找到命令

#以累加的方法将『错误的数据』输出到指定的文件或装置上
Administrator@jungou-PC /cygdrive/d/20151221
$ ech 2>> 2.txt

Administrator@jungou-PC /cygdrive/d/20151221
$ cat 2.txt
-bash: ech: 未找到命令
-bash: ech: 未找到命令

#以覆盖的方法将『正确的数据』输出到sus.txt,将『错误的数据』输出到err.txt
Administrator@jungou-PC /cygdrive/d/20151221
$ echo "all" > sus.txt 2> err.txt
#以累加的方法将『正确的数据』输出到sus.txt,将『错误的数据』输出到err.txt
Administrator@jungou-PC /cygdrive/d/20151221
$ eco "add" >> sus.txt 2>> err.txt

Administrator@jungou-PC /cygdrive/d/20151221
$ cat sus.txt
all

Administrator@jungou-PC /cygdrive/d/20151221
$ cat err.txt
-bash: eco: 未找到命令

Administrator@jungou-PC /cygdrive/d/20151221
$

3.输入重定向

  • 在标准输入中,<代表将原来需要由键盘输入的数据改由文件内容来替代,<<则代表结束输入。
1
2
3
4
5
6
7
8
9
10
11
   Administrator@jungou-PC /cygdrive/d/20151221
$ cat input.txt
1
2
3
#这里按下[ctrl+d]来离开
Administrator@jungou-PC /cygdrive/d/20151221
$ cat input.txt
1
2
3

Android 测试库

Android的测试支持库为测试Android应用提供了大量框架。该库提供了一组API快速构建和运行测试代码,包括JUnit4和功能用户界面(UI)测试。可以从Android Studio IDE中或命令行这执行。

Android的测试支持库可通过Android SDK管理器获取。参考: Testing Support Library Setup 。

AndroidJUnitRunner

AndroidJUnitRunner类是一个JUnit测试运行器,可以让你在Android设备上执行包括Espresso和UI Automatorr测试框架运行JUnit3或JUnit4中风格的测试类。测试运行器手柄测试加载测试包和应用,运行测试,并报告测试结果。该类取代InstrumentationTestRunner类(仅支持JUnit 3)。

这个运行器的主要特点:

  • JUnit支持

  • 获得设备信息

  • 测试筛选

  • 测试分片

要求的Android2.2(API 8)或更高。

Espresso

Espresso提供了一组API来构建UI测以测试用户流程。这些API让你写简洁和可靠运行的自动化UI测试。Espresso非常适合白盒自动测试,在测试代码利用实现代码的细节从应用程序测试。

Espresso的主要特性:

  • 灵活的API用于查看和适配目标应用。

  • 一套扩展的action API自动化UI交互。

  • UI线程同步以提高测试的可靠性。欲了解更多信息,请参见UI线程同步。

要求Android2.2(API 8)或更高。

UI Automator

UI Automator提供了一组API来构建基于交互UI的测试。API允许你执行操作,如打开设置菜单,非常适合黑盒自动化测试,在测试代码不依赖于应用的内部实现。

主要特性:

  • UI Automator Viewer:检查的布局层次。

  • API来获取设备状态信息并执行操作。欲了解更多信息,请参见访问设备状态。

  • API跨应用测试。

要求Android4.3(API等级18)或者更高。

uiautomatorviewer提供了一个方便的图形用户界面进行扫描和分析在Android设备上当前显示的UI组件。您可以使用此工具来检查的布局层次和查看UI组件。

UiDevice类可以访问设备并进行操作。你可以调用它的方法来访问设备属性,如当前的方向或显示尺寸。该UiDevice类也让您执行操作,例如:旋转设备;按下D-pad按钮;按Back、Home、Menu等;打开通知树栏;当前窗口截图等。

多应用相关的API: UiCollection枚举容器的UI元素以计数,或通过文字(或属性等)针定位子元素; UIObject表示是在设备上可见的UI元素; UiScrollable?:为可滚动UI容器提供查找支持; UiSelector?:查询一个或者多个UI元素; Configurator: 设置参数。

JUnit4 Annotations常用注释

JUnit4的测试类不用再继承TestCase类了。使用注解会方便很多。

@Before:初始化方法

@After:释放资源

@Test:测试方法,在这里可以测试期望异常和超时时间

@Ignore:忽略的测试方法

@BeforeClass:针对所有测试,只执行一次,且必须为static void

@AfterClass:针对所有测试,只执行一次,且必须为static void

一个JUnit 4 的单元测试用例执行顺序为:

@BeforeClass –> @Before –> @Test –> @After –> @AfterClass

每一个测试方法的调用顺序为:

@Before –> @Test –> @After

app推送架构设计之全局观——xpleemoon

app推送架构设计之全局观

  • BroadcastReceiver只负责显示Notification,而Service才是推送消息的实际执行者,那为何要如此设计呢?
    • BroadcastReceiver生命周期最多只有8秒,超过8秒会造成ANR,而Service则不存在这个问题。很明显,BroadcastReceiver天生就是不适合处理复杂操作的组件。因此,考虑到未来推送的复杂性和扩展性,上图的具体处理流程将是:
      1. BroadcastReceiver接收push消息
      2. 显示Notification
      3. 点击Notification(Notification包含的PendingIntent用于启动Service),启动Service
      4. Service解析Notification传递过来的Intent,执行具体操作(可通过策略模式,让代码不要那么烂),比如打开一个页面、执行网络请求、执行本地操作……
    • Notification上按钮的点击是无法自动取消Notification,因此,Service还用于取消Notification
    • 显示和执行分开,同时执行通过策略模式进行。这样的好处是便于维护和降低代码耦合

express.js 入门介绍 - 曾铭

三个 Demo

  • node.js 的 hello world
  • express.js app 的 hello app
  • 一个完整的 express.js 项目结构
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
➜  myapp  tree -L 2
.
├── app.js
├── bin
│ └── www
├── hello_app.js
├── helloworld.js
├── node_modules
│ ├── ...
├── package.json
├── public
│ ├── images
│ ├── javascripts
│ └── stylesheets
├── routes
│ ├── index.js
│ └── users.js
└── views
├── error.jade
├── index.jade
└── layout.jade

weui - 王胜

WeUI 为微信 Web 服务量身设计

WeUI是一套同微信原生视觉体验一致的基础样式库,由微信官方设计团队为微信 Web 开发量身设计,可以令用户的使用感知更加统一。包含button、cell、dialog、 progress、 toast、article、icon等各式元素。

查看演示实例

1
2
3
4
5
git clone https://github.com/weui/weui.git
cd weui
npm install -g gulp
npm install
gulp -ws

运行gulp -ws命令,会监听src目录下所有文件的变更,并且默认会在8080端口启动服务器,然后在浏览器打开 http://localhost:8080/example。

参考 github-weui

周精益分享 - 前端入门篇2期

博客地址

Swift柯里化 Curring 学习

什么是Curring

维基百科解释
在计算机科学中,柯里化(英语:Currying),又译为卡瑞化或加里化,是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。

概念看上去还是有点抽象,直接看代码

例子
注:所有swift代码都可以放到playgroun中运行查看输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import UIKit

// 一个普通求sum函数
func add(a: Int, b: Int, c: Int) -> Int{
print("\(a) + \(b) + \(c) = \(a + b + c)")
return a + b + c
}

add(1, b: 2,c: 3) // 打印1 + 2 + 3 = 6

// 柯里化版本的求sum函数
func addCur(a: Int)(b: Int)(c: Int) -> Int{
print("\(a) + \(b) + \(c) = \(a + b + c)")
return a + b + c
}

addCur(1)(b: 2)(c: 3) // 打印1 + 2 + 3 = 6

你也许觉得Curring只是用一个特殊的写法
其他与普通函数无异
那么换一种curring的调用方式

1
2
3
4
5
6
7
8
9
10
// curring 另类调用方式
let funcWithA = addCur(1)
print("funcWithA type : \(funcWithA.dynamicType)")
let funcWithAB = funcWithA(b: 2)
print("funcWithAB type : \(funcWithAB.dynamicType)")
let resultCurring = funcWithAB(c: 3)
print("resultCurring type : \(resultCurring.dynamicType)")
```

此时你还是会觉得这只是把一个函数拆开来调用了 那么我们打印一下 每个调用步骤中变量的类型

let funcWithA = addCur(1)
print(“funcWithA type : (funcWithA.dynamicType)”) //
let funcWithAB = funcWithA(b: 2)
print(“funcWithAB type : (funcWithAB.dynamicType)”)
let resultCurring = funcWithAB(c: 3)
print(“resultCurring type : (resultCurring.dynamicType)”)

1
2

打印结果为

1 + 2 + 3 = 6
funcWithA type : Int -> Int -> Int
funcWithAB type : Int -> Int
1 + 2 + 3 = 6
resultCurring type : Int

1
2
3
4
5
6
7
8
9
10
11
12
13

可以看到 变量funcWithA 为 Int -> Int -> Int 类型
funcWithAB 为Int -> Int

这说明 curring函数在绑定最后一个参数之前 每个步骤返回的值都是一个函数

###Curring实现原理

Swift实现Curring的基础有两个
- 函数是一级公民
- 闭包

[啊崢的Swift Curring文章](http://www.jianshu.com/p/6eaacadafa1a)代码例子解释的已经很清楚了

class Currying
{
/ uncurried:普通函数 /
// 接收多个参数的函数
func add(a: Int, b: Int, c: Int) -> Int{
println(“(a) + (b) + (c)”)
return a + b + c
}

/*** 手动实现柯里化函数 ***/
// 把上面的函数转换为柯里化函数,首先转成接收第一个参数a,并且返回接收余下第一个参数b的新函数(采用闭包)
// 为了让大家都能看懂,我帮你们拆解来看下
// (a: Int) : 参数
// (b:Int) -> (c: Int) -> Int : 函数返回值(一个接收参数b的函数,并且这个函数又返回一个接收参数c,返回值为Int类型的函数)

// 定义一个接收参数a,并且返回一个接收参数b的函数,并且这个函数又返回一个接收参数c,返回值为Int类型的函数
func add(a: Int) -> (b:Int) -> (c: Int) -> Int{

    // 一个接收参数b的函数,并且这个函数又返回一个接收参数c,返回值为Int类型的函数
    return { (b:Int) -> (c: Int) -> Int in

        // 返回一个接收余下第一个参数c,并且有返回结果为Int类型的函数
        return { (c: Int) -> Int in

            return a + b + c;

 注解: 这里为什么能使用参数a,b,c?
       利用闭包的值捕获特性,即使这些值作用域不在了,也可以捕获到他们的值。
       闭包会自动判断捕获的值是值拷贝还是值引用,如果修改了,就是值引用,否则值拷贝。

       注意只有在闭包中才可以,a,b,c都在闭包中。

        }

    }

}


/*** curried: 系统自带的柯里化函数 ***/
func addCur(a: Int)(b: Int)(c: Int) -> Int{
    println("\(a) + \(b) + \(c)")
    return a + b + c
}

}

1
2
3
4

###OC版的Curring

大部分的iOS程序员都还是习惯写OC 找了一个[OC版本的Curring](https://gist.github.com/lukhnos/1771842)实现 我们可以理解的更深刻

include

int f(int x, int y)
{
return x + y;
}

int main()
{
// 闭包1 绑定第一个参数
typedef int (^int_to_int_t)(int);
// 闭包2 绑定第二个参数
typedef int_to_int_t (^int_to_int_to_int_t)(int);

int_to_int_to_int_t h = ^(int x) {
    int_to_int_t g = ^(int y) {
        // 将两个闭包持有的参数做处理 返回结果
        return f(x, y);  
    };

    return g;        
};

// 第一个闭包持有 5
int_to_int_t g = h(5);

int z;
// 第二个闭包持有10 并返回结果
z = g(10);
printf("%d\n", z);

}

1
2
3
4
5
6
7
8
9
10
11
12

###什么?你觉得然并卵!

你一定觉得这是一个然并卵的东西

好吧 我第一个看到的时候 只是觉得有趣 其实也没有想到较好的应用场景

[为什么要柯里化](https://gist.github.com/jcouyang/b56a830cd55bd230049f)这篇文章提供的例子给了很多启发

用Swift重新实现了一遍 又加了一点打印 豁然开朗

上代码

// 拼接字符
func concat(chars: Array) {
var resultString = “”
for char in chars {
resultString += char
}
print(resultString)
}

concat([“1”,”2”,”3”])
print(“**“)

// 每个字符代表的数字 + 1 后再拼接
func concatAdd(chars: Array, addNum: Int) {
var resultString = “”
for char in chars {
var num = Int(char)!
num = num + addNum
let newChar = String(num)
resultString += newChar
}
print(resultString)
}

concatAdd([“1”,”2”,”3”], addNum: 1)
concatAdd([“1”,”2”,”3”], addNum: 2)
concatAdd([“1”,”2”,”3”], addNum: 3)

print(“**“)
// 每个字符代表的数字 10 后再拼接
func concatMultiply(chars: Array, multiplyNum: Int) {
var resultString = “”
for char in chars {
var num = Int(char)!
num = num
multiplyNum
let newChar = String(num)
resultString += newChar
}
print(resultString)
}

concatMultiply([“1”,”2”,”3”], multiplyNum: 1)
concatMultiply([“1”,”2”,”3”], multiplyNum: 2)
concatMultiply([“1”,”2”,”3”], multiplyNum: 3)

print(“**“)
/
让我们使用柯里化吧
*
/
func add(a: Int)(b: Int) -> Int{
return a + b
}

func multiply(a: Int)(b: Int) -> Int{
return a * b
}

func concatByCurring(chars: Array)(caculateFunc: (Int)->Int) {
var resultString = “”
for char in chars {
var num = Int(char)!
var newNum = caculateFunc(num)
let newChar = String(newNum)
resultString += newChar
}
print(resultString)
}

concatByCurring([“1”,”2”,”3”])(caculateFunc: add(1))
concatByCurring([“1”,”2”,”3”])(caculateFunc: add(2))
concatByCurring([“1”,”2”,”3”])(caculateFunc: add(3))

print(“**“)
concatByCurring([“1”,”2”,”3”])(caculateFunc: multiply(1))
concatByCurring([“1”,”2”,”3”])(caculateFunc: multiply(2))
concatByCurring([“1”,”2”,”3”])(caculateFunc: multiply(3))

1
2
3
4
5
6
7
8
9
10
11
12
13
14

以上的代码用普通的函数 和 Curring函数 处理了同一个功能

Curring优势明显
- 重用了拼接代码
- 支持更为灵活的算法替换
- 写功能扩展的人不用怎么关心老代码的实现

###iOS开发中可以应用的场景

**使用Curring处理selector**
这个例子是我在看[王巍的Swifter](https://selfstore.io/~onevcat)时候看到的一个例子

不废话 直接上代码

// 打印func类型
class People: NSObject {
func speak(){
print(“hello”)
}

func printSpeak() {
    print("People func speak  : \(People.speak.dynamicType)")
}

}

People().printSpeak()
// 打印结果为 People func speak : People -> () -> ()
// 可见实例的方法就是一个Curring结构

1
2

下面是[Instance Methods are Curried Functions in Swift](http://oleb.net/blog/2014/07/swift-instance-methods-curried-functions/?utm_campaign=iOS_Dev_Weekly_Issue_157&utm_medium=email&utm_source=iOS%2BDev%2BWeekly)中的例子 很开脑洞

//Selector demo
protocol TargetAction {
func performAction()
}

struct TargetActionWrapper : TargetAction {
weak var target: T?

// 此处的action类型是不是和我上面代码中打印的结构一致
let action: (T) -> () -> ()

func performAction() -> () {
    if let t = target {
        // Curring调用
        action(t)()
    }
}

}

enum ControlEvent {
case TouchUpInside
case ValueChanged
}

class Control {
var actions = ControlEvent: TargetAction

func setTarget<T: AnyObject>(target: T, action: (T) -> () -> (), controlEvent: ControlEvent) {
    actions[controlEvent] = TargetActionWrapper(target: target, action: action)
}

func removeTargetForControlEvent(controlEvent: ControlEvent) {
    actions[controlEvent] = nil
}

func performActionForControlEvent(controlEvent: ControlEvent) {
    actions[controlEvent]?.performAction()
}

}

class MyViewController {
let button = Control()

func viewDidLoad() {
    button.setTarget(self, action: MyViewController.onButtonTap, controlEvent: .TouchUpInside)
}

func onButtonTap() {
    print("Button was tapped")
}

}

// 调用
MyViewController().onButtonTap()

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

###小结
- Curring让计算过程更清晰 函数式编程的有点初见端倪
- Curring让计算过程更独立 可重用
- Curring让计算功能扩展更方便
- Curring让Swift中Selector的重构可以实现



## Umeng分享遇到的小坑 -- 张超耀
### 写在前面的话
- 在iOS9下,系统默认会拦截对http协议接口的访问,因此无法获取http协议接口的数据。对Umeng来说,具体表现可能是,无法授权、分享、获取用户信息等。
- iOS9新建项目默认需要支持bitcode,而不支持bitcode的SDK会导致无法编译运行。[App Thining](https://developer.apple.com/library/ios/documentation/IDEs/Conceptual/AppDistributionGuide/AppThinning/AppThinning.html#//apple_ref/doc/uid/TP40012582-CH35))

[Umeng适配iOS9](http://dev.umeng.com/social/ios/ios9)

### 针对iOS9的Https特性,解决方案
- Plan A: 暂时退回到http协议:

>**1**、在项目的info.plist中添加一个Key:NSAppTransportSecurity,类型为字典类型。
>**2**、然后给它添加一个Key:NSAllowsArbitraryLoads,类型为Boolean类型,值为YES;

- Plan B:设置域。可以简单理解成,把不支持https协议的接口设置成http的接口


>**1**、在项目的info.plist中添加一个Key:NSAppTransportSecurity,类型为字典类型。

>**2**、然后给它添加一个NSExceptionDomains,类型为字典类型;

>**3**、把需要的支持的域添加給NSExceptionDomains。其中域作为Key,类型为字典类型。

>**4**、每个域下面需要设置3个属性:NSIncludesSubdomains、 NSExceptionRequiresForwardSecrecy、 NSExceptionAllowsInsecureHTTPLoads。
均为Boolean类型,值分别为YES、NO、YES。

### 针对App Thinning

### 添加Scheme白名单实现应用跳转(SSO等)
- 问题描述:在iOS 9下涉及到平台客户端跳转,系统会自动到项目info.plist下检测是否设置平台Scheme。对于需要配置的平台,如果没有配置,就无法正常跳转平台客户端。因此要支持客户端的分享和授权等,需要配置Scheme名单。
- 解决方案:

具体方法:
>1、在项目的info.plist中添加一LSApplicationQueriesSchemes,类型为Array。

>2、然后给它添加一个需要支持的项目,类型为字符串类型


### 注意
- 由于苹果审核政策需求,需要对未安装客户端平台进行隐藏,在设置QQ、微信AppID之后调用下面的方法,

[UMSocialConfig hiddenNotInstallPlatforms:@[UMShareToQQ, UMShareToQzone, UMShareToWechatSession, UMShareToWechatTimeline]];

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
 - but  这个接口只对默认分享面板平台有隐藏功能,自定义分享面板或登录按钮需要自己处理

- 对于自定义分享面板处理:
- UmengSDK已经嵌入相关API,直接用就好

### 只要按照官方文档来,基本上就能马到成功()



##MarkDown 的 CSS 实现配置 - 杨志平

####题目灵感来源
> 起源于我们现有的博客引擎主题交互很不错,但是排版烂的要死 ,我水平有限这里只是浅显介绍实现修改我们的markdown编译器的一些排版样式

#### 研究方向
> 自定义一个非标准化,有其他多元素的MarkDown解析器 如下几点:

- 可选框
`- [ ] `
- 本地图片索引,可控制对齐及大小
`![Alt text](http://path/to/img.jpg "optional title" 100x200)`
`![Alt text](./1449756974449.png)`
- 标签功能
`@(Share)[css, Markdown]`
- 代码高亮(不同语言)

`swift`

``` swift
private var majorModel = PickMajorModel()
private var subjectModel = OFFKeyNameModel()
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}

objectivec

1
2
3
UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, kScreenW, kScreenH)];
scrollView.showsVerticalScrollIndicator = NO;
[scrollView addSubview:self.downMainView];

有一种惨不忍睹的即视感
Alt text

MarkDown来源及实现

Markdown is a plain text format for writing structured documents, based on conventions used for indicating formatting in email and usenet posts. It was developed in 2004 by John Gruber, who wrote the first markdown-to-html converter in Perl, and it soon became widely used in websites. By 2014 there were dozens of implementations in many languages.

见知乎上回答
实现一个markdown解析器需要具备那些知识

如何简单的改善文字编排的效果

更换博客主题(简单粗暴)

我们使用的博客引擎Hexo来举例,列举下面三个主题

  • landscape
  • hexo-theme-vno-master
  • hexo-theme-yilia-master
更换MarkDown编译器的主题

Mou 举例子,它提供了多套markdown语法下的排版样式
手动新创建一个CSS文本布局配置 Blog
Alt text

CSS配置文件修改

详细配置参数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
html { font-size: 62.5%; }
html, body { height: 100%; }

body {
font-family: Helvetica, Arial, sans-serif;
font-size: 150%;
line-height: 1.3;
color: #f6e6cc;
width: 700px;
margin: auto;
background: #27221a;
position: relative;
padding: 0 30px;
}

多级标题配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
h1 {
font-size: 28px;
color: black; }


h2 {
font-size: 24px;
border-bottom: 1px solid #cccccc;
color: black; }


h3 {
font-size: 18px; }


h4 {
font-size: 16px; }


h5 {
font-size: 14px; }


h6 {
color: #777777;
font-size: 14px; }

表格

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
table {
padding: 0;border-collapse: collapse; }

table tr {
border-top: 1px solid #cccccc;
background-color: white;
margin: 0;
padding: 0; }

table tr:nth-child(2n) {
background-color: #f8f8f8
; }

table tr th {
font-weight: bold;
border: 1px solid #cccccc;
margin: 0;
padding: 6px 13px; }

table tr td {
border: 1px solid #cccccc;
margin: 0;
padding: 6px 13px; }

table tr th :first-child, table tr td :first-child {
margin-top: 0
; }

table tr th :last-child, table tr td :last-child {
margin-bottom: 0
; }

代码高亮

1
2
3
4
5
6
7
8
9
10
11
12
13
14
code, tt {
margin: 0 2px;
padding: 0 5px;
white-space: nowrap;
border: 1px solid #eaeaea;
background-color: #f8f8f8;
border-radius: 3px; }


pre code {
margin: 0;
padding: 0;
white-space: pre;
border: none;
background: transparent; }

周精益分享 - 前端入门篇

Ruby on Rails 初体验 - 王胜

使用命令行快速创建应用的步骤

  • 创建应用程序

    1
    $rails new hello -d mysql

    备注 这里演示使用mysql作为数据持久层

  • 浏览hello world界面

    1
    $cd hello
    $rake db:create RAILS_ENV='development' // 创建开发数据库
    $bin/rails server

    备注 由于这里的rails版本是4.2.1,与msqyl2的最高版本有不兼容行,所以需要手动将Gemfile中mysql2降级。降级之后执行bundle install。

    1
    gem 'mysql2' -> gem 'mysql2', '~> 0.3.18'
  • 使用脚手架,创建模型、数据迁移脚本以及控制器和试图

    1
    $bin/rails generate scaffold Person name:string age:integer
  • 执行数据迁移

    1
    $bin/rake db:migrate RAILS_ENV=development
  • 启动应用

    1
    $bin/rails server
  • 浏览效果

    1
    http://localhost:3000/people

备注

此演示项目,ruby、gems和Rails的版本信息如下:

  • ruby: 2.0.0p481
  • rubygems: 2.4.5
  • rails: 4.2.1

参考:

Express for Node.js 上手演示 - 曾铭 (先占坑)

矢量图在Android 上应用 - 王进

VectorDrawable

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
<!-- intrinsic size of the drawable -->

android:height="300dp"
android:width="300dp"
<!-- size of the virtual canvas -->
android:viewportHeight="40"
android:viewportWidth="40"
>
<path android:fillColor="#ff00ff"
android:pathData="M20.5,9.5
c-1.955,0,-3.83,1.268,-4.5,3
c-0.67,-1.732,-2.547,-3,-4.5,-3
C8.957,9.5,7,11.432,7,14
c0,3.53,3.793,6.257,9,11.5
c5.207,-5.242,9,-7.97,9,-11.5
C25,11.432,23.043,9.5,20.5,9.5z"

/>

</vector>

SVG→Vector

Android并不原生支持读取常见的向量文件格式,如SVG
(主要是缺少css支持),但是支持相同路径数据语法,
所以SVG资源需要经过一定的转换;

使用SVG Path Data

  • M: move to 移动绘制点
  • L:line to 直线
  • Z:close 闭合
  • C:cubic bezier 三次贝塞尔曲线
  • Q:quatratic bezier 二次贝塞尔曲线
  • A:ellipse 圆弧

每个命令都有大小写形式,大写代表后面的参数是绝对坐标,小写表示相对坐标。参数之间用空格或逗号隔开

命令详解:

  • M (x y) 移动到x,y
  • L (x y) 直线连到x,y,还有简化命令H(x) 水平连接、V(y)垂直连接
  • Z,没有参数,连接起点和终点
  • C(x1 y1 x2 y2 x y),控制点x1,y1 x2,y2,终点x,y
  • Q(x1 y1 x y),控制点x1,y1,终点x,y
  • A(rx ry x-axis-rotation large-arc-flag sweep-flag x y)
    • rx ry 椭圆半径
    • x-axis-rotation x轴旋转角度
    • large-arc-flag 为0时表示取小弧度,1时取大弧度
    • sweep-flag 0取逆时针方向,1取顺时针方向

详细参考:http://www.w3.org/TR/SVG11/paths.html#PathData

Icon Font

解决矢量图低版本不兼容问题

详细参考:
1,http://blog.csdn.net/a345017062/article/details/46455745
2,http://blog.csdn.net/goodlixueyong/article/details/41448483

WordPress快速建站 —杨俊构

准备工具

操作步骤

  • 配置服务器和数据库

    1. 解压UPUPW_NP7.0-1511.7z 到本地目录
    2. 打开解压包后的upupw.exe配置平台。
    3. 输入“S1”开启全部服务
    4. 打开地址 http://127.0.0.1/ 会看到UPUPWPHP探针页面
    5. 打开地址 http://127.0.0.1/pmd/ 配置mysql数据库(默认用户名密码都是root)
    6. 新建数据库,用户名,密码
    7. 打开地址 http://127.0.0.1/ 使用数据库连接测试连接数据库是否正常
  • 安装配置wordpress

    1. 解压wordpress-4.3.1-zh_CN.zip压缩包
    2. 将wordpress文件夹拷贝到UPUPW发布根目录/htdocs文件夹下
    3. 打来地址http://127.0.0.1/wordpress,根据开始安装
    4. 输入刚才配置的数据库名,用户名,密码,点击提交进行
    5. 设置站点标题,后台登陆用户名,后台密码,邮件地址完成安装
    6. 再次打开地址http://127.0.0.1/wordpress测试安装完成

WordPress相关资源

spring aop 简单介绍

51offer Android重构之Architecture - xpleemoon

历史

  • 我们的app经历了一次重构,在重构过程中将IDE切换到Android Studio,并且编译脚本从Ant切换到了Gradle。
  • 不介绍IDE和Gradle的优势,主要介绍重构后app的Architecture相关部分。

从0到1是一个开始,再从1到N又是另一个开始。

Architecture

  • 当前我们的结构主要为4层:
    1. app
    2. core
    3. lib
    4. model

project结构

  • app依赖core,而core依赖lib和model。随着以后的业务发展、产品迭代或者有更好的结构划分,会再做调整。

module依赖

app——展现层(presention)

  • 用于页面展现,主要包含MVP模式中的VP。我们对V采用接口的方式去定义,然后让Activity或者Fragment去实现V接口,这样我们就达到接口编程的目的,P只需要关注V接口即可。以注册中的地区列表页面为例

VP

  • V
1
2
3
4
5
6
7
8
9
/**
* 地区view,MVP-V
*/

public interface IRegionView {
/**
* 搜索更新
*/

void search2Update();
}
  • P
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
/**
* 国家地区,MVP-P
*/

public class RegionPresenter {
private List<Region> mRegions = OFRRegionHelper.getRegions();

/**
* 获取地区列表
*
* @return
*/

public List<Region> getRegions() {
return mRegions;
}

/**
* 搜索
*
* @param regionView
* @param searchRegion
*/

public void search(@NonNull IRegionView regionView, String searchRegion) {
mRegions.clear();
if (TextUtils.isEmpty(searchRegion)) {
mRegions.addAll(OFRRegionHelper.getRegions());
} else {
mRegions.addAll(OFRRegionHelper.searchRegions(searchRegion));
}
regionView.search2Update();
}
}
  • 另外,我们的app有太多的业务逻辑,所以我们也使用了比较多的策略模式,方便以后的扩展和维护。比如不同的国家,它在意向单展现时就有不同的表现:
    • 可以同时选择的最多意向单数量
    • 每个学校可以同时选择的最多专业数量
    • 意向单名字
  • 于是,我们就对此采用了策略模式

国家意向单策略图

  • 国家意向单策略接口
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
/**
* 国家策略
* <ul>
* <li>不同国家可选的意向单数量不同,具体见{@link #maxCount2Select()}</li>
* <li>不同国家的学校专业可选数量不同,具体见{@link #maxCountOfPerSchool2Select()}</li>
* </ul>
*/

public interface ICountryPurpose {
/**
* 国家可以同时选择的最多意向单数量
*
* @return
*/

int maxCount2Select();

/**
* 每个学校可以同时选择的最多专业数量
*
* @return
*/

int maxCountOfPerSchool2Select();

/**
* 意向单名字,目前可用名字为:
* <ol>
* <li>专业</li>
* <li>入学时间,当前只限日本</>
* </ol>
*
* @param ctx
* @return
*/

String getName(@NonNull Context ctx);
}

core

  • 核心层,主要用于应用相关的URL拼接、图片加载、网络请求、json解析、第三方平台、配置和工具。

lib

  • 通用库层,不依赖于应用的任何业务,方便快速的移植到其它项目.

model

  • 数据层,作为MVP的M,用于应用的数据模型.

简单HTML5 吴明

  • 烟火效果
  • 404动画
  • 爱心树
  • 开发ide storm
  • 目录结构
    • dist:第三库存放地方,以下为存放指定文件类型目录
      • css
      • js
      • doc
      • img
    • src:项目指定文件类型目录
      • css
      • js
      • doc
      • img
  • 简单代码
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
<!--html5网页声明-->
<!DOCTYPE html>
<html lang="en">
<head>
<title>mobile</title>
<meta charset="UTF-8">
<!-- 此处还可以插入其他meta,样式单等信息-->
<script type="text/javascript"></script>

</head>

<body>
<!--页面部分-->
<p>现实是残酷的,别做梦,从hello word开始吧!</p>
<canvas id="canvas">不支持html5</canvas>


<script type="text/javascript">
<!-- 获取内容中id为canvas的元素-->
var canvas = document.getElementById('canvas');
// 获取canvas的环境,
var ctx = canvas.getContext('2d');
// 填充的样式为红色
ctx.fillStyle = '#ff0000';
ctx.st// 画一个矩形,位置x,y,w,h
ctx.fillRect(0, 0, 600, 100);
</script>

</body>
</html>

周精益分享 - 后端开发

Swift学习之二 - 张超耀

深度理解”?” && “!”

1
Optional其实是个enum,里面有None和Some两种类型。 nil就是Optional.None, 非nil就是Optional.Some, 然后会通过Some(T)包装(wrap)原始值,这也是为什么在使用Optional的时候要拆包(从enum里取出来原始值)的原因。声明为Optional只需要在类型后面紧跟一个"?"即可
1
2
var strValue: String?  //?相当于下面这种写法的语法糖
var strValue1: Optional<Int>

一旦声明为Optional的,如果不显式的赋值就会有个默认值nil。判断一个Optional的值是否有值,可以用if来判断:

1
2
3
if (strValue != nil) {
//do sth with strValue
}

怎么使用Optional值呢?
在使用Optional值的时候只前面需要加上一个”?”,就是这么简单。

  • 这个”?”什么意思?:
    1、 询问是否响应后面这个方法,如果是nil值,也就是Optional.None,就不响应后面的方法,直接跳过,
    2、 如果有值,就是Optional.Some,可能就会拆包(unwrap),然后对拆包后的值执行后面的操作,比如
1
let hashValue = strValue?.hashValue
  • strValue是Optional的字符串,如果strValue是nil,则hashValue也为nil,如果strValue不为nil,hashValue就是strValue字符串的值

  • 到这里我们看到了?的两种使用场景:

    1.声明Optional值变量
    2.用在对Optional值操作中,用来判断是否能响应后面的操作
    
  • 对于Optional值,不能直接进行操作,否则会报错
1
let hashValue1 = strValue!.hashValue

上面错误提示需要拆包(unwrap)后才能得到值,然后才能对其操作,那怎么来拆包呢?拆包提到了几种方法,一种是Optional Binding

1
2
3
if let str = strValue {
let hashValue = str.hashValue
}

还有一种是在具体的操作前添加”!”符号。

1
let hashValue2 = strValue!.hashValue

这里的”!”表示我确定肯定这里的的strValue一定是非nil的,随便用吧,木事de 就像下面:

1
2
3
if (strValue != nil) {
let hashValue = strValue!.hashValue
}
1
2
{}里的strValue一定是非nil的,所以就能直接加上!,强制拆包(unwrap)并执行后面的操作。
当然如果不加判断,strValue不小心为nil的话,就会出错,crash掉。

51offer例子解释『隐式拆包的Optional』

1
这种是特殊的Optional,称为Implicitly Unwrapped Optionals, 直译就是隐式拆包的Optional,就等于说你每次对这种类型的值操作时,都会自动在操作前补上一个!进行拆包,然后在执行后面的操作,当然如果该值是nil,也一样会报错crash掉。
  • “!”也有两种使用场景
    1、强制对Optional值进行拆包(unwrap)
    2、声明Implicitly Unwrapped Optionals值,一般用于类中的属性
    

swift 断言

1
2
3
4
断言(Assertions)
Optionals可以让我们检测值是否存在。在某些情况下,如果某个值不存在或者没有提供特定的满足条件,代码不应该继续往下执行。
在这些情况下,可以使用触发断言来终止执行并提供调试。
断言是在运行时检测条件是否为true,如果为true,就继续往下执行,否则就在这里中断。
1
2
3
var jobs = "我是一个好人"
assert(jobs == "我是一个好人", "我是一个好人")
assert(jobs == "我是一个坏人", "我是一个坏人么?")
1
2
3
4
5
什么时候使用断言呢?
包含下面的情况时使用断言:
1、整型下标索引作为值传给自定义索引实现的参数时,但下标索引值不能太低也不能太高时,使用断言
**2、传值给函数但如果这个传过来的值无效时,函数就不能完成功能时,使用断言。
3、Optional值当前为nil,但是后面的代码成功执行的条件是要求这个值不能为nil,使用断言

IM中输入框的优化方案实现

以下图的输入框为例

stand

输入框的呈现方式选择

以下两个方法对比

  • Keyboard的inputAccessoryView

相对推荐的方法,有更好的丰富的交互效果

可以与scrollView的一些属性直接绑定

  • InputView的frame适应

传统的方法

优点:灵活控制显示位置

缺点:过多的计算frame

InputAccessoryView方式

案例见GitHub的Demo

iOS的输入源都有输入源(keyboard及keyboard的配件InputAccessoryView),那通常的方法是输入源控件(Textfield、TextView等)使用一般的InputView的frame适应

解释几个技巧点

  • InputView( 输入源 )的父视图作为该InputView的InputAccessoryView,要避免相互引用
  • 巧用第三方不可见的InputView在适当时间点转移第一响应者给可见的InputView
  • 完成编辑时去除第一响应者(注意iOS9下键盘响应逻辑视图层级都发生了变化)

主要代码

simulator screen shot nov 27 2015 4 17 38 pm

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
// 单例初始化
+ (UUInputAccessoryView*)sharedView {
static dispatch_once_t once;
static UUInputAccessoryView *sharedView;
dispatch_once(&once, ^ {
sharedView = [[UUInputAccessoryView alloc] init];
sharedView->btnBack = [UIButton buttonWithType:UIButtonTypeCustom];
sharedView->btnBack.frame = CGRectMake(0, 0, UUIAV_MAIN_W, UUIAV_MAIN_H);
[sharedView->btnBack addTarget:sharedView action:@selector(dismiss) forControlEvents:UIControlEventTouchUpInside];
sharedView->btnBack.backgroundColor=[UIColor clearColor];
UIToolbar *toolbar = [[UIToolbar alloc]initWithFrame:CGRectMake(0, 0, UUIAV_MAIN_W, 44)];
sharedView->inputView = [[UITextField alloc]initWithFrame:CGRectMake(UUIAV_Edge_Hori, UUIAV_Edge_Vert, UUIAV_MAIN_W-UUIAV_Btn_W-4*UUIAV_Edge_Hori, UUIAV_Btn_H)];
sharedView->inputView.borderStyle = UITextBorderStyleRoundedRect;
sharedView->inputView.returnKeyType = UIReturnKeyDone;
sharedView->inputView.clearButtonMode = UITextFieldViewModeWhileEditing;
sharedView->inputView.enablesReturnKeyAutomatically = YES;
sharedView->inputView.delegate = sharedView;
[toolbar addSubview:sharedView->inputView];
sharedView->assistView = [[UITextField alloc]init];
sharedView->assistView.delegate = sharedView;
sharedView->assistView.returnKeyType = UIReturnKeyDone;
sharedView->assistView.enablesReturnKeyAutomatically = YES;
[sharedView->btnBack addSubview:sharedView->assistView];
sharedView->assistView.inputAccessoryView = toolbar;
sharedView->BtnSave = [UIButton buttonWithType:UIButtonTypeCustom];
sharedView->BtnSave.frame = CGRectMake(UUIAV_MAIN_W-UUIAV_Btn_W-2*UUIAV_Edge_Hori, UUIAV_Edge_Vert, UUIAV_Btn_W, UUIAV_Btn_H);
sharedView->BtnSave.backgroundColor = [UIColor clearColor];
[sharedView->BtnSave setTitle:@"确定" forState:UIControlStateNormal];
[sharedView->BtnSave setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
[sharedView->BtnSave addTarget:sharedView action:@selector(Done) forControlEvents:UIControlEventTouchUpInside];
[toolbar addSubview:sharedView->BtnSave];
});
CGRectGetHeight([UIScreen mainScreen].bounds);
return sharedView;
}

实现逻辑代码

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
+ (void)showKeyboardType:(UIKeyboardType)type content:(NSString *)content Block:(UUInputAccessoryBlock)block
{
[[UUInputAccessoryView sharedView] show:block
keyboardType:type
content:content];
}

- (void)show:(UUInputAccessoryBlock)block keyboardType:(UIKeyboardType)type content:(NSString *)content
{
UIWindow *window=[UIApplication sharedApplication].keyWindow;
[window addSubview:btnBack];

inputBlock = block;
inputView.text = content;
inputView.keyboardType = type;
assistView.keyboardType = type;
[assistView becomeFirstResponder];
shouldDismiss = NO;

[[NSNotificationCenter defaultCenter] addObserverForName:UIKeyboardDidShowNotification
object:nil
queue:nil
usingBlock:^(NSNotification * _Nonnull note) {
if (!shouldDismiss) {
[inputView becomeFirstResponder];
}
}];
}

- (void)Done
{
[inputView resignFirstResponder];
!inputBlock ?: inputBlock(inputView.text);
[self dismiss];
}

- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
[self Done];
return NO;
}

- (void)dismiss
{
shouldDismiss = YES;
[inputView resignFirstResponder];
[btnBack removeFromSuperview];
[[NSNotificationCenter defaultCenter] removeObserver:self];
}

InputView的frame适应

案例见GitHub的Demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 键盘响应布局
@objc func keyboardFrameChanged(notification: NSNotification) {

let dict = NSDictionary(dictionary: notification.userInfo!)
let keyboardValue = dict.objectForKey(UIKeyboardFrameEndUserInfoKey) as! NSValue
let bottomDistance = mainScreenSize().height - keyboardValue.CGRectValue().origin.y
let duration = Double(dict.objectForKey(UIKeyboardAnimationDurationUserInfoKey) as! NSNumber)

UIView.animateWithDuration(duration, animations: {
self.inputViewConstraint!.constant = -bottomDistance
self.view.layoutIfNeeded()
}, completion: {
(value: Bool) in
self.chatTableView.scrollToBottom(animation: true)
})

}

ScrollView下拉动态修改keyboard(InputView)的frame

常见隐藏keyboard的一些方式

  • TouchBeigin
  • DidDrag
  • EndDrag
  • Interactive

iOS7 开始,ScrollView提供

1
2
3
4
5
6
@available(iOS 7.0, *)
public enum UIScrollViewKeyboardDismissMode : Int {
case None
case OnDrag // dismisses the keyboard when a drag begins
case Interactive // the keyboard follows the dragging touch off screen, and may be pulled upward again to cancel the dismiss
}

所以在ScrollView上添加scrollView.keyboardDismissMode = UIScrollViewKeyboardDismissMode.Interactive就可以了。
效果详见iOS7及以上原生设备的短信滑动消失键盘的交互

写负责任的代码之异常处理 - 曾铭

千万不要以为你可以忽视这个特殊的返回值,因为它是一种“可能性”。代码漏掉任何一种可能出现的情况,都可能产生意想不到的灾难性结果。

  • 重视代码(及业务)逻辑的异常情况,将其视为『正常流程』的一部分。
  • 重视函数的抛出异常,将其视为返回值之一(union类型)。

政策之后的对策

1
2
String foo() throws MyException {
}
  • catch 全部异常
  • try 全部代码
1
2
3
4
5
6
7
try {
...
...
... foo() ...
...
...
} catch (Exception e) {}

我们 API 为什么难调试

  • Service 层未声明抛出异常,也会抛出异常
  • 抛出的异常 API 层记入日志,向上抛时却丢弃了异常信息
  • API 层方法代码全部 try,很难定位到是哪一行出问题

推荐的对策

  • 只 catch 指定的 exception
  • try 进可能的小
1
2
3
4
5
try {
foo();
} catch (MyException e) {
Log.warning(e);
}
  • 源头思考全部可能性,内部的问题不抛给别人
  • 不要试图忽略(隐藏)错误,每次出错都是优化代码(反思自己局限)的好机会。否则,你终将付出代价。

周精益分享 - 动画

Android L动画 - 吴明

  • Touch feedback(触摸反馈)
    • 波纹有边界
    • 波纹超出边界
    • 波纹颜色
  • Reveal effect(揭露效果)
  • liecap
    • Circular Reveal动画
  • Activity transitions(Activity转换效果)
    • Enter(进入)
      • 普通Transition
        • explode:从场景的中心移入或移出
        • slide:从场景的边缘移入或移出
        • fade:调整透明度产生渐变效果
      • Shared Elements Transition 共享元素转换:共享两个 acitivity中共同的元素
        • changeBounds - 改变目标视图的布局边界
        • changeClipBounds - 裁剪目标视图边界
        • changeTransform - 改变目标视图的缩放比例和旋转角度 changeImageTransform - 改变目标图片的大小和缩放比例
    • Exit(退出)
    • demo地址
      Markdown preferences pane

objectorAnimator 动画 王进

动画类型

  • View Animation(Tween Animation 补间动画)
    只能支持简单的缩放、平移、旋转、透明度等基本的动画,且有一定的局限性
    动画时View的真正的View的属性保持不变,实际位置未改变
    原理:提供动画的起始和结束状态信息,中间的状态根据上述类里差值器算法填充
  • Drawable Animation(Frame Animation 帧动画)
  • Property Animation(属性动画)
    它更改的是对象的实际属性,

Property Animation属性

  • Duration:动画的持续时间
  • TimeInterpolation:属性值的计算方式,如先快后慢
  • TypeEvaluator:根据属性的开始、结束值与TimeInterpolation计算出的因子计算出当前时间的属性值
  • Repeat Count and behavoir:重复次数与方式,如播放3次、5次、无限循环,可以此动画一直重复,或播放完时再反向播放
  • Animation sets:动画集合,即可以同时对一个对象应用几个动画,这些动画可以同时播放也可以对不同动画设置不同开始偏移
  • Frame refreash delay:多少时间刷新一次,即每隔多少时间计算一次属性值,默认为10ms,最终刷新时间还受系统进程调度与硬件的影响

Property Animation 动画流程

ValueAnimator

ValueAnimator包含Property Animation动画的所有核心功能,如动画时间,开始、结束属性值,相应时间属性值计算方法等。应用Property Animation有两个步聚:

  1. 计算属性值
  2. 根据属性值执行相应的动作,如改变对象的某一属性。(需要在onAnimationUpdate中传入执行动画的对象)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    ValueAnimator animation = ValueAnimator.ofFloat(0f, 1f);
    animation.setDuration(1000);
    animation.addUpdateListener(new AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
    Log.i("update", ((Float) animation.getAnimatedValue()).toString());
    //这个函数中会传入ValueAnimator对象做为参数,通过这个ValueAnimator对象的getAnimatedValue()函数可以得到当前的属性值
    }
    });

    animation.setInterpolator(new CycleInterpolator(3));
    animation.start();

ObjectAnimator

ObjectAnimator继承自ValueAnimator,要指定一个对象及该对象的一个属性,例如

  • 常用方法有ofFloat(),ofInt(),ofObject(),ofArgb(),ofPropertyValuesHolder()。
  • 属性动画可用的属性
    答案是:任何一切带有set开头的方法属性名字。可能我们常用的有:

    • 平移 translationX,translationY, X,Y。
    • 缩放 scaleX,scaleY。
    • 旋转 rotationX, rotationY。
    • 透明度 alpha。

    也就是说我们所有控件都有以上setTranslationX(),setScaleX(),setRotationX(),setAlpha()等方法。
    我们不仅限于这几个属性,就拿TextView控件来说,只要是TextView有的属性都可以用来实现动画效果,比如 字体大小:“textColor”,字体颜色“textSize”等。

    限制:对象应该有一个setter函数:set(驼峰命名法)及要有相应属性的getter方法:get
    且应返回值类型应与相应的setter方法的参数类型一致。
    如果上述条件不满足,则不能用ObjectAnimator,应用ValueAnimator代替。

1
2
3
4
5
6
7
ObjectAnimator animator = ObjectAnimator.ofFloat(imageView, 'alpha', 1.0f, 0.3f, 1.0F);
animator.setDuration(2000);//动画时间
animator.setInterpolator(new BounceInterpolator());//动画插值
animator.setRepeatCount(-1);//设置动画重复次数
animator.setRepeatMode(ValueAnimator.RESTART);//动画重复模式
animator.setStartDelay(1000);//动画延时执行
animator.start();//启动动画

根据应用动画的对象或属性的不同,可能需要在onAnimationUpdate函数中调用invalidate()函数刷新视图。

组合动画

  • 组合动画1–AnimatorSet的使用
    这个类提供了一个play()方法,如果我们向这个方法中传入一个Animator对象(ValueAnimator或ObjectAnimator)将会返回一个AnimatorSet.Builder的实例,AnimatorSet.Builder中包括以下四个方法:

    • after(Animator anim) 将现有动画插入到传入的动画之后执行
    • after(long delay) 将现有动画延迟指定毫秒后执行
    • before(Animator anim) 将现有动画插入到传入的动画之前执行
    • with(Animator anim) 将现有动画和传入的动画同时执行

    Android 除了提供play(),还有playSequentially(),playTogether() 可供使用,可传入一个或者多个动画对象(,隔开),或者动画集合

1
2
3
4
5
6
7
8
9
10
11
ObjectAnimator animator = ObjectAnimator.ofInt(container, "backgroundColor", 0xFFFF0000, 0xFFFF00FF);
ObjectAnimator animator1 = ObjectAnimator.ofFloat(view, "translationX", 0.0f, 200.0f, 0f);
ObjectAnimator animator2 = ObjectAnimator.ofFloat(view, "scaleX", 1.0f, 2.0f);
ObjectAnimator animator3 = ObjectAnimator.ofFloat(view, "rotationX", 0.0f, 90.0f, 0.0F);
ObjectAnimator animator4 = ObjectAnimator.ofFloat(view, "alpha", 1.0f, 0.2f, 1.0F);

//组合动画方式1
AnimatorSet set = new AnimatorSet();
((set.play(animator).with(animator1).before(animator2)).before(animator3)).after(animator4);
set.setDuration(5000);
set.start();
  • 组合动画2–PropertyValuesHolder的使用
    使用方法ObjectAnimator.ofPropertyValuesHolder(Object target,PropertyValuesHolder… values);第一个参数是动画的目标对象,之后的参数是PropertyValuesHolder类的实例,可以有多个这样的实例。代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    PropertyValuesHolder valuesHolder = PropertyValuesHolder.ofFloat("translationX", 0.0f, 300.0f);
    PropertyValuesHolder valuesHolder1 = PropertyValuesHolder.ofFloat("scaleX", 1.0f, 1.5f);
    PropertyValuesHolder valuesHolder2 = PropertyValuesHolder.ofFloat("rotationX", 0.0f, 90.0f, 0.0F);
    PropertyValuesHolder valuesHolder3 = PropertyValuesHolder.ofFloat("alpha", 1.0f, 0.3f, 1.0F);

    ObjectAnimator objectAnimator = ObjectAnimator.ofPropertyValuesHolder(view, valuesHolder, valuesHolder1, valuesHolder2, valuesHolder3);
    objectAnimator.setDuration(2000).start();
    //类似于AnimatorSet.playTogether(Animator... items);
  • 组合动画3-ViewPropertyAnimator(多属性动画)

    1
    2
    ViewPropertyAnimator animator5 = imageView.animate();
    animator5.translationX(200).scaleX(2).setDuration(2000).start();

注意:使用ViewPropertyAnimator类需要API>=12

动画监听

  • animator.addListener(new Animator.AnimatorListener(){});//监听动画开始,结束,取消,重复(四种都包括)
  • animator.addListener(new AnimatorListenerAdapter(){});
    推荐,可代替AnimatorListener,需要监听动画开始,结束,取消,重复那种就直接实现那种方法就行
    其实AnimatorListenerAdapter的源码只是一个实现了AnimatorListener接口的抽象类而已
  • animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener(){});
    更加精确的方法来时刻监听当前动画的执行情况,可以读取到动画的每个更新值了
    1
    2
    3
    4
    5
    6
    7
    8
    animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {

    float value = (float) animation.getAnimatedValue();
    //可以根据自己的需要来获取动画更新值。
    Log.e('TAG', 'the animation value is ' + value);
    }
    });

Keyframes

keyFrame是一个 时间/值 对,通过它可以定义一个在特定时间的特定状态,即关键帧,而且在两个keyFrame之间可以定义不同的Interpolator,就好像多个动画的拼接,第一个动画的结束点是第二个动画的开始点。KeyFrame是抽象类,要通过ofInt(),ofFloat(),ofObject()获得适当的KeyFrame,然后通过PropertyValuesHolder.ofKeyframe获得PropertyValuesHolder对象,如以下例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/*
* 动画效果:btn对象的width属性值使其:
* 开始时 Width=400
* 动画开始1/4时 Width=200
* 动画开始1/2时 Width=400
* 动画开始3/4时 Width=100
* 动画结束时 Width=500
*/

Keyframe kf0 = Keyframe.ofInt(0, 400);
Keyframe kf1 = Keyframe.ofInt(0.25f, 200);
Keyframe kf2 = Keyframe.ofInt(0.5f, 400);
Keyframe kf4 = Keyframe.ofInt(0.75f, 100);
Keyframe kf3 = Keyframe.ofInt(1f, 500);
PropertyValuesHolder pvhRotation = PropertyValuesHolder.ofKeyframe("width", kf0, kf1, kf2, kf4, kf3);
ObjectAnimator rotationAnim = ObjectAnimator.ofPropertyValuesHolder(btn2, pvhRotation);
rotationAnim.setDuration(2000);

Property Animation在XML中使用

  • xml文件放在res/animator/中

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    <set xmlns:android='http://schemas.android.com/apk/res/android'
    android:duration='2000'
    android:ordering='sequentially'><!--动画执行顺序 sequentially:顺序执行;together:同时执行。 -->


    <objectAnimator
    android:propertyName='translationX'
    android:valueFrom='0'
    android:valueTo='200'
    android:valueType='floatType' />


    <set android:ordering='together'>
    <objectAnimator
    android:propertyName='scaleX'
    android:valueFrom='1'
    android:valueTo='2'
    android:valueType='floatType' />

    <objectAnimator
    android:propertyName='rotationX'
    android:valueFrom='0'
    android:valueTo='90'
    android:valueType='floatType' /><!--动画值的类型-->


    </set>
  • 通过AnimatorInflater.loadAnimator方法加载xml动画返回一个Animator的对象,然后调用setTarget方法给动画设置对象调用哪个start启动动画即可完成xml动画效果

    1
    2
    3
    Animator animator = AnimatorInflater.loadAnimator(context, R.animator.anim_file);
    animator.setTarget(view);
    animator.start();

花花绿绿的ProgressBar 杨俊构

参考Android 基础入门教程

  • ProgressBar(进度条)是Android基本UI控件,ProgressBar的应用场景很多,比如 用户登录时,后台在发请求,以及等待服务器返回信息,这个时候会用到进度条;或者当在进行一些比较 耗时的操作,需要等待一段较长的时间,这个时候如果没有提示,用户可能会以为程序Carsh或者手机死机 了,这样会大大降低用户体验,所以在需要进行耗时操作的地方,添加上进度条,让用户知道当前的程序 在执行中,也可以直观的告诉用户当前任务的执行进度等!使用进度条可以给我带来这样的便利!
  • ProgressBar官方API文档,ProgressBar继承与View类,直接子类有AbsSeekBar和ContentLoadingProgressBar, 其中AbsSeekBar的子类有SeekBar和RatingBar,可见这二者也是基于ProgressBar实现的

  • 常用属性详解:
  • android:max:进度条的最大值
  • android:progress:进度条已完成进度值
  • android:progressDrawable:设置轨道对应的Drawable对象
  • android:indeterminate:如果设置成true,则进度条不精确显示进度
  • android:indeterminateDrawable:设置不显示进度的进度条的Drawable对象
  • android:indeterminateDuration:设置不精确显示进度的持续时间
  • android:secondaryProgress:二级进度条,类似于视频播放的一条是当前播放进度,一条是缓冲进度,前者通过progress属性进行设置!
  • 对应的再Java中我们可调用下述方法:
  • getMax():返回这个进度条的范围的上限
  • getProgress():返回进度
  • getSecondaryProgress():返回次要进度
  • incrementProgressBy(int diff):指定增加的进度
  • isIndeterminate():指示进度条是否在不确定模式下
  • setIndeterminate(boolean indeterminate):设置不确定模式下
  • 系统默认进度条使用实例:

耀眼的IOS的ProgressBar ,参看开源中国IOS代码库

圆形进度条

网上一个简单的自定义圆形进度条!代码还是比较简单,容易理解,有兴趣可以看看,或者进行相关扩展~

  • 实现代码:自定义View类:
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
102
103
104
105
106
107
108
109
110
111
112
113
114
115

/**
* Created by Jay on 2015/8/5 0005.
*/

public class CirclePgBar extends View {


private Paint mBackPaint;
private Paint mFrontPaint;
private Paint mTextPaint;
private float mStrokeWidth = 50;
private float mHalfStrokeWidth = mStrokeWidth / 2;
private float mRadius = 200;
private RectF mRect;
private int mProgress = 0;
//目标值,想改多少就改多少
private int mTargetProgress = 90;
private int mMax = 100;
private int mWidth;
private int mHeight;


public CirclePgBar(Context context) {
super(context);
init();
}

public CirclePgBar(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}

public CirclePgBar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}


//完成相关参数初始化
private void init() {
mBackPaint = new Paint();
mBackPaint.setColor(Color.WHITE);
mBackPaint.setAntiAlias(true);
mBackPaint.setStyle(Paint.Style.STROKE);
mBackPaint.setStrokeWidth(mStrokeWidth);

mFrontPaint = new Paint();
mFrontPaint.setColor(Color.GREEN);
mFrontPaint.setAntiAlias(true);
mFrontPaint.setStyle(Paint.Style.STROKE);
mFrontPaint.setStrokeWidth(mStrokeWidth);


mTextPaint = new Paint();
mTextPaint.setColor(Color.GREEN);
mTextPaint.setAntiAlias(true);
mTextPaint.setTextSize(80);
mTextPaint.setTextAlign(Paint.Align.CENTER);
}


//重写测量大小的onMeasure方法和绘制View的核心方法onDraw()
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mWidth = getRealSize(widthMeasureSpec);
mHeight = getRealSize(heightMeasureSpec);
setMeasuredDimension(mWidth, mHeight);

}


@Override
protected void onDraw(Canvas canvas) {
initRect();
float angle = mProgress / (float) mMax * 360;
canvas.drawCircle(mWidth / 2, mHeight / 2, mRadius, mBackPaint);
canvas.drawArc(mRect, -90, angle, false, mFrontPaint);
canvas.drawText(mProgress + "%", mWidth / 2 + mHalfStrokeWidth, mHeight / 2 + mHalfStrokeWidth, mTextPaint);
if (mProgress < mTargetProgress) {
mProgress += 1;
invalidate();
}

}

public int getRealSize(int measureSpec) {
int result = 1;
int mode = MeasureSpec.getMode(measureSpec);
int size = MeasureSpec.getSize(measureSpec);

if (mode == MeasureSpec.AT_MOST || mode == MeasureSpec.UNSPECIFIED) {
//自己计算
result = (int) (mRadius * 2 + mStrokeWidth);
} else {
result = size;
}

return result;
}

private void initRect() {
if (mRect == null) {
mRect = new RectF();
int viewSize = (int) (mRadius * 2);
int left = (mWidth - viewSize) / 2;
int top = (mHeight - viewSize) / 2;
int right = left + viewSize;
int bottom = top + viewSize;
mRect.set(left, top, right, bottom);
}
}


}
  • 然后在布局文件中加上:
1
2
3
4

<com.jay.progressbardemo.CirclePgBar
android:layout_width="match_parent"
android:layout_height="match_parent"/>

总结

  • progressbar是Android中的常用控件,在实际开发中和动画结合较多,好的进度条可以缓解使用者焦躁的情趣,自定义圆形进度条可以自行完善,然后用到实际开发中!

layer-list实现阴影效果——xpleemoon

  • 为控件实现阴影效果,可以有多种方式:

    • 多个drawable层叠在一起(不好的实现是多个View层叠达到多个drawable的层叠效果,相对好的实现是在同一个View钟实现多个drawable的层叠)
    • 自定义view
    • Material Design中设置Z轴的方式
  • 本文的思路是多个drawable叠在一起,但是不额外使用View,通过layer-list可以将多个item按照顺序层叠在一起显示。首先来看效果图:

LayerShadow

  • 第一个和第二个控件是用来展示layer-list实现阴影效果的基本款,而第三个控件是综合上述两个控件效果,再集合selector实现的。

  • 默认状态:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 阴影:左偏移2dp,上偏移4dp -->
<item
android:left="2dp"
android:top="4dp">

<shape>
<solid android:color="@android:color/holo_blue_dark" />
<corners android:radius="10dp" />
</shape>
</item>
<!-- 前景::底偏移4dp,右偏移2dp -->
<item
android:bottom="4dp"
android:right="2dp">

<shape>
<solid android:color="@android:color/holo_blue_bright" />
<corners android:radius="10dp" />
</shape>
</item>
</layer-list>
  • 点击状态:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 为了达到点击的真实感,将原来默认状态的前景色设置为阴影,并将前景设为无透明-->
<item
android:left="2dp"
android:top="4dp">

<shape>
<solid android:color="@android:color/holo_blue_bright" />
<corners android:radius="10dp" />
</shape>
</item>
<item
android:bottom="4dp"
android:right="2dp">

<shape>
<corners android:radius="10dp" />
</shape>
</item>
</layer-list>

layer-list的item可以通过以下属性设置偏移量:

  • android:top 顶部的偏移量
  • android:bottom 底部的偏移量
  • android:left 左边的偏移量
  • android:right 右边的偏移量
  • selector,使用上述layer-list:
1
2
3
4
5
6
7
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/layer_list_btn_pressed" android:state_pressed="true" />
<item android:drawable="@drawable/layer_list_btn_pressed" android:state_selected="true" />
<item android:drawable="@drawable/layer_list_btn" />

</selector>
  • 最后再来看下,布局代码:
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
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity">


<TextView
android:layout_width="100dp"
android:layout_height="50dp"
android:background="@drawable/layer_list_btn"
android:clickable="true"
android:gravity="center"
android:text="默认状态" />


<TextView
android:layout_width="100dp"
android:layout_height="50dp"
android:layout_marginTop="@dimen/activity_vertical_margin"
android:background="@drawable/layer_list_btn_pressed"
android:clickable="true"
android:gravity="center"
android:text="点击状态" />


<TextView
android:layout_width="100dp"
android:layout_height="50dp"
android:layout_marginTop="@dimen/activity_vertical_margin"
android:background="@drawable/selector_btn"
android:clickable="true"
android:gravity="center"
android:text="点我" />

</LinearLayout>
  • 第一个和第二个TextView分别引用了对应的layer-list(默认和点击)作为背景,第三个引用了selector

  • 源码地址

零部署的云服务 - 王胜

本周的分享主题是动画,但之前7月份我已经分享过一次,知其所以然 。再加上前面几位已经对android的动画介绍的很完整了,所以我就分享另一个话题『云平台下的零部署开发网站』。

牵扯的技能

  • Git
  • 任何一门后端开发语言

搭建步骤

AZURE -> 点击 Web应用模块 -> 选择语言,点击创建Web应用 -> 选择模板,点击创建,至此,应用以创建,点击页面上站点url,就能看到新创建的Web站点了。

修改代码,Git提交自动完成部署

复制创建成功页面『使用 Git 克隆或推送』里的Git地址,将初始化源码clone到本地,然后编辑代码,修改功能。完成后,直接push到远程,云平台自动完成部署。再看看网页是不是已经发生变化了。

1
Victors-MPB:azure wangsheng$ git clone https://$4f743e5d-0ee0-4-231-b9ee:WSc8Szx4Rf30apo3Ky7hpw6mXAadAZYvn1zagCvMaboj7mHFyJ2vzuYn9i12@4f743e5d-0ee0-4-231-b9ee.scm.azurewebsites.net/4f743e5d-0ee0-4-231-b9ee.git
Cloning into '4f743e5d-0ee0-4-231-b9ee'...
remote: Counting objects: 4, done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 4 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (4/4), done.
Checking connectivity... done.
Victors-MPB:azure wangsheng$ ls
4f743e5d-0ee0-4-231-b9ee
Victors-MPB:azure wangsheng$ cd 4f743e5d-0ee0-4-231-b9ee/
Victors-MPB:4f743e5d-0ee0-4-231-b9ee wangsheng$ git br
* master
Victors-MPB:4f743e5d-0ee0-4-231-b9ee wangsheng$ git log
commit cf6edda65210ef7254f9bc545c778489f123620e
Author: windowsazure <windowsazure>
Date:   Fri Nov 20 03:53:00 2015 +0000

    Initial Commit
Victors-MPB:4f743e5d-0ee0-4-231-b9ee wangsheng$ ls
favicon.ico	index.php
Victors-MPB:4f743e5d-0ee0-4-231-b9ee wangsheng$ vi index.php
Victors-MPB:4f743e5d-0ee0-4-231-b9ee wangsheng$ git st
On branch master
Your branch is up-to-date with 'origin/master'.
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

	modified:   index.php

no changes added to commit (use "git add" and/or "git commit -a")
Victors-MPB:4f743e5d-0ee0-4-231-b9ee wangsheng$ git diff
diff --git a/index.php b/index.php
<?php
index 712f825..50a17e7 100644
--- a/index.php
+++ b/index.php
@@ -1,3 +1,4 @@
 <?php
     echo "Hello World"
-?>
\ No newline at end of file
+    echo "hack it."
+?>
Victors-MPB:4f743e5d-0ee0-4-231-b9ee wangsheng$ git ci -am "add one line."
[master 6c5551f] add one line.
 1 file changed, 2 insertions(+), 1 deletion(-)
Victors-MPB:4f743e5d-0ee0-4-231-b9ee wangsheng$ git push origin master
Counting objects: 3, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 324 bytes | 0 bytes/s, done.
Total 3 (delta 0), reused 0 (delta 0)
remote: Updating branch 'master'.
remote: Updating submodules.
remote: Preparing deployment for commit id '6c5551f090'.
remote: Generating deployment script.
remote: Generating deployment script for Web Site
remote: Generated deployment script files
remote: Running deployment command...
remote: Handling Basic Web Site deployment.
remote: Finished successfully.
remote: Deployment successful.

对,后端开发可以如此简单快乐地开发部署!!!

周精益分享 - Swift入门专题

Swift初见 — 张超耀

1
2
3
4
5
6
7
8
9
10
// in objective-c, but in swift, #define can't be used any more  
// use let keyword to define a macro, look up original document:
/*
Simple Macros
Where you typically used the #define directive to define a primitive constant in C and Objective-C, in Swift you use a global constant instead. For example, the constant definition #define FADE_ANIMATION_DURATION 0.35 can be better expressed in Swift with let FADE_ANIMATION_DURATION = 0.35. Because simple constant-like macros map directly to Swift global variables, the compiler automatically imports simple macros defined in C and Objective-C source files.
*/
// in objective-c
// #define kCommonAPI @"http://xxxxxxx"
// but in swift, no #define, just use let to define
let kCommonAPI = "http://xxxxxxx"

注释 & 分号

  • 在Swift中,注释跟C/OC语言中的注释很像,但最大的不同点就是在Swift中多行注释可以嵌套
1
2
3
4
5
6
// 这是单行注释  

/* 这也是注释,但是多行注释
/*多行注释在swift中是可以嵌套的*/
/*原官方指导教程上说嵌套多行注释可以快速、简单地把大的代码块分成多块来注释 */
*/
  • 与其它开发语言不同的时,swift是不要求写分号的,当然如果想写,也是可以的。当你想把多个语句写到同一行时,这种情况下就一定要使用分号来隔开不同的语句了
1
2
3
let dog = "a gog" // 可以不添加分号  
let cat = "a cat"; print(cat)// 除了最后一条语句可以不添加分号外,其它都需要添加分号来隔开
let catTwo = "two cats"; let name = "Jobs"; print("\(name) has \(catTwos)")

类型转换

  • Swift不会像C、OC那样自动隐式转换类型,所以我们需要手动进行类型转换
1
2
3
4
5
6
7
8
let twoThousand: UInt16 = 2000   
// one是UInt8类型
let one: UInt8 = 1
// twoThousand是UInt16类型,one是UInt8类型,如果要执行相加,那么就需要进行类型转换
// 否则会报错的。
let twoThousandAndOne = twoThousand + UInt16(one)

** 浮点值转换成整型时,会截尾**

类型别名(Typealias)

  • 类型别名也就是给已经存在的类型起一个别名。定义类型别名是使用关键字typealias。 类型别名一般是为了让开发者更容易看出变量或者常量的类型或者是更好地归类某一个模块中需要使用到的类型,让开发者见名知意。
1
2
3
4
5
6
7
// 下面是给UInt16起一个别名,叫mySample  
// 然后就可以在其它地方使用这个mySample声明变量或者常量
typealias mySample = UInt16

// 由于前面已经定义了类型别名,那么这里使用AudioSample也相当于使用UInt16
所以mySample.min = UInt16.min,也就是0
var maxAmplitudeFound = mySample.min

“?” 和 “!”

  • Swift语言使用var定义变量,但和别的语言不同,Swift里不会自动给变量赋初始值, 也就是说变量不会有默认值,所以要求使用变量之前必须要对其初始化
    。如果在使用变量之前不进行初始化就会报错.
1
2
3
4
5
6
7
8
9
10
11
12
13
var stringValue : String?
stringValue = nil
print("\(stringValue)")
let hashValue = stringValue?.hashValue

// 这就是optional, strValue自动得到默认值:nil
// 这个nil跟Objective-C中的nil不同,不是指针,而是表示值不存在。
var strValue: String?

// 判断optional是否有值
if (strValue != nil) {
// do what you need to do here
}

下期预告

深度理解Swift的”?”和”!”

Swift之断言

浅谈Swift的closure

Dubbo 上手 — 陈奎

『图像和滤镜』 - 图像选择器

我们可以使用以下常规的图像获取方式

Alt text

图库与相册

Alt text

系统自带

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {

let sheet = UIAlertController(title: "图片选择", message: "简单版的三种选择", preferredStyle: .ActionSheet)
// 判断设备是否支持相机(iPod & Simulator)
if (UIImagePickerController.isSourceTypeAvailable(.Camera)) {
sheet.addAction(UIAlertAction.init(title: "Camera", style: .Default, handler: { _ in
self.showPhotoes(.Camera)
}))
}
sheet.addAction(UIAlertAction.init(title: "PhotoLibrary", style: .Default, handler: { _ in
self.showPhotoes(.PhotoLibrary)
}))
sheet.addAction(UIAlertAction.init(title: "SavedPhotosAlbum", style: .Default, handler: { _ in
self.showPhotoes(.SavedPhotosAlbum)
}))
sheet.addAction(UIAlertAction.init(title: "Cancel", style: .Cancel, handler: nil))
presentViewController(sheet, animated: true, completion: nil)
}
1
2
3
4
5
6
7
8
func showPhotoes(source: UIImagePickerControllerSourceType) {
let controller = UIImagePickerController()
controller.delegate = self
controller.sourceType = source
controller.allowsEditing = source == .SavedPhotosAlbum ? true:false

self.presentViewController(controller, animated: true, completion: nil)
}

UIImagePickerController的代理

1
2
3
4
5
6
7
8
func imagePickerControllerDidCancel(picker: UIImagePickerController) {
dismissViewControllerAnimated(true, completion: nil)
}
// 非常坑,这个方法废弃了但代码提示只有它
// func imagePickerController(picker: UIImagePickerController, didFinishPickingImage image: UIImage, editingInfo: [String : AnyObject]?) {
// }
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) {
}

info字典介绍
1
2
3
4
5
6
7
8
9
10
UIImagePickerControllerMediaType: String
UIImagePickerControllerOriginalImage: UIImage
UIImagePickerControllerEditedImage: UIImage
UIImagePickerControllerCropRect: NSValue -> CGRect
// MediaURL只为视频提供
UIImagePickerControllerMediaURL: NSURL
// LivePhoto是一张图片,保留那个moment的前后动作和声音
UIImagePickerControllerLivePhoto: String
// 摄像摄影时返回media的信息字典
UIImagePickerControllerMediaMetadata: NSDictionary
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// SavedPhotosAlbum 的 info 示例

▿ 5 elements
▿ [0] : 2 elements
- .0 : "UIImagePickerControllerCropRect"
▿ [1] : 2 elements

- .0 : "UIImagePickerControllerOriginalImage"
▿ [2] : 2 elements

- .0 : "UIImagePickerControllerReferenceURL"
- .1 : assets-library://asset/asset.JPG?id=99D53A1F-FEEF-40E1-8BB3-7DD55A43C8B7&ext=JPG
▿ [3] : 2 elements

- .0 : "UIImagePickerControllerMediaType"
- .1 : public.image
▿ [4] : 2 elements

- .0 : "UIImagePickerControllerEditedImage"

自定义

Alt text

遍历相册的所有图片

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
// AssetsLibrary.framework
// ALAssetsLibrary 的使用,但是它慢慢的被放弃了
func loadLocalPhotoes(){
var countOne = 0
//ALAssetsGroupSavedPhotos表示只读取相机胶卷(ALAssetsGroupAll则读取全部相簿)
assetsLibrary.enumerateGroupsWithTypes(ALAssetsGroupSavedPhotos, usingBlock: {
(group: ALAssetsGroup!, stop) in
print("is goin")
if group != nil {
let assetBlock : ALAssetsGroupEnumerationResultsBlock = {
(result: ALAsset!, index: Int, stop) in
if result != nil {
self.assets.append(result)
countOne++
}
}
group.enumerateAssetsUsingBlock(assetBlock)
print("assets:\(countOne)")
self.startChangeLocalImages(0)
}
}, failureBlock: { (fail) in
print(fail)
})
}

// 展现本地图片
func startChangeLocalImages(var index: Int){
if index==assets.count {
index = 0
}
let myAsset = assets[index]
let image = UIImage(CGImage:myAsset.thumbnail().takeUnretainedValue())
self.backImageView.image = image

let popTime = dispatch_time(DISPATCH_TIME_NOW, Int64(1 * Double(NSEC_PER_SEC)))
dispatch_after(popTime, dispatch_get_main_queue()) {
self.startChangeLocalImages(index+1)
}
}

iOS9 开始使用新库
PHPhotoLibrary

周精益分享 - 程序员装逼指南二期

Android 单元测试 吴明

  • Android单元测试支持
    • Android studio 1.2 beta3版本,原生支持单元测试
  • 环境搭建
    • 配置环境命令:./gradlew build connectedCheck
  • 测试示例
    • MacDown Screenshot
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class MyFirstTestActivityTest extends ActivityInstrumentationTestCase2<MyFirstTestActivity> {

private MyFirstTestActivity mFirstTestActivity;
private TextView mFirstTestText;

public MyFirstTestActivityTest() {
super(MyFirstTestActivity.class);
}

@Override
protected void setUp() throws Exception {
super.setUp();
mFirstTestActivity = getActivity();
mFirstTestText =
(TextView) mFirstTestActivity
.findViewById(R.id.my_first_test_text_view);
}
}
  • ps:由于配置环境一直报:
1
Test running failed: Instrumentation run failed due to 'java.lang.RuntimeException'

Linux 文件权限入门 - 曾铭

一个线上 bug

图片上传 API 无法使用,定位到问题是 dev1 帐号移动的 tomcat 无法访问 /home/img_temp 目录,处理权限问题后搞定

基本概念

user, group, others

1
2
3
4
5
6
[dev1@apps-51offer ~]$ ll
total 16
-rw-r--r-- 1 dev1 dev 742 Nov 5 11:22 ming.pub
drwxrwx--- 4 root root 4096 Nov 6 09:30 test1
drwxr-xr-- 2 dev1 dev 4096 Nov 6 10:44 test2
drwxr-xr-x 2 dev1 dev 4096 Nov 6 09:30 test3
  • 文件 -
  • 文件夹 d
  • 链接 l

rwx 读、写、执行权限 , 754 代表什么

  • 文件所有者变更:chown ming:mobile file
  • 文件权限变更: chmod g+rx file

一个小坑

问:

drwxr-xr-- 2 dev1 dev 4096 Nov 6 10:44 test2 文件夹下有 -rwxr-xr-- 2 root root 4096 Nov 6 10:44 file
dev1 用户可以删除 file 这个文件吗?

答:

可以,文件的 rwx 控制文件本身的读写运行权限,文件的增删文件名的修改要看文件夹的权限。

Android 压力测试工具 Monkey&MonkeyRunner- 杨俊构

Monkey

一、Monkey 是什么?

  • Monkey 就是SDK中附带的一个工具。

二、Monkey 测试的目的?

  • 该工具用于进行压力测试。 然后开发人员结合monkey 打印的日志 和系统打印的日志,结局测试中出现的问题。

三、Monkey 测试的特点?

  • Monkey 测试,所有的事件都是随机产生的,不带任何人的主观性。

四、Monkey 命令详解

  • 标准的monkey 命令: [adb shell] monkey [options] ,

    例如:adb shell monkey -v 500 ————产生500次随机事件,

  • 四大类—— 常用选项 、 事件选项 、 约束选项 、 调试选项

    -v:指定打印信息的详细级别,一个 -v增加一个级别 , 默认级别为 0 。

    -s:指定产生随机事件种子值,相同的种子值产生相同的事件序列。如: -s 200

    -p:指定有效的package(如不指定,则对系统中所有package有效),一个-p 对应一个有效package, 如:-p com.ckt -p com.ckt.asura;

五、一个简单的monkey命令:

  • adb shell monkey -p com.xy.android.junit -s 500 -v 10000

  • 但是,工作中为了保证测试数量的完整进行,我们一般不会在发生错误时立刻退出压力测试。

  • monkey 测试命令如下

adb shell monkey -p com.xy.android.junit -s 500 —ignore-crashes —ignore-timeouts —monitor-native-crashes -v -v 10000 > E:\monkey_log\java_monkey_log.txt

MonkeyRunner

一、什么是monkeyrunner

  • monkeyrunner工具提供了一个API,使用此API写出的程序可以在Android代码之外控制Android设备和模拟器。通过monkeyrunner,您可以写出一个Python程序去安装一个Android应用程序或测试包,运行它,向它发送模拟击键,截取它的用户界面图片。

  • monkeyrunner工具的主要设计目的是用于测试功能/框架水平上的应用程序和设备,或用于运行单元测试套件,但您当然也可以将其用于其它目的。

二、monkeyrunner工具同Monkey工具的差别

  • Monkey: Monkey工具直接运行在设备或模拟器的adb shell中,生成用户或系统的伪随机事件流,属于黑盒测试。

  • monkeyrunner:monkeyrunner工具则是类似于灰盒测试的样子,可以编写定制一些测试方法和用例针对具体情况测试。

三、实例 :双11抽取优惠券不停点击一个按钮脚本

1
2
3
4
5
6
7
8
9
10
11
#coding=utf-8 
from com.android.monkeyrunner import MonkeyRunner, MonkeyDevice, MonkeyImage
from com.android.monkeyrunner import MonkeyRunner as mr
from com.android.monkeyrunner import MonkeyDevice as md
from com.android.monkeyrunner import MonkeyImage as mi
device=mr.waitForConnection()
for i in range(1000):
MonkeyRunner.sleep(1)
print i
device.touch(500,950,'DOWN_AND_UP')
print "end"

人脸识别技术简单介绍- 王进

Android自带的API:FaceDetector

  • Step 1: 读取bitmap图片(API 只接受Bitmap)

    1
    Bitmap sampleBmp=BitmapFactory.decodeResource(getResources(), R.drawable.sample1);

    (拍照或者从相册中也可以)

  • Step 2: 通过FaceDetector API进行人脸识别

    1
    2
    3
    4
    5
    6
    7
      //FaceDetecor只能读取RGB 565格式的Bitmap
    Bitmap tmpBmp = inputImage.copy(Bitmap.Config.RGB_565, true);
    //新建FaceDetector 实例,
    FaceDetector faceDet = new FaceDetector(tmpBmp.getWidth(), tmpBmp.getHeight(), MAX_FACES);//MAX_FACES 搜索人脸个数
    // 调用findFaces方法,将数据储存在facelist中
    FaceDetector.Face[] faceList = new FaceDetector.Face[MAX_FACES];
    faceDet.findFaces(tmpBmp, faceList);

    找人脸的原理是:找眼睛。
    它返回的人脸数据face中,
    通过调用public float eyesDistance (),得到眼距
    public void getMidPoint (PointF point),得到两眼中心点位置。
    public float confidence () 可以返回该人脸数据的可信度(0~1),这个值越大,该人脸数据的准确度也就越高

  • Step3:对原图进行缩放,并在图上显示人脸框

    • 吐槽:
      A,只能接受Bitmap 格式的数据;
      B,只能识别双眼距离大于20 像素的人脸像(当然,这个可在framework层中修改);
      C,只能检测出人脸的位置(双眼的中心点及距离),不能对人脸进行匹配(查找指定的脸谱)。

第三方Face++

  • 在线API:基础版API供免费调用测试和小规模使用(会尽力维护API服务稳定性,但不保证每次调用均会成功),企业版API相对基础版API在算法和稳定性方面会有更好的表现
  • 离线SDK包括人脸关键点定位等功能。
  • 网址:http://www.faceplusplus.com.cn/

Android启动模式之singelTask扫盲- 李仙鹏

周精益分享 - 程序员装逼指南

Android 单元测试

『图像和滤镜』

@(Share)[UIImage]

[toc]

图像

常见的图像格式

  • png(Portable Network Graphic)便携式网络图形格式
  • tiff、tif(Tagged Image File Format)标记图像文件格式
  • jpg、jpeg(Joint Photographic Experts Group)联合摄影专家组
  • gif(Graphic Interchange Format)图形交换格式

图像实例化

  • imageNamed:

    使用频率高,内存缓存优化

  • imageWithContentsOfFile:

    单次使用,暂时不清楚如何支持ImageAssert下的图片路径

  • imageWithData:

    本地或网络的文件数据加载

  • imageWithCGImage:

    绘制生成图,代码如下

1
2
3
4
5
// 旧图局部裁剪
CGImageRef myImageRef = [oldImage CGImageRef];
CGRect subRect = CGRectMake(20, 20, 100, 100);
CGImageRef cgCrop = CGImageCreateWithImageInRect(myImageRef, subRect);
UIImage *imageCrop = [UIImage imageWithCGImage:cgCrop];

显示图像

屏幕的缩放因子,导致我们需要@2x及@3x图,对视图截图操作也同样需要针对Retina及plus优化

1
2
3
4
5
6
7
8
9
10
11
- (UIImage*)screenShotBy:(UIImageView*)imagView
{
// UIGraphicsBeginImageContext(imagView.bounds.size);
// 2表示Retina,3表示plus分辨率,1正常,0则是当前设备的缩放因子
// YES 表示不包含图像的alpha通道
UIGraphicsBeginImageContextWithOptions((imagView.bounds.size), YES, 2);
[imagView.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage*image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}

contentModel

图像内容展现方式

  • UIViewContentModelScaleToFill(默认)
  • UIViewContentModelScaleAspectFill(下拉放大)
  • UIViewContentModelScaleAspectFit
  • UIViewContentModelCenter (一些停靠模式)
  • UIViewContentModelTop/Bottom
  • UIViewContentModelLeft/Right
拉伸属性

代码

1
2
3
4
5
// 方法一
image=[oldIconImage stretchableImageWithLeftCapWidth:10 topCapHeight:12];

// 方法二
image = [oldIconImage resizableImageWithCapInsets:UIEdgeInsetsMake(17, 17, 17, 25)];

ImageAssert

Alt text
使用slicing,小变大无视差
Alt text


图像选择器

系统自带

自定义


滤镜

人脸识别

聊聊 QCon2015 - 曾铭

pure-native 移动跨平台架构设计与实战

by 赵世婚

好处

  • 重复逻辑:网络层、账户逻辑
  • 性能(网络压缩、打破内存限制)
  • 多一层封装应对业务变动

坏处

  • 跨平台开发代码生成
  • 调试:异常捕获

总结:

  • 学不来:人员构成决定技术选型,技术选型决定依赖人员 {:&.rollIn}
  • 分层来应对业务变动(横向分层和纵向分层)

JavaScript - The World’s Best Programming Language

by Hax

JavaScript 最好 ?

  • 语言简陋:弱类型、标准库、面向对象、模块、异步 {:&.rollIn}
  • 编程时注意:好的范式、不好的范式

不过

  • npm 飞速发展的 packages 数量 {:&.rollIn}
  • ECMAScript 6,语言特性问题完美解决 (c)
  • Babel 6to5 (python)
    • most hard to upgrade -> always use latest feature!
  • 最快的动态语言,还会更快 (ruby)
  • JS as a Platform:中间语言 (c#, java)
  • 生态系统:浏览器、node.js、公司、社区

总结:

  • js 是现阶段发展最快的语言 {:&.rollIn}
  • 前端前途无量!

react

jQuery

  • DOM 操作
  • 组件散乱,社区力量难以聚合

AngularJS

  • 绑定 数据与数据、数据与视图之间的关系
  • npm

React

  • 用 js 写界面……(似乎是一种倒退)
  • 还整个 JSX 让界面好写一点
  • React Native

总结

  • 前端风起云涌 {:&.rollIn}
  • 小扎灭我客户端之心不死

总的总结

  • 拥抱变化
  • 最差也可以变最好,关键要敢于拥抱变化
  • 技术紧迫感:勇于尝试新东西
  • 业务紧迫感:技术改变留学

推荐:

  • JavaScript - The World’s Best Programming Language
  • pure-native移动跨平台架构设计与实战
  • 注重实效的性能
  • ReactJS 实战
  • ELE.ME是如何运行的

Android 6.0运行权限 - 王胜

一般权限和危险权限

Android 6.0以前,所有的权限在应用安装或者更新时自动完成授权。如果用户想安装这个App,必需接受所有的权限申请。为了保护用户的隐私,Android M特意调整了Android的授权架构。将应用权限分为一般(Normal)和危险(Dangerous)两种级别的权限:

  • 一般权限不会直接危害用户的隐私。如果你的App在manifest列出一般权限,那么系统会自动授予这些权限。

  • 危险权限可以访问用户的隐私数据。 如果你的App在manifest列出一般权限,那么系统会自动授予这些权限。如果列出危险权限,需要通过用户显示交互通过你的应用权限申请。

权限组

Anroid系统里所有的危险权限都拥有所归属的权限组。如果设备运行在Android6.0(API level 23),并且App的targetSdkVersion是23或者更高,当App请求危险权限时将会有以下表现:

  • 如果App请求manifest里声明的危险权限,并且App当前还没有获取任何权限组里的权限,系统将弹出一个描述App想要访问权限的对话框。但是对话框不会描述该权限属于哪个权限组。例如:App请求权限 READ_CONTACTS ,系统对话框只显示App需要访问设备的联系人。如果用户授权通过,系统仅仅给App需要的权限。

  • 如果App请求manifest里声明的危险权限,并且App已经用户该危险权限所在的权限组的权限,系统则直接授予此次请求的权限,而不需要用户手动交互。例如:如果App已经有先前的 READ_CONTACTS 权限,那么再请求 WRITE_CONTACTS 权限,系统则直接授予。

任何权限都有所归属的权限组,包括一般权限和App自定义的权限。然而,权限组只影响那些危险权限相关的用户体验。你可以忽略一般权限的权限组。

危险权限和权限组

权限组 权限
CALENDAR
  • READ_CALENDAR
  • WRITE_CALENDAR
CAMERA
  • CAMERA
CONTACTS
  • READ_CONTACTS
  • WRITE_CONTACTS
  • GET_ACCOUNTS
LOCATION
  • ACCESS_FINE_LOCATION
  • ACCESS_COARSE_LOCATION
MICROPHONE
  • RECORD_AUDIO
PHONE
  • READ_PHONE_STATE
  • CALL_PHONE
  • READ_CALL_LOG
  • WRITE_CALL_LOG
  • ADD_VOICEMAIL
  • USE_SIP
  • PROCESS_OUTGOING_CALLS
SENSORS
  • BODY_SENSORS
SMS
  • SEND_SMS
  • RECEIVE_SMS
  • READ_SMS
  • RECEIVE_WAP_PUSH
  • RECEIVE_MMS
STORAGE
  • READ_EXTERNAL_STORAGE
  • WRITE_EXTERNAL_STORAGE

权限检测

Android的所有版本里,都需要将所需的一般权限和危险权限都声明在App的manifest里。然而这些声明在不同的系统版本和App的target SDK Level会有不同的影响:

  • 如果设备运行在Android 5.1或者更低,或者你的App target SDK是22或者更低:如果你在manifest里声明了危险权限,用户则不得不在安装App时给予授权;如果他们不授权,系统将不会安装此App。

  • 如果设备运行在Android 6.0或者更高,或者你的App target SDK是23或者更高:App不仅需要在manifest里声明危险权限,而且App在运行时还需要在每次需要危险权限时发起请求。用户可以接受也可以拒绝权限申请,App也需要在用户拒绝危险权限申请时能长长运行。

注意:此处的权限处理只有App的target API level 23或者更高,而且运行在Android 6.0 (API level 23)或者更高的设备上才会如此。如果设备的中App的targetSdkVersion 是 22或者更低,系统则在安装或者更新App时自动授予危险权限。

The Android Support Library能很好地处理检测、请求和权限。Android 6.0 (API level 23) framework也提供了类似的方法。然而,使用support库能更简单一些,因为你不需要在调用方法之前检测android系统的版本。

发起权限申请请求

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
// Here, this Activity is the current activity
if (ContextCompat.checkSelfPermission(thisActivity,
Manifest.permission.READ_CONTACTS)
!= PackageManager.PERMISSION_GRANTED) {

// Should we show an explanation?
if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
Manifest.permission.READ_CONTACTS)) {

// Show an expanation to the user *asynchronously* -- don't block
// this thread waiting for the user's response! After the user
// sees the explanation, try again to request the permission.

} else {

// No explanation needed, we can request the permission.

ActivityCompat.requestPermissions(thisActivity,
new String[]{Manifest.permission.READ_CONTACTS},
MY_PERMISSIONS_REQUEST_READ_CONTACTS);

// MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
// app-defined int constant. The callback method gets the
// result of the request.
}
}

处理权限请求结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Override
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults)
{

switch (requestCode) {
case MY_PERMISSIONS_REQUEST_READ_CONTACTS: {
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {

// permission was granted, yay! Do the
// contacts-related task you need to do.

} else {

// permission denied, boo! Disable the
// functionality that depends on this permission.
}
return;
}

// other 'case' lines to check for other
// permissions this app might request
}
}

注意:每次需要权限时,都需要显示请求调用,即使你已经获得的同权限组的其他权限。另外,权限分组处理的机制在将来的Android发型版本中有可能更改。因此你的App不应该依赖所需的权限在或者不在同一个权限组里。

参考:

Swift Access Control - 潘君

(未完待续)

  • 三个级别

    • Public
    • Internal
    • Private

      大部分默认是internal

  • 可应用于

    • Module
      • application
      • framework
    • source file
      • properties
      • types
      • functions
      • and so on(苹果文档这个太二了)
  • 准则

    • public变量不能定义为internal或者private的类型
    • 函数的访问权限不能高于参数和返回值
  • 应用场景

    • Single-Target Apps Internal
    • Frameworks Public
    • Unit Test Targets @testableAlt text
  • 会级联影响 例如修饰了type 同时也会影响type的members

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    public class SomePublicClass {          // explicitly public class
    public var somePublicProperty = 0 // explicitly public class member
    var someInternalProperty = 0 // implicitly internal class member
    private func somePrivateMethod() {} // explicitly private class member
    }

    class SomeInternalClass { // implicitly internal class
    var someInternalProperty = 0 // implicitly internal class member
    private func somePrivateMethod() {} // explicitly private class member
    }

    private class SomePrivateClass { // explicitly private class
    var somePrivateProperty = 0 // implicitly private class member
    func somePrivateMethod() {} // implicitly private class member
    }
  • Function

  • Tuple

    • 取元素中访问level最小的为准
    • 例如 (PrivateClass, InternalClass) 则取private
  • Enumeration

    • 一样
  • class

    1
    2
    3
    4
    5
    6
    7
    public class A {
    private func someMethod() {}
    }

    internal class B: A {
    override internal func someMethod() {}
    }