乘风破浪


  • 首页

  • 归档

  • 标签
乘风破浪

Volley源码解析

发表于 2018-03-21 |

基于”我们不重复造轮子不表示我们不需要知道轮子该怎么造及如何更好的造”,我开始了我的拆轮子之旅。
从使用方法出发我带大家跟着我的思路分析一遍volley1.1.0的源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//创建请求队列
RequestQueue queue = Volley.newRequestQueue(this);
String url = "https://www.baidu.com";
StringRequest stringRequest = new StringRequest(Request.Method.GET, url,
new Response.Listener<String>() {
@Override
public void onResponse(String response) {
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
}
});
queue.add(stringRequest);

RequestQueue 一个带有线程池调度的请求分派队列
StringRequest 请求的封装,将url的response body作为String返回

我们来逐行分析上面源码

1
2
3
public static RequestQueue newRequestQueue(Context context) {
return newRequestQueue(context, (BaseHttpStack) null);
}

传入了一个null的 BaseHttpStack 类
这里可见Volley的扩展性,可以修改网络传输层,例如扩展 OkHttpStack,用okHttp作为传输层。

再看 newRequestQueue(context, (BaseHttpStack) null)

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
public static RequestQueue newRequestQueue(Context context, BaseHttpStack stack) {
BasicNetwork network;
if (stack == null) {
if (Build.VERSION.SDK_INT >= 9) {
network = new BasicNetwork(new HurlStack());
} else {
// Prior to Gingerbread, HttpUrlConnection was unreliable.
// See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html
// At some point in the future we'll move our minSdkVersion past Froyo and can
// delete this fallback (along with all Apache HTTP code).
String userAgent = "volley/0";
try {
String packageName = context.getPackageName();
PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);
userAgent = packageName + "/" + info.versionCode;
} catch (NameNotFoundException e) {
}
network = new BasicNetwork(
new HttpClientStack(AndroidHttpClient.newInstance(userAgent)));
}
} else {
network = new BasicNetwork(stack);
}
return newRequestQueue(context, network);
}

HurlStack和HttpClientStack 都继承BaseHttpStack抽象类,发起Http请求,HurlStack使用HttpURLConnection执行网络请求,
HttpClientStack使用HttpClient执行网络。
HttpURLConnection比起HttpClient来说更加简单易用,修复了之前的bug。
从Android4.4开始HttpURLConnection的底层实现采用的okHttp。

BasicNetwork 通过对HttpStack的封装,执行网络请求, 看下调用的另一个重载的构造函数。

1
2
3
4
5
6
7
8
9
private static RequestQueue newRequestQueue(Context context, Network network) {
File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);
RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
queue.start();
return queue;
}
public RequestQueue(Cache cache, Network network) {
this(cache, network, DEFAULT_NETWORK_THREAD_POOL_SIZE);
}

创建文件缓存路径,传入一个 DEFAULT_NETWORK_THREAD_POOL_SIZE 常量大小为4,顾名思义是默认网络线程池大小。

接着往下看

1
2
3
4
public RequestQueue(Cache cache, Network network, int threadPoolSize) {
this(cache, network, threadPoolSize,
new ExecutorDelivery(new Handler(Looper.getMainLooper())));
}

这里创建了一个ExecutorDelivery对象,利用Handler把结果回调到主线中。
后面用到时候再看具体实现

接着看上面构造函数中调用的 queue.start()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public void start() {
stop(); // Make sure any currently running dispatchers are stopped.
// Create the cache dispatcher and start it.
mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
//开启网络调度线程
mCacheDispatcher.start();
// Create network dispatchers (and corresponding threads) up to the pool size.
for (int i = 0; i < mDispatchers.length; i++) {
NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,
mCache, mDelivery);
mDispatchers[i] = networkDispatcher;
//开启4个网络调度线程
networkDispatcher.start();
}
}
阅读全文 »
乘风破浪

js声明提升(hoisting)和 TDZ(Temporal Dead Zone)暂时性死区

发表于 2017-03-03 |

先谈谈作用域

  • 什么是作用域:
    就是某个变量有起作用的范围
  • 词法作用域和动态作用域:
    词法作用域:在变量声明的时候,它的作用域就已经确定了;
    动态作用域:在程序运行的时候,由程序的当前上下文(执行环境)决定的;
  • js属于词法作用域
    词法作用域的访问规则:
        先在当前作用域中查找,如果找到就直接使用,如果没有找到,那么就到上一级作用域中查找,如果还没有找到那么就重复这个查找过程,直到全局作用域。

JS中的作用域

  1. script标签构成的全局作用域;
  2. 在js中,函数是唯一一个可以创建作用域的对象;
1
2
3
4
5
6
7
var a1 = "a1";
var b1 = "b1";
function func() {
var a1 = "n1"
console.log(a1); //n1 a1先在func函数内部作用域中查询
console.log(b1); //b1
}

变量和函数的提升

js的执行过程
预解析阶段,变量和函数的提升(声明)

具体的执行阶段:
变量和函数的提升:
js代码是一个从上至下逐步解析的过程,在这个过程中,之前会把所有的变量和函数提前声明。

1
2
3
4
5
6
console.log(a); //undefined 而不是报错
var a = 10;
f1(); //f1 而不是报错
function f1() {
console.log("f1")
}

上面一段js代码,会先把var a与fun函数提前声明,所以代码实际可模拟成先这段代码

1
2
3
4
5
6
7
var a; //变量提前声明,但未定义
function f1() { //函数提前声明
console.log("f1")
}
console.log(a); //声明为定义结果为undefined
a = 10
f1(); //结果为f1

经过上面的解析,结果便可以理解了。
具体会出现的一些问题和几种情况:
1.变量和变量同名的情况,后面的变量会把前面的变量覆盖。

1
2
3
4
5
6
7
8
9
var n1 = "n1";
console.log(n1); //n1
function test() {
console.log(n1)
}
test(); //n1
var n1 = "new n1" //覆盖之前n1的值
console.log(n1); //new n1
test() //new n1

2.函数和函数同名的情况,后面的函数会覆盖前面的函数。

1
2
3
4
5
6
7
8
9
f1(); //20
function f1() {
console.log(10);
}
f1(); //20
function f1() {
console.log(20);
}
f1(); //20

3.函数和变量同名,可以理解为:函数声明提升,而变量的声明不提升(实际上变量也提升了,但是会被函数覆盖)。

1
2
3
4
5
6
console.log(demo); //function demo() {console.log(我是函数)}
var demo = "我是字符串";
function demo() {
console.log("我是函数")
}
console.log(demo); //我是字符串

4.变量提升是分作用域的

1
2
3
4
5
6
var num = 5;
function test1() {
console.log(num); //undefined
var num = 10; //此处函数test1作用域中有声明变量num, 会提升声明, 但不会赋值
}
test1()

5.如果函数是函数表达式定义,那么在做函数声明提升的时候,仅仅只会把var变量的名字提升到当前作用域中

1
2
3
4
5
6
console.log(func) //undefined
var func = function() { // 此处只是提升声明var func, 所以为undefined
console.log("func")
}
var func = "我是MT"
console.log(func); //'我是MT'

深入理解 Hoisting

上面描述的是关于ES5中的声明提升,即使用var声明的变量的声明提升。 有人说在ES6中用
let和const声明的变量不会发生声明提升,那究竟是不是这样呢?
我们先来看一段代码。

1
2
console.log(a); //ReferenceError
let a = 1

运行这段代码会报错,把上面代码中的let换成var就不会报错,会输出undefined, 那这是不是
就说明用let声明的变量不会发生声明提升呢?

我们先假设用let声明的变量不会发生声明提升,看看会发生什么?

1
2
3
4
5
var a = 1;
(function() {
console.log(a)
let a = 2
})();

按照我们预先的假设,如果用let声明变量不会发生声明提升,这里console语句输出的结果应该是1。
但是这里会报ReferenceError的错误。这说明了用let声明的变量不会发生声明提升,这一结论是错误的!
那么用let声明的变量究竟会不会发生声明提升的现象呢?

答案是会发生声明提升,用let/const/class声明的变量均会发生声明提升,既然用let声明的变量会出现声明提升的现象,
那之前的报错(ReferenceError)又怎么解释呢?

区别就在于var和let声明的变量在发生声明提升时,初始化的行为不同导致的,用var声明的变量会初始化为undefined,
而用let声明的变量会保持为未初始化的状态。也就是这样:

阅读全文 »
乘风破浪

前端开发面试题

发表于 2017-03-02 |

转载至

只看问题点这里

本文由我收集总结了一些前端面试题,初学者阅后也要用心钻研其中的原理,重要知识需要系统学习、透彻学习,形成自己的知识链。万不可投机取巧,临时抱佛脚只求面试侥幸混过关是错误的!也是不可能的!不可能的!不可能的!

前端还是一个年轻的行业,新的行业标准, 框架, 库都不断在更新和新增,正如赫门在2015深JS大会上的《前端服务化之路》主题演讲中说的一句话:“每18至24个月,前端都会难一倍”,这些变化使前端的能力更加丰富、创造的应用也会更加完美。所以关注各种前端技术,跟上快速变化的节奏,也是身为一个前端程序员必备的技能之一。

最近也收到许多微博私信的鼓励和更正题目信息,后面会经常更新题目和答案到github博客。希望前端er达到既能使用也会表达,对理论知识有自己的理解。可根据下面的知识点一个一个去进阶学习,形成自己的职业技能链。

面试有几点需注意:(来源寒冬winter 老师,github:@wintercn)

1. 面试题目: 根据你的等级和职位的变化,入门级到专家级,广度和深度都会有所增加。

2. 题目类型: 理论知识、算法、项目细节、技术视野、开放性题、工作案例。

3. 细节追问: 可以确保问到你开始不懂或面试官开始不懂为止,这样可以大大延展题目的区分度和深度,知道你的实际能力。因为这种知识关联是长时期的学习,临时抱佛脚绝对是记不住的。

4. 回答问题再棒,面试官(可能是你面试职位的直接领导),会考虑我要不要这个人做我的同事?所以态度很重要、除了能做事,还要会做人。(感觉更像是相亲( •̣̣̣̣̣̥́௰•̣̣̣̣̣̥̀ ))

5. 资深的前端开发能把absolute和relative弄混,这样的人不要也罢,因为团队需要的是:你这个人具有可以依靠的才能(靠谱)。

前端开发知识点:

HTML&CSS:
对Web标准的理解、浏览器内核差异、兼容性、hack、CSS基本功:布局、盒子模型、选择器优先级、
HTML5、CSS3、Flexbox

JavaScript:
数据类型、运算、对象、Function、继承、闭包、作用域、原型链、事件、RegExp、JSON、Ajax、
DOM、BOM、内存泄漏、跨域、异步装载、模板引擎、前端MVC、路由、模块化、Canvas、ECMAScript 6、Nodejs

其他:
移动端、响应式、自动化构建、HTTP、离线存储、WEB安全、优化、重构、团队协作、可维护、易用性、SEO、UED、架构、职业生涯、快速学习能力

作为一名前端工程师,无论工作年头长短都应该掌握的知识点:

此条由 王子墨 发表在 攻城师的实验室

1、DOM结构 —— 两个节点之间可能存在哪些关系以及如何在节点之间任意移动。

2、DOM操作 —— 如何添加、移除、移动、复制、创建和查找节点等。

3、事件 —— 如何使用事件,以及IE和标准DOM事件模型之间存在的差别。

4、XMLHttpRequest —— 这是什么、怎样完整地执行一次GET请求、怎样检测错误。

5、严格模式与混杂模式 —— 如何触发这两种模式,区分它们有何意义。

6、盒模型 —— 外边距、内边距和边框之间的关系,及IE8以下版本的浏览器中的盒模型

7、块级元素与行内元素 —— 怎么用CSS控制它们、以及如何合理的使用它们

8、浮动元素 —— 怎么使用它们、它们有什么问题以及怎么解决这些问题。

9、HTML与XHTML —— 二者有什么区别,你觉得应该使用哪一个并说出理由。

10、JSON —— 作用、用途、设计结构。

备注:

根据自己需要选择性阅读,面试题是对理论知识的总结,让自己学会应该如何表达。

HTML

Doctype作用?标准模式与兼容模式各有什么区别?

(1)、<!DOCTYPE>声明位于位于HTML文档中的第一行,处于 <html> 标签之前。告知浏览器的解析器用什么文档标准解析这个文档。DOCTYPE不存在或格式不正确会导致文档以兼容模式呈现。
(2)、标准模式的排版 和JS运作模式都是以该浏览器支持的最高标准运行。在兼容模式中,页面以宽松的向后兼容的方式显示,模拟老式浏览器的行为以防止站点无法工作。

HTML5 为什么只需要写 <!DOCTYPE HTML>?

HTML5 不基于 SGML,因此不需要对DTD进行引用,但是需要doctype来规范浏览器的行为(让浏览器按照它们应该的方式来运行);
而HTML4.01基于SGML,所以需要对DTD进行引用,才能告知浏览器文档所使用的文档类型。

行内元素有哪些?块级元素有哪些? 空(void)元素有那些?

首先:CSS规范规定,每个元素都有display属性,确定该元素的类型,每个元素都有默认的display值,如div的display默认值为“block”,则为“块级”元素;span默认display属性值为“inline”,是“行内”元素。

(1)行内元素有:a b span img input select strong(强调的语气)
(2)块级元素有:div ul ol li dl dt dd h1 h2 h3 h4…p

(3)常见的空元素:
    <br> <hr> <img> <input> <link> <meta>
    鲜为人知的是:
    <area> <base> <col> <command> <embed> <keygen> <param> <source> <track> <wbr>
不同浏览器(版本)、HTML4(5)、CSS2等实际略有差异

参考:

页面导入样式时,使用link和@import有什么区别?

(1)link属于XHTML标签,除了加载CSS外,还能用于定义RSS, 定义rel连接属性等作用;而@import是CSS提供的,只能用于加载CSS;
(2)页面被加载的时,link会同时被加载,而@import引用的CSS会等到页面被加载完再加载;
(3)import是CSS2.1 提出的,只在IE5以上才能被识别,而link是XHTML标签,无兼容问题;

介绍一下你对浏览器内核的理解?

主要分成两部分:渲染引擎(layout engineer或Rendering Engine)和JS引擎。
渲染引擎:负责取得网页的内容(HTML、XML、图像等等)、整理讯息(例如加入CSS等),以及计算网页的显示方式,然后会输出至显示器或打印机。浏览器的内核的不同对于网页的语法解释会有不同,所以渲染的效果也不相同。所有网页浏览器、电子邮件客户端以及其它需要编辑、显示网络内容的应用程序都需要内核。

JS引擎则:解析和执行javascript来实现网页的动态效果。

最开始渲染引擎和JS引擎并没有区分的很明确,后来JS引擎越来越独立,内核就倾向于只指渲染引擎。

常见的浏览器内核有哪些?

Trident内核:IE,MaxThon,TT,The World,360,搜狗浏览器等。[又称MSHTML]

Gecko内核:Netscape6及以上版本,FF,MozillaSuite/SeaMonkey等

Presto内核:Opera7及以上。      [Opera内核原为:Presto,现为:Blink;]

Webkit内核:Safari,Chrome等。   [ Chrome的:Blink(WebKit的分支)]

详细文章:浏览器内核的解析和对比

html5有哪些新特性、移除了那些元素?如何处理HTML5新标签的浏览器兼容问题?如何区分 HTML 和 HTML5?

HTML5 现在已经不是 SGML 的子集,主要是关于图像,位置,存储,多任务等功能的增加。
    绘画 canvas;
    用于媒介回放的 video 和 audio 元素;
    本地离线存储 localStorage 长期存储数据,浏览器关闭后数据不丢失;
    sessionStorage 的数据在浏览器关闭后自动删除;
    语意化更好的内容元素,比如 article、footer、header、nav、section;
    表单控件,calendar、date、time、email、url、search;
    新的技术webworker, websocket, Geolocation;

移除的元素:
    纯表现的元素:basefont,big,center,font, s,strike,tt,u;
    对可用性产生负面影响的元素:frame,frameset,noframes;
    支持HTML5新标签:
    IE8/IE7/IE6支持通过document.createElement方法产生的标签,
    可以利用这一特性让这些浏览器支持HTML5新标签,
    浏览器支持新标签后,还需要添加标签默认的样式。
    当然也可以直接使用成熟的框架、比如html5shim;

<!--[if lt IE 9]>
<script> src="http://html5shim.googlecode.com/svn/trunk/html5.js"</script>
<![endif]-->
  • 如何区分HTML5: DOCTYPE声明\新增的结构元素\功能元素

简述一下你对HTML语义化的理解?

用正确的标签做正确的事情。
html语义化让页面的内容结构化,结构更清晰,便于对浏览器、搜索引擎解析;
即使在没有样式CSS情况下也以一种文档格式显示,并且是容易阅读的;
搜索引擎的爬虫也依赖于HTML标记来确定上下文和各个关键字的权重,利于SEO;
使阅读源代码的人对网站更容易将网站分块,便于阅读维护理解。

HTML5的离线储存怎么使用,工作原理能不能解释一下?

在用户没有与因特网连接时,可以正常访问站点或应用,在用户与因特网连接时,更新用户机器上的缓存文件。
原理:HTML5的离线存储是基于一个新建的.appcache文件的缓存机制(不是存储技术),通过这个文件上的解析清单离线存储资源,这些资源就会像cookie一样被存储了下来。之后当网络在处于离线状态下时,浏览器会通过被离线存储的数据进行页面展示。

如何使用:

1、页面头部像下面一样加入一个manifest的属性;
2、在cache.manifest文件的编写离线存储的资源;
    CACHE MANIFEST
    #v0.11
    CACHE:
    js/app.js
    css/style.css
    NETWORK:
    resourse/logo.png
    FALLBACK:
    / /offline.html
3、在离线状态时,操作window.applicationCache进行需求实现。

详细的使用请参考:

HTML5 离线缓存-manifest简介

有趣的HTML5:离线存储

阅读全文 »
乘风破浪

闭包

发表于 2017-02-28 |

闭包(closure)是javascript语言的一个难点,也是它的一个特色,很多高级应用都要依靠闭包实现

概念

首先了解一个JavaScript变量的作用域, 无非就是两种: 全局变量和局部变量。 JavaScript语言的特殊之处,
就是在函数内部可以直接读取全局变量。另一方面,在函数外部自然无法读取函数内的局部变量。但是通过闭包,可以
在函数外面访问到内部的变量! 比如

function f1() {
    var n = 999;
    function f2() {
        alert(n)
    }
}

函数f2就被包含在函数f1内部,这时f1内部的所有局部变量,对f2都是可见的。但是反过来就不行,f2内部的局部变量
对f1就是不可见的。这就好似javascript语言特有的”链式作用域”结构(chain scope),子对象会一级一级地向上
寻找所有父对象的变量。所以,父对象的所有变量对子对象都是可见的,反之则不成立。

所以,我们说的闭包,就是能够在外部访问函数内部的函数。在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁

用途

闭包允许将函数与其所操作的某些数据(环境)关连起来。这显然类似于面向对象编程。在面向对象编程中,对象
允许我们将某些数据(对象的属性)与一些或者多个方法相关联。因此,一般来说,可以使用只有一个方法的对象的地方,
都可以使用闭包
。

读取函数内部的变量

比如上面的例子

是变量的值始终保存在内存中

function f1() {
    var n = 999;
    function f2() {
        console.log(n++)
    }
    return f2;
}
var result=f1()

这里我们外部调用result函数,可以不断增加内部的n值,实际上函数f1中的局部变量n一直保存在内存中,
并没有在f1调用后被自动清除

原因: f1是f2的父函数,而f2被赋给了一个全局变量,这导致f2始终在内存中,而f2的存在依赖于f1,因此f1也
始终在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收。

用闭包模拟私有方法

javascript并不提供原生的支持,但是可以使用闭包模拟私用方法。私有方法不仅仅有利于限制对代码的访问,
还提供了管理全局命名空间的强大能力,避免非核心的方法弄乱了代码的公共接口部分。

下面的示例展现了如何使用闭包来定义公共函数,且其可以访问私有函数和变量。这个方式也称为模块模式(module pattern):

var Counter = (function() {
    var privateCounter = 0;
    function changeBy(val) {
        privateCounter += val;
    }
    return {
        increment: function() {
            changeBy(1)
        },
        decrement: function() {
            changeBy(-1)
        },
        value: function () {
            return privateCounter
        }
    }
})();

在循环中创建闭包

当我们为一组对象进行操作的时候,比如注册事件,如果我们这样写:

function showHelp(help) {
    document.getElementById('help').innerHTML = help
}

function setupHelp() {
    var helpText = [
        {'id': 'email', 'help': 'Your e-mail address'},
        {'id': 'name', 'help': 'Your full name'},
        {'id': 'age', 'help': 'Your age (you must be over 16)'}
    ];
    for (var i = 0; i < helpText.length; i++) {
        var item = helpText[i];
        document.getElementById(item.id).onfocus = function () {
            showHelp(item.help);
        };
    }
}

<body onload="setupHelp()">

运行这段代码后,您会发现它没有达到想要的效果。无论焦点在哪个输入域上,显示的都是关于年龄的消息。
该问题的原因在于在 onfocus 的回调被执行时,循环早已经完成,且此时 item 变量已经指向了 helpText
列表中的最后一项。

修改如下:

function showHelp(help) {
    document.getElementById('help').innerHTML = help
}

function makeHelpCallback(help) {
    return function () {
        showHelp(help)
    }
}

function setupHelp() {
    var helpText = [
        {'id': 'email', 'help': 'Your e-mail address'},
        {'id': 'name', 'help': 'Your full name'},
        {'id': 'age', 'help': 'Your age (you must be over 16)'}
    ];
    for (var i = 0; i < helpText.length; i++) {
        var item = helpText[i];
        document.getElementById(item.id).onfocus = makeHelpCallback(item.help)
    }
}

使用闭包的注意点

  • 由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。
    解决方法是,在退出函数之前,将不使用的局部变量全部删除。

  • 闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,
    把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),
    这时一定要小心,不要随便改变父函数内部变量的值。

乘风破浪

Flex 布局教程:语法篇

发表于 2017-02-20 |

网页布局(layout)是CSS的一个重点应用。

布局的传统解决方案,基于盒状模型,依赖 display属性 + position属性 + float属性。它对于那些特殊布局非常不方便,比如,垂直居中就不容易实现。

2009年,W3C提出了一种新的方案—-Flex布局,可以简便、完整、响应式地实现各种页面布局。目前,它已经得到了所有浏览器的支持,这意味着,现在就能很安全地使用这项功能。

Flex布局将成为未来布局的首选方案。本文介绍它的语法,下一篇文章给出常见布局的Flex写法。

以下内容主要参考了下面两篇文章:A Complete Guide to Flexbox 和 A Visual Guide to CSS3 Flexbox Properties。

Flex布局是什么?

Flex是Flexible Box的缩写,意为”弹性布局”,用来为盒状模型提供最大的灵活性。
任何一个容器都可以指定为Flex布局。

.box{
  display: flex;
}

行内元素也可以使用Flex布局。

.box{
  display: inline-flex;
}

Webkit内核的浏览器,必须加上-webkit前缀。

.box{
  display: -webkit-flex; /* Safari */
  display: flex;
}

注意,设为Flex布局以后,子元素的float、clear和vertical-align属性将失效。

基本概念

采用Flex布局的元素,称为Flex容器(flex container),简称”容器”。它的所有子元素自动成为容器成员,称为Flex项目(flex item),简称”项目”。

Flex容器

容器默认存在两根轴:水平的主轴(main axis)和垂直的交叉轴(cross axis)。主轴的开始位置(与边框的交叉点)叫做main start,
结束位置叫做main end;交叉轴的开始位置叫做cross start,结束位置叫做cross end。
项目默认沿主轴排列。单个项目占据的主轴空间叫做main size,占据的交叉轴空间叫做cross size。

容器的属性

以下6个属性设置在容器上。

  • flex-direction
  • flex-wrap
  • flex-flow
  • justify-content
  • align-items
  • align-content

flex-direction属性

flex-direction属性决定主轴的方向(即项目的排列方向)。

.box {
  flex-direction: row | row-reverse | column | column-reverse;
}

flex-direction

阅读全文 »
乘风破浪

android自定义view

发表于 2017-02-20 |

为什么要自定义控件:
1、特定的显示风格
2、处理特有的用户交互
3、优化我们的布局
4、封装等…

如何自定义控件:

1、自定义属性的声明与获取
2、测量onMeasure
3、布局onLayout(ViewGroup)
4、绘制onDraw
5、onTouchEvent
5、onInterceptTouchEvent (ViewGroup)

自定义属性的声明与获取

1、分析需要的自定义属性
2、在res/values/attrs.xml定义声明
3、在layout.xml文件中进行使用
4、在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
<resources>
// 自定义TextView
<declare-styleable name="TextView">
// name 是名称,format是格式 color(颜色),string(文本),dimension(sp,dp)...
<attr name="textColor" format="color"/>
<attr name="text" format="string"/>
<attr name="textSize" format="dimension"/>
</declare-styleable>
</resources>
```
```
public TextView(Context context) {
this(context,null);
}
public TextView(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
public TextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
// 获取TypedArray
TypedArray typedArray = context.obtainStyledAttributes(attrs,R.styleable.TextView);
// 获取文本
mText = typedArray.getText(R.styleable.TextView_text);
// 获取文字颜色
mTextColor = typedArray.getColorStateList(R.styleable.TextView_textColor);
// 获取文字大小
mTextSize = typedArray.getDimensionPixelSize(R.styleable.TextView_textSize,mTextSize);
// 回收
typedArray.recycle();
}

onMeasure
1、EXACTLY, AT_MOST, UNSPECIFIED
2、MeasureSpec
3、setMeasuredDimension
4、requestLayout()

onLayout(ViewGroup)
1、决定子View的位置
2、尽可能将onMeasure中一些操作移动到此方法
3、requestLayout()

绘制onDraw
1、绘制内容区域
2、invalidate(), postInvalidate()
3、Canvas.drawXXX
4、translate、rotate、 scale、 skew
5、save(), restore()

onTouchEvent
onInterceptTouchEvent (ViewGroup)

乘风破浪

为什么要写博客?

发表于 2017-02-20 |

一个选择

我知道现在可能说这话有点不合时宜,毕竟博客时代都已经过去了,再号召大家用过时的东西,是不是有点逆流而上?

我曾经也问过自己这个问题,但是我觉得,博客时代过去,跟我们要开博客是没有多大关系的,就好像你的读书时代已经过去你就不再读书一样。

判断一件事情值不值得去做有一个方法:在一张白纸的左边写不值得做的原因,然后在右边写值得做的原因,写完一比较,一权衡,自然能够得出结果。

大家都成年人了,你会觉得这样思考分析总结的过程才是正确的思考的方法吧?

所以,我在这里列出要写(独立)博客的原因,供大家去选择,然后填在你白纸的右边。

注意,我不是给你一个建议,而是提供一个选择,这个选择蕴藏着我也不知道的可能。

博客的内容

写博客不难,你可以当作是生活的记录,但是这样的记录没有任何的意义。写要对得住写本身,写出来的东西应该是思考的结果。我认为,如果你要开一个博客,博客的内容应该是这样的:

  • 不是生活杂记、不是流水账、不是牢骚、不是抱怨、不是心情琐记……;
  • 有目的地写,要务实,追求质量;
  • 承认真实的自己,不要吹嘘,不要装逼,无需讨好读者;
  • 记录自己学习、思考、总结的过程;
  • 分享你的故事、所得、感想、经验;

值得写的原因

以下是一个清单,可以根据自己的情况匹配,然后选择。

重新认识自己

是不是很久没有跟自己对话了?

你可以尝试从回答一些问题开始,将你过去要回避的问题写下来,例如就可以从这个九个问题开始:

  1. 请你介绍一下你自己,你是个什么样的人?
  2. 你有什么理想吗?这个理想是怎么形成的?
  3. 你理想的伴侣关系是什么样的?你自己在这个伴侣关系中扮演什么样的角色?要承担什么样的责任?
  4. 你理想的事业是什么,你正在做的工作符合你的事业理想吗?这份工作对你的意义是什么?
  5. 你对亲子关系怎么看?对你来说,什么是一个理想的父亲(母亲),你期望自己成为这样一个理想父亲(母亲)吗?
  6. 你对钱怎么看,你认为赚到多少钱是足够的?如果你明天一早醒来,已经有足够的钱,你将会如何继续安排自己的生活?
  7. 对你来说,什么是理想的性生活?什么是理想的性道德,在你的性道德观中,什么样的性生活是禁忌的,需要避免的,什么样的性生活是美好的,需要得到鼓励和发展的?
  8. 你的择友标准是什么?什么样的人你会愿意交往,什么样的人你会拒绝和他交往?
  9. 你对死亡怎么看?你希望自己活到多少岁,你准备怎么度过从现在到死亡的这段时间?如果你要立遗嘱,这份遗嘱会怎么写?

以上的这九个问题摘自《很少人能顺畅回答这9个问题——心理治疗刚开始医生常常会先问你的 》by 李孟潮。

这些问题的答案你可以选择不发,但是我强烈地建议写下来,只有在写的时候你才可以慎重地思考这些问题,而不会回避跳过或者留下空白,这是接受自己的第一步。

提供持续学习的动力

例如,我为自己设限每天写一千字,信息的不断输出给我带来恐惧,我害怕有一天我写无可写,于是我不停地阅读,通过个人的知识管理促使自己不断学习,提高核心竞争力。

积累更多的知识

写并不是单纯的写。

例如你写着写着,你突然忘记了一个概念,于是上网找,找回来这个概念的时候,你重温这个概念,可能还会顺便看了一下这个概念的其他东西。

例如你需要获取第一手的资料,寻找信息来源本身就是一个知识积累的过程,同时,你慢慢就学会了鉴别知识:什么是没有用的心灵鸡汤,什么是不值得关注的吐槽名人,还有,在这个过程中,你还养成你的心智。

提高将事情讲清楚的能力

很多东西你以为懂了,但当你在写下来的时候,你就觉得无从下手了。

如果一件事情你不能讲清楚,十有八九你还没有完全理解。

将事情写下来,慢慢就可以提高你的逻辑思维能力,分析能力,写会迫使你在你脑中搭建一个有条理的框架。例如我写这篇文章一样,我就将值得写博客的原因一点一点地罗列出来,事情就更加清晰,你也可以更好的思考问题。

分享带来的连锁反应

“通过分享,你获得了直接而快速的回报,你最终或许会发现你已将版权和“保留所有权利”抛诸脑后。新的经济学准则是:参与你作品的人越多,回报越高。在分享主义里,如果你愿意你可以保留所有权,但是我乐于分享。” by 毛向辉 《分享主义:一场思维革命》

互联网精神其中最重要的就是分享主义,基于分享主义,你可以享受到社会化及互联网给你带来的种种便利和好处,你分享了一个知识,你就成为了互联网中的一个点,这个点的大小由你自己来决定,互联网的大潮会将你的这个点推送到它所能触及的每个角落,让需要的人得到,同时,你的这个点也会继续扩大,连接到整个网络,这个点有可能连接成一张网,而你就是这张网的中心。

帮你找到志同道合的人

在微博,在朋友圈,你可能找不到跟你志同道合的人,而在博客,你可以通过看他的几篇文章就迅速地理解认同这个人,即使你没有见过这个人,但你也可以通过这种关联来相互学习。

如果你在一个领域有相当的了解,你将这些内容发在网络上,网络上跟你志趣相投的人也会被你吸引过来,根据吸引力法则,你是怎样的人你就被怎么样的人吸引,这就是博客所能赋予你的魅力。

即使博客没有被他人关注,我们依然可以找到同好,你可以自己将博文转载到其他站点,人们会通过搜索引擎找到你,有邮件、微博等工具,我们不乏与他人交流的途径。by Gabriel Weinberg《Why I blog》

记录成长

隔一段时间,你再回头看你写的博客,你会发现自己正在通过这样的方式在不断的成长,这种成长在自己眼里是一种财富,在别人眼里是一张地图,你得到了收获,不断修正自己的错误,别人得到了指引,避免走弯路。

更多的情况是当你回望自己的时候你会发现自己是一个傻逼,so what,that is what I am!

培养持续做一件事情的能力

开始是坚持,后来是习惯,接着喜欢。以后当有人对你说,「你写那么多有用的东西,你真的很厉害啊!」你可以笑而不语,也可以大声说道:「你妹,你不知道我开始的时候多么痛苦!」

让你长久地去跑步,你可能做不到;让你每个月看一本书,你也可能做不到;但让你持续地写一个博客,你可以做得到。

你不相信?你不试试你怎么知道?

默默地持续做一件事是一种难得的能力,也是一种难得的品质。

讨论反思

每人都会有思维的盲点,就好像这篇文章一样,可能你觉得我可能说得不对,你可以反驳我,我欢迎这种讨论,因为讨论的过程中会产生各种的思维的碰撞,这种碰撞会让你反思,也会激发出你新的灵感,这种讨论反思给自己的带来巨大的受益。

互联网给你的反馈就是让你承受更多,接受更多,成为一个更好的人。

搜寻到你意想不到东西

世界不止是你的家,你的公司,你的朋友圈,你应该去发现一个更大的世界,通过写博客,你会知道世界上还有很多人像你一样在写博客,这些人和知识正在世界的某个角落在等着你。

例如,在写这篇文章的过程中,我才知道了Gabriel Weinberg,我才要将阳志平的博客重读一遍。写的过程会让你有很多新的发现,这些新的发现都值得你去再写下来,总结分享出去。

一个人在做一件属于自己的事

很多你认为自己很牛逼的事情都是自己一个人做出来。

别人在刷微博,你在看书,别人在看穿越剧,你在学英文,别人在去唱K,你在写个人总结。吃饭也要找同伴,出游要找同伴,看电影要找同伴,你上一次一个人在做一件属于自己的事是在什么时候?

如果你想要清晰地思考,就必须远离人群。但是走得越远,你的处境就会越困难,收到的阻力也会越大。因为你没有迎合社会习俗,而是一步步地与它背道而驰。如果自己就是潮水的一部分 ,怎么能看见潮流的方向呢?你只能永远保持质疑,问自己,什么话是我不能说的?为什么?——Paul Graham《不能说的话》

互联网的身份识别

一个长期的价值博客是一份很好的简历。这里的“简历”并非是狭义上的求职简历,毕竟现在还没有到价值博客的时代,很多人写博客都是到处转载或者干脆碎碎念,正因此面试官未必拿个人博客当成了解一个人的更可靠窗口。

这里的“简历”是指一个让别人了解自己的窗口,虽然我们未必做得到像罗永浩、Keso这样的博客,个人的影响力已经足以支撑出一份事业(牛博和5gme),但至少你会因此而结识更多的人,你的博客价值越高,你结识的人就越牛,跟牛人交流又会让你的眼界得到极大的开阔,打开一扇又一扇你原本不知道的门,于是你就变得更牛… 这是一个良性循环。by 刘未鹏

最后

你可能想不到在白纸的左边(不值得写博客的原因)写什么了,想不到写个「博客时代已经过去」或者「我没有时间」也可以,但与此同时,你也可以用那些时间去思考一下「怎么做到长期写一个价值博客」。

文章转载至

mars

mars

要输输给追求,要嫁嫁给幸福。

7 日志
5 标签
© 2018 mars
由 Hexo 强力驱动
主题 - NexT.Mist