December 2009 Archives

2009 in Review

| No Comments
2009年对于我来说,是自己充分利用时间体验、学习并且收获的重要一年。同时思考问题、做事情的方式通过学习和了解,有不小的提高。

一月,工作里可以由自己主导并实现的技术部分开始让自己获得空前的成就感,公司年会的工作则让自己第一次发现了自己各个方面能力所能到达的空间是如此之大。

二月,主要时间放在Bugzilla的研究上、简单的体会都放在了OpenParty"有狐"分享的话题中。

三月,开始一个自己主导的系统工程的设计工作;月底购买了cnborn.net的域名,筹划了近2年的独立Blog生涯终于正式开始。

四月-五月在青年旅社住了月余。本来只是作为自己放松心情和切换环境的尝试,没想到从这环境中接触到的众多有趣的人和事,成了自己开阔视野,改变思想的巨大契机。从未想到过,自己曾经的世界,是如此狭小;太喜欢 Getting Real 这本书,于是就把零散的翻译又重新修订整理了一下,然后发布。

六月,月初的 Google Developer Day 是那种难得的盛大、却又感到无比亲切的活动,有幸能上台做了个简短发言。月底完成 CheckNerds API 的设计。

七月,发布自己一直就想做的一个PyGame小游戏

八月,在扬州、镇江的三天旅行;正式发布 CheckNerds Labs 开源项目

九月,对于自己未来方向思考最多的时间,同时下定了决心

九月底-十月初,在柬埔寨独自背包旅行,详情请见《柬埔寨背包记》系列文章

十一月,自己独自主导、设计、实施的项目最终交付,过程中对于流程、需求以及其它诸如高效交流等方面的学习,远远超过技术上的收获。对于生产力工具的设计流程、有了无比清晰的体会和认识。

十二月,到了自己想要做些什么事情的时刻,脱离了一个对自己来说足够舒服却不能以最高效率推动自己进步的环境, 告别了原来的公司与行业,来到新公司,意味着新的机会、方向和新的开始。这其中的经历和思考本来可以单独写些东西的,但是超群的一篇文章《致年轻朋友的一封信》里所阐述的内容近乎完全就是我心中所想,而且我俩的经历也很相似,所以就不再另写,请大家阅读超群的这篇优秀文章。

年度影片: The Curious Case of Benjamin Button,震撼人心的体验,充满了伤逝情怀的故事,而影片描述"在异乡探险,发觉生活真谛"的短暂一瞬,成为震撼自己以及诸多朋友的难以忘怀的时刻。
年度书籍:《达摩流浪者》,如果说有什么东西,可以让一个人觉得自己可以完全打破对于自己所生活的环境以及精神世界的认知,进入到一个新的、前所未见的世界并认识自己曾经的狭隘和世俗的话,这本书就是这样的东西。其中或许很多人认为并不够"宗教"的宗教描写,却为普通读者更好地阐述了这种精神生活的状态和体验。
年度音乐:Beethoven - Piano Concerto No. 5 in E-flat major 'Emperor' (Op. 73) / Symphony No. 3 in E flat major 'Eroica' (Op. 55) - 恰好今年听的一场音乐会就是这两部反复在听的音乐

自己越加发现知识交流与分享的重要性、积极参与并记录了一整年的OpenParty活动,结识了很多充满精力与热情、热爱并坚持着自己的梦想的朋友,绝对是今年精彩的一笔。开阔了自己的视野和思路,突破了无论是知识、地域等曾经束缚自己的很多障碍,发现了更多新大陆;自己思考问题的方式更加理性化、更具有条理性、更加稳重;看了非常多的TED视频,难能可贵的是对其中很多内容的理解对于自己有非常大的收益和帮助

读着自己2009年的精确到每天的日志记录,发现那些琐碎的记录中体现这自己的对于时间点滴的把握还是值得肯定的。年初计划中基本的目标都完成了:职业大方向的确定;参与更多的开源社区类活动;拥有更多的冒险精神和体验;结识了很多朋友;只看著作级的书/电影,节省时间的同时获得最大收益。

2010年对于自己最重要的,就是在新的方向上更加专注、节约有限的注意力,将以往单纯的节约时间,变成更加高效的利用时间。同时继续其余各个方向上的努力,这就是对我来说,完美的计划了。

OpenParty "岩上"

| No Comments
12月份的活动聚集了来自Apple、设计、架构、出版等行业的大牛们前来分享话题,所以来到现场参与活动的人数达到了 OpenParty 活动的新高峰,100多人几乎坐满了宽敞的ThoughtWorks办公室。

恰逢 OpenParty 成立两周年,现场还播放了温馨的回忆片花,感谢长期以来大家对于OpenParty的热爱与支持。

重点讲述一下资深的Apple专家 @hengdm 带来的重量级话题,iPhone软件开发设计流程。(现场投票58票,为 OpenParty 活动历史以来最高,可见此话题多么受欢迎)

---

"用设计Windows软件的心态去做OS X应用软件,下场必然失败;
用设计Windows软件的心态去做iPhone应用软件,下场必然失败"

设计师以及设计团队的灵光闪现,固然可以促成一款好软件的诞生,但是这种灵光闪现却可能带来更多无法管控的内容,影响软件的整体质量和体验。所以,苹果公司在软件产品设计流程的管理中,严格控制了规范流程,尽力避免软件受到这些灵光闪现的影响,而从一个理性、量化、可以阐述的角度,来规范软件的用户体验和质量。

如果用一种苹果独有的体验标准来衡量软件的体验和质量的话,那就是"让用户感觉自己使用这个软件的动作都变得十分优雅"。


总体上说,iPhone User Interface Design分为四个部分

  • 平台的范例:了解用户的使用情景和使用习惯
  • 软件产品定义:明确软件的功能目标
  • 设计及原型
  • 对软件的打磨与改进

Coding 的部分,所耗费的时间在整个的设计流程中,绝对不是最多的。通常夹杂在第三步和第四步之间。


"我们造就工具,工具也造就我们"

作为产品设计人员,一个必要的宗旨是做有水准的产品,对用户负责。用户从来就不是你所想象的那样,会因为"素质低"而使用并习惯设计不好的产品。现实的情况是,如果产品的设计人员足够认真地进行产品的设计和打磨,那么就会有足够多认真的用户,来一起使用并且认同一款产品,从而形成正常的良性循环。端木非常不赞同国内脑残产品的做法,对用户的不负责,就是对自己的不负责。

iPhone的革命性创新,对于软件以及交互式体验设计提出了新的需求。内置电子罗盘、GPS、Internet浏览器等各种功能和设备的结合,为产品设计者带来了更多的用户信息,而如何应用、认知这些信息从而产生有价值的产品及用户体验,是关键所在。

从操作体验上进行阐述:软件的操作界面,从1970年至今经历了打孔纸带→终端界面→图形界面这三个大的演变,总体的发展历程来看,用户的行为,离直接操作数据越来越接近,操作方式也逐渐由抽象变得更加直接。iPhone更是第一次给用户以"直接用手来触控数据"的体验,而非像传统的操作方式还需要一个中介媒介(输入设备)来进行,这个技术上的不大的变化,带来了感知体验上巨大的提升。

从一些具体的设计细节来看iPhone给用户界面体验带来的变化:

  • 鼠标点击,所影响的尺寸为1x1px
  • 手指触摸,所影响的尺寸为22x22-55x55px
  • 滚动条在以往的手持移动设备中(如Windows Mobile),还存在。但是在苹果的概念中,滚动条的设计与用户直接的体验设计向背离。用户可以用手指直接滚动屏幕,与常识性的概念完全一致。同时滚动条的样式还存在,但是仅仅作为一个信息指示用的工具(提示页面位置)的工具而存在了。
  • 同理,下拉菜单也变成了转轮,完全摒弃了在大屏幕系统上常见的各种GUI控件先入为主的概念,而完全从用户操作的角度考虑,来达到用户可以直接操作并反馈的效果,而不是纠结与细小的,难以控制的组件中。

可以说,抛弃以往由输入设备、遗留GUI设计等原因的操作定式,而将对于用户的操作变得更直接以后,带给用户的提升和震撼,是可以想象的。

不过请注意,上面讲述的都是将操作界面变得更直接更加易于使用。但是这种情况并不适用于100%的应用程序。合理应用间接操作的设计,也可以达到良好的效果。那么什么样的应用需要并不那么直接的,也就是刻意被复杂化的操作呢?举个例子,比如在游戏中,如果玩家可以通过手指快速点击游戏中的目标,那么游戏就变得毫无挑战性了,所以游戏中,将操作间接处理,让玩家需要左右摇摆位置变换目标,再按发射按钮开火,这样以来,间接的操作就给游戏带来了挑战性,组成了游戏乐趣的核心。由此可见,针对不同的应用,提供不同的设计思想和操作模式,是十分重要的。


明确软件设计的目的:不是功能的大集合,而是明确要解决一个怎样的困难,为用户提供一个具体的解决方案。

界定应用程序的三大基本要素:这个应用程序与其它应用程序的不同之处/需要解决的问题/所面向的用户群

Context/使用情景:不同的用户所需要的使用界面的差异。列举了商务人士/消防员这两个不同的职业,倘若使用iPhone应用程序的话,对于应用程序界面感受的具体需求会是怎么样的。

通过iPhoto软件桌面版和iPhone版本的设计区别来重点讲述,设计软件中基本要素的体现:

iPhoto桌面版作为一款功能全面的软件,其基本的功能介绍有几十项之多,基本覆盖了一个通常用户对于一个好的相片管理软件的需求。其功能可以主要概括为以下三个项目,组织照片、编辑照片、分享照片。

那么,iPhone的版本,是否也应该照搬这个设计呢?答案是不,有如下的几个原因:

  • 首先,单单就界面元素的设计来说,很多在桌面版本中应用得十分出色的界面元素,如操作面板、照片显示风格等,都不适用于直接搬入iPhone版本中,画面太小,会导致操作起来不友善。
  • 从功能上来说,用户时候需要在移动中,从手持设备上认真的组织自己的照片?是否需要在路上用手指细细地编辑自己的照片呢?答案基本上是否定的。不过,分享照片这个功能,确实是iPhone版本可以大放异彩的地方,所有人的都会有分享照片的场合,而一个随身携带的设备,恰恰是这个功能应用最好的载体和实现者。

思考到这里,这个软件就有了一个明确的设计方向。iPhone版本的iPhoto软件,应该有清晰、流畅的浏览体验,并且可以让用户迅速和别人分享该照片。

这就体现了iPhone软件设计的一个很重要的宗旨,选取最少的功能,简单就是美。但注意这一切是在了解用户需求,并集中精力去解决用户所遇到的问题的基础之上。而与之理念相反的、功能复杂、冗长的产品,绝称不上是个好产品。

整个设计中,还包含了无数的细节,而高的价值,往往都体现在这些细节中。通常来讲,AppStore中的优秀软件,都用了大约60%-70%的时间来进行产品的设计和定义,实际的编码时间所占的比例,要远远少于通常的软件项目。这就意味着,iPhone平台上的优秀软件在用户交互以及满足用户需求的方面做的更好。从而让整个平台以及平台生态系统的易用性体验,达到一个新的高度。而苹果这些设计理念,都注入到了 iPhone Human Interface Guideline 这部文档中,此文档堪称iPhone开发人员的圣经,不单单介绍了iPhone开发中这些元素、以及针对用户体验相关的要求,更是把与此相关的来龙去脉全部呈现并细致讲解,是苹果无数理念的结晶。


提供优秀操作特性的基础元素:
  • 多点触摸
  • 虚拟键盘,可根据应用需求进行定制,摆脱了实体键盘在体积以及直观程度上的困扰
  • 可隐藏的控件
  • 减少用户输入操作,自动提示和补全,提供默认选择

根据掌上设备的特点,捕捉用户的行为习惯,为用户提供最好的体验。端木在讲述时举了这样一个例子:用户在早上的某个时段、特定位置经常查看一些内容;晚上在某个位置特定时段查看一些其它内容,在几天之后,软件自动识别出相应的规律,并自动为用户展现相应的内容。用户会觉得软件的人性化程度非常高,从而对产品有着更强的投入感。所以,利用好获得的用户信息资源,可以有很好的成效。

具体到控件设计应用的一些指示:
  • 工具栏加材质,图标一定要保持简单
  • 可点击的控件都带有触感
  • 导航
    • 指示层级位置,直观
    • 显示目标,回退按钮名称为上一级内容名称
    • 使用标准控件
  • 列表
    • 使用图标,便于用户记忆
    • 两种不同的展开箭头:进入新界面和不进入新界面的
  • Tab的使用可以减少层级结构,有效组织内容,可以参考iPod应用程序下面的Tab栏

苹果的设计美学体现在很多细小的地方,一个非常明显的例子就是联系人管理中的联系人详细信息页面:这个页面设计中的行间距、颜色搭配、版式等等都是苹果美学元素的最佳体现。端木限于时间关系没有过多描述,简单的说,行间距中文字实际上并不是居中的,而是下方比上方空白多出一个像素,原因是这样的视觉效果给人以更加稳妥的感觉。


针对不同应用程序类型,使用图形界面元素所需要注意的技巧:
iPhoneDesign.png
严肃类工具:直观的界面,便于操作,使用标准化的控件
有趣类的工具:可以加入些个性活泼的因素
有趣的娱乐软件(如游戏):不能使用标准控件,在界面上提供足够的新意和感觉
严肃的娱乐软件(如iTunes商店):可以适当使用一部分图形来提高体验认知。可以用动画来帮助用户理解行为,接受反馈


创造实用的小工具:最受欢迎的工具都是单一工具,只做一件事,数据不要太复杂。

讲述设计过程中的纸上原型设计时,讲到了Things团队精彩的设计过程。而细致的设计流程通常需时一个月,这是非常重要的过程。

界面上的打磨与改进:加入软件自动提示、根据用户行为提供足够反馈等细节功能的提升。但注意要避免:
  • 加入动画不意味着全部界面元素都在动
  • 设置有意义的动画
  • 各种视觉效果要以不影响用户的主要任务为前提


总结:
  • 产品给予用户直接的操作体验,在可操作元素上进行视觉反馈。
  • UE>UI
  • 最好产品的元素定义,经过仔细的设计流程,最终生产、发布软件产品,如此才能保证有一个良好用户体验的产品设计


总体来说,这个话题从主旨上强化了很多我在 Getting Real 里面学习到的理念,又经历了一个打破条条框框,从新的方向开拓思维的旅程。也感受到了苹果的团队,对于细节已经不再是一种要求,而几乎就是一种痴迷,绝对的痴迷。这种乔布斯气质领导下,很难出现质量不高的产品。当然,细节只是关键环节的其中一环:正确的方向和思路,完善而严谨的细节设计,良好的用户体验,这些要素缺一不可,而成功地把握它们,并在其中找到绝对的平衡,才是苹果的成功之道。端木恒的演讲非常精彩和有感染力,slide的设计更是出色。实为一次绝佳的收获。

接下来又收听了西乔带来的"理性的设计"话题,由于时间和精力有限,无法做非常细致的呈现了,感兴趣的朋友可以移步这里查看现场视频录像

柬埔寨背包记三:风雨中的吴哥

| 6 Comments
我于09年9月27日-10月8日在柬埔寨独自背包旅行,其间的经历和收获数不胜数。相关的经历我曾在10月底在 Beijing Open Party 上有过一次交流分享,现在把旅行途中以及后来的一些记录陆续发布到这里,请感兴趣的朋友关注这里的更新。要查看本系列更多文章,欢迎点击页面右侧的Cambodia标签。除正文中的照片以外,还可以在我的Footbig以及我的豆瓣相册中欣赏因篇幅原因未能在文章中出现的照片上一次交流分享的slide可以在我的slideshare页面看到。

前面的文章提到了,吴哥景区是一个非常庞大的区域,面积约为386平方英里的主要区域上,坐落着世界上最大的宗教建筑群,以及近千年以前曾经的辉煌都市及灌溉奇观。吴哥景点的旅游线路分为小圈和大圈,一般各占用一天时间。小圈和大圈的旅程就基本囊括了吴哥核心区域的主要景点,是吴哥游览的核心部分。

早上9点,冒着滂沱的大雨,为了不耽误接下来的旅程和计划,开始了吴哥小圈之旅。天色灰暗阴沉,从暹粒通向吴哥的主要道路已经满是积水,路边到处都是熄火的摩托车。心里还为驱动TukTuk的小摩托捏把汗,索性还好。于是就这么颠簸着来到了吴哥窟面前。

和昨天傍晚热闹的情景不同,今天的景象颇为平和,人也少了许多,估计是大雨打消了不少游客前来的念头。穿过满是积水的门廊后,终于瞥见了这座向往了许久的宏大庙宇。

angkorwat_siemreap.JPG在外围仰望了许久才进入中心建筑的内部。首先感到震撼的就是中心寺庙外围800米长的浅浮雕,虽然大多数游客可能并没有时间和经历完全消化掉这些精美浮雕所呈现的史诗般的历史故事,但是这伟大的艺术佳作和对于细节的追求,确实令人赞叹。

继续进入,就发现那些细节之美,体现在这里建筑的各个部分之上,那些800年前的雕刻,就完整地呈现在你视线所及的方方面面,墙上、廊柱上、屋檐上...... 那些让你惊异的细节无处不在。
angkorwat_collection2_siemreap.jpg走近吴哥窟的核心----五座中心塔,庞大而华丽的圣殿出现在眼前,而那陡峭的台阶让人望而怯步。不过现实中,也不得不"怯步",因为中心塔部分修复工作中,不允许游客登塔参观。颇为遗憾,但在它脚下的确更能感受其宏大和壮观。
angkorwat_collection1_siemreap.jpg冒着雨继续游走,继续品味整个建筑群的对称美以及象征意义。但这雨天太糟糕,光线太亮,极不适合拍照。总共用了大约三个小时。

从吴哥窟出来时,卖东西的小女孩,十几岁的样子,与人打交道经历的纯熟使人印象深刻。在你进入景区之前和你打个招呼,就认定你是和她谈好买东西----却没有什么让我喜欢的东西。这时过来另一个小女孩来兜售,看我觉得另一个女孩的东西还不错,于是这个小女孩立马改换策略:开始哭,让你觉得内心十分愧疚,给她钱买东西也不肯了。最后她提议:"不买也没关系,只要别从她俩任何一人手中买东西就好了。"于是故事就以她成功搅黄了另一个女孩的生意,以及让我有些内疚而告终。现在想来,期间这个小女孩采用的种种策略可真是有一套。后来应对景区兜售东西而又不想买的情况就有了经验,直接告诉他们你不需要就好了,不要不好意思,那么死缠烂打的情况也就很少发生。

接着驱车前往巴戎寺,吴哥王城的中心。路上看到了供游客骑乘的大象,不过没见到前去尝试的游客。

路过华丽壮观的吴哥南门后,就到达了巴戎寺。巴戎寺最负盛名就是其的数百个"高棉的微笑"雕塑了。在启程出发去柬埔寨之前,这雕塑的照片在我的iPod上面停留了数月之久,作为激励我去柬埔寨的标志。这次终于亲眼看到了。沿着陡峭的台阶爬上二层,立刻被众多面孔围绕的神秘气息包围了。近距离欣赏那些微笑的面孔,尝试找到我的iPod壁纸里的那个。但是最后还是没能找到一样的。
bayon_siemreap.jpg这时雨开始变大,很不舒服。照片中的天空亮得刺眼。想想自己几近湿透地在雨中揣着单反,按着iPod Touch的样子,这也就是八九个世纪以来,朝觐者的变化吧。
冒着雨沿着巴戎寺走了一圈,细细品味了这个吴哥王城的著名建筑。

临近中午,游客在逐渐减少。中午在巴戎寺对面吃了顿饭,买了瓶水,继续前进。

需要注意的是巴戎寺附近还有很多景点,如吴哥皇宫、巴芳寺等,步行距离也就百余米,不过由于时间关系,这些景点要留到明天进行大圈路线时再做浏览了。

穿过了吴哥王城的东门(胜利门),拍下了一张满意的照片。
victorygate_angkor_siemreap.jpg然后路过了周萨神庙以及其对面的Thommanon。中国修复队主要在对周萨神庙进行修复工作。但是我对中国团队主持的修复工作不是很满意,从细节上看,诸如用水泥添补雕塑等做法很有些偷工减料的样子。先前在网络上的照片就略有耳闻,但现场看到还是让我觉得有些吃惊和惭愧,加上雨下得特别大,也就没有怎么深入地参观里面。

有朋友说,中国修复队的工作,也有很多方面的原因,语言不通,而且中国在这样的古建修复方面的经验非常之少,同时周萨神庙本身的损坏情况也非常严重的。我觉得这些因素都可以理解和接受,我只是不希望看到中国特有的不负责任的表现在这样一个环境中被表现出来,显得非常地不专业。因为与之相对应的是,第二天我再访巴戎寺,看到了资料介绍的日本一个团队修复其中一个图书馆的过程,其工程量之大、以及整个工程所需要的细致和用心,让我有着很深的感触。这部分放到游记的下一部分再说。

下一站茶胶寺。昨天晚饭时就听那两个北京女孩说,其台阶非常陡峭,天气不错的话,爬上去以后风景非常好。但在这阴沉的天色下,风景是看不到了。可台阶还是一样地陡峭,感觉在湿漉漉的雨中,穿着拖鞋往上爬只是让我觉得更加危险。茶胶寺的台阶大约有一个手掌宽,而每一级台阶的高度大约是半米,也就是说,向上爬的时候,脚要横过来才能保证脚踩实地爬上去。总体说来,整个茶胶寺就是一个坡度大约为70度,高度几十米的金字塔式建筑。爬上去还算好,爬下来就更是恐怖,由于坡度的关系,从上面看下去,台阶就像是笔直的一样,脚上穿着拖鞋,背着大书包,一个打滑简直就不可想象后果了(见下图)。所以为了安全第一,走一步就在台阶上一坐,然后再迈下一步,这样就稳妥多了。爬上去以后更多的是成就感,而非景色。天气太差,四周的景色基本已经完全看不清了。但这让人精神紧绷的体验在远观茶胶寺时完全体会不到,远观时,它只是一座宏伟、壮观的美丽建筑,这截然不同的体验真是让人印象深刻。隔了一天后我重访茶胶时,它的样子简直让人沉醉,在后面的游记中还将有它最美的全景照片。
takeo_siemreap.jpg结束了惊心动魄的茶胶寺之旅,启程前往吴哥区域最期待的寺庙之一,塔布隆(Ta Phnom)寺。

塔布隆寺有其被自然反噬的著名景象而闻名,宏大而破败的寺院,巨树在其间交错盘绕,人们已经无法分辨它们的依赖关系,似乎自然在这里宣告着其默默征服一切的力量。2001年的电影《古墓丽影》(Tomb Raider)在此取景后,塔布隆寺更负盛名。

我也就在这个大雨天开始了探索。踏着已经完全是泥的小路进入,首先看到的就是不同于别的庙宇的巨大的树,它们肆意地跨过围墙,对人类的所谓奇迹不以为然。很多走廊已经无法通行,绿色的苔藓以及各种植物覆盖着的雕塑,到处可见。寺庙里到处都是交错的大树根,寺院的建筑倒塌很严重,这可能和历史上塔布隆较晚才被发现和重新维护有关。让人感觉自己很渺小。

著名的"古墓丽影"之树(下图右上)更是期待已久的著名景色。
taphnom_collection_siemreap.jpg我记住了当时路过的一位美国游客的话,"In there, it seems that the nature is taking over.", 的确,在这里,自然控制一切,当自己脚陷泥土,冒着大雨在这座令人畏惧的大寺庙参观时,我想我对所有的这些寺庙来说,都只是一个谦卑的祈祷者而已。
entrance_taphnom_siemreap.jpg从塔布隆出来,道路上的积水越来越严重了。赶往路线上的下一座庙宇Banteay Kdei,但其入口前的水早已形成了一条几近膝盖、水流湍急的小河。若想进入景点,非得趟几十米的水才能进去...... 自己只好小心翼翼地趟进去。

刚进去看到两个中国女孩正在往外走,她们正在请工作人员帮忙,用摩托车带她们出来。艰难地步行进入之后,发现这个若大的寺庙除了我自己,没有其它的游客。

Banteay Kdei 主路的大石板在下雨的时候非常滑,大家务必小心,我有好几次都险些滑倒。
banteaykdei_siemreap.jpg大约看了一半,雨下得更大了,蚊虫还很多,而且走了半天,还是没见到一个别的游客,天又迅速地黑下来,于是就折返了。

这天的最后一个景点是豆蔻寺,这是一个小型的寺,砖石的颜色非常漂亮,亮点在于寺中央内部的雕像。

在依然滂沱的大雨中,吴哥小圈的游览结束了,折返回去休息,这时已经接近下午6点。回去稍作休息,然后就前往酒吧街附近的路边摊吃饭。

之先去了趟老市场,想买双合适的拖鞋(像柬埔寨这种国家,人字拖相对普通的运动鞋要适用得多,几乎是标准配置)。自己带来的家用拖鞋走完吴哥窟的路线,鞋底就掉了。偶遇了这两天在景点都一直碰到的一队北京人,买鞋时,她们推荐买一双合适、舒服的,最后$3买了一双,感觉还算舒服(可没想到后来就是因为这双鞋,受了莫大的苦......)。事实证明,买人字拖鞋至少要买大上自己的脚不止一号甚至几号的(之前没怎么穿过这样的鞋,也没有穿着它们爬上爬下到处走的经验)。

随后请我的小司机一起去夜市吃饭。晚饭时聊了一些关于柬埔寨通常情况,然后早早回去,看电视到午夜,睡觉。

从傍晚到晚上,雨还一直淅淅沥沥地下着,但看样子比早上人们说的要连下五天的情况要好。希望明天的天气可以好一些。

结束了这匆忙而辛苦,略有艰难的一天。

需要注明的是,今天的条件实在是艰苦些。不过也希望打算去旅行的大家不要被这天气吓倒,这天的平均降水量超过了柬埔寨平时雨季的降水量,全是拜09年十一期间东南亚地震后所产生的台风所赐。应该说,即使是在平常的大雨天,情况也应该比这篇游记描述的要好一些。好在坏天气也没有持续太久,很快吴哥就要展现它真正的美丽了。若要我现在回忆在雨里攀爬、前行的感觉,我还是会觉得无比赞叹,难得的困苦就像是一种标志,更为深刻的体会把自己和寻常游览拍照的游客区别开来。而这一切又像是这些古老庙宇的小小试炼,回想起来实在是别有一番感觉。(读过凯鲁亚克的《达摩流浪者》的朋友,更能体会这种感觉)

第二天起点起床,一眼就看到金黄色的阳光、湛蓝的天空,外面也没有了昨天滂沱的大雨声。心里兴奋极了。但是马上一个新的问题就要出现......

未完待续

Movable Type 4.3 分页改进

| No Comments
Movable Type 4.3 版本开始,官方提供了一种分页方法。解决了MT静态化爱好者们实现像各种动态Blog程序,如Wordpress那样,可以一页一页地翻到最后的心愿。

从Movable Type 4.3开始加入的分页方法,其原理是将网站首页进行静态化生成,此后翻页的第2页开始,内容使用MT-Search脚本动态生成。此功能可以在文章索引以及按日期索引上使用。

对于原教旨主义静态化控的朋友们来说,此方案难免差强人意;但是作为普通的用户,这个方案已经是一个近似于完美的方案了。(纯静态化的分页方案,网上也有相关的插件等解决方案。不过这些方案的性能热点在于发布生成这些页面的时间会非常长。相比之下此种方法通过动态访问的性能还是可以接受的,毕竟系统其它页面都是静态页面,资源的耗费在通常情况下可以接受。)据我观察一些大型网站如apple4us所使用的,猜测也是类似的解决方案(如果有误请指正)

官方的Pagination解决方案,请点击:http://www.movabletype.org/documentation/designer/pagination-static.html#pagination-in-movable-type-43

但是在我架设Blog的过程中,发现官方的分页解决方案有如下两个问题:

  • 上一页/下一页的链接,在首页是/pages/n.html这种格式,但是在从第二页开始的动态生成页面中,程序就对此链接直接生成了mt-search.cgi?xxxx=xxxx这样的链接,而不是期待中的pages/2.html。不美观同时也不利于SEO
  • 同样在从第二页开始的动态生成页面中,页码显示也不正确,只显示出了当前页的页码,而没有所有页面的页码,无法快速翻页。

于是自己针对原始版本,进行了一些小修改,解决了如上问题。

首先需要确认已经应用官方的分页方式,根据官方的文档,对模板中的 Main Index 模板做出相应修改。

然后确保已经加入Rewrite Rule。我使用的Rewrite Rule如下:(.htaccess放置于网站根目录)
RewriteEngine on

#Pagination
RewriteRule page/([0-9]+).html/?$ /cgi-bin/mt4/mt-search.cgi?IncludeBlogs=1&template_id=46&limit=3&archive_type=Index&page=$1 [L,QSA]
Pagination分页的url设置为 /page/1.html 这种格式。需要注意的是,用户需要把/cgi-bin/mt4/这个路径,需要修改为和自己blog一致的cgi路径。同时template_id也要和自己的网站设置一致。

然后我们就动手针对遇到的问题进行修改。要解决这些问题,只要修改Main Index这个模板就可以了。

MT的模板文件中,使用很多MT标签来执行一些简单的程序逻辑,首先让我们看看该问题的根源。

从这里可以发现"上一页"链接的指向。
<mt:IfPreviousResults>
<a href="<$mt:PreviousLink$>" rel="prev" onclick="return swapContent(-1);">&lt; Previous</a>&nbsp;
</mt:IfPreviousResults>
可以发现上一页/下一页的链接是由<mt:PreviousLink>以及<mt:NextLink>两个模板标签来控制的。经过测试发现这两个标签会直接输出诸如mt-search.cgi?xxxx=xxx这样的链接。

那么就要把这两个标志用指向页面的链接替换,以达到我们使用直观页面URL的效果。

通过分析下方控制第一页链接的部分,我们可以看到,链接是通过一个记录当前页链接的变量,再组合url来生成的。
<a href="<$mt:Var name="search_link"gt;<$mt:Var name="pbpage"gt;.html"><$mt:Var name="__value__"gt;</a>
这样具体解决这个问题的思路就清晰了:首先找到当前页面的页码,然后生成一个url显示出来,就可以达到我们想要的美观的分页url。

所以我们就需要在<mt:PagerBlock>里面,设置一个记录当前页码的变量。(<mt:PagerBlock是循环体,相当于程序设计语言里的for,遍历程序的所有页面)

我们只需要判断一个循环中的页面是否是当前页,然后设置一个变量即可。但是需要注意的是,我经过很多次实验,发现<mt:IfCurrentPge>这个判断条件判断的,恰恰不是CurrentPage, <mt:Else>分支下,反而是当前页面的判断,这十分奇怪。(这情况也导致出现这许多问题)

最终修改好的PreviousLink部分代码为:(该部分设置了一个变量currpage,记录当前页面号码)
 <mt:Ignore><!-- Navigation for dynamic pages (same as navigation found in the Search Results system template). --></mt:Ignore>
        <mt:IfPreviousResults>
           <mt:PagerBlock>
            <mt:IfCurrentPage>
            <mt:Else>
                <$mt:Var name="currpage" value="$__value__"$>
            </mt:IfCurrentPage>
           </mt:PagerBlock>
           <$mt:SetVar name="currpage" op="--"$>
           <a href="<$mt:Var name="search_link"$><$mt:Var name="currpage"$>.html" rel="prev" onclick="return swapContent(-1);">&lt; 上一页</a>&nbsp;
           <$mt:SetVar name="currpage" op="++"$>
        <mt:Else>
           <$mt:Var name="currpage" value=1$>
        </mt:IfPreviousResults>
随后处理并修改页码显示的部分,修改后可以正确显示当前页面以及所有页面。
      <mt:PagerBlock>
            <mt:SetVarBlock name="pbpage"><$mt:Var name="__value__"$></mt:SetVarBlock>
                <mt:If name="pbpage" ne="$currpage"><a href="<$mt:Var name="search_link"$><$mt:Var name="pbpage"$>.html"><$mt:Var name="__value__"$></a></mt:If>
            <mt:IfCurrentPage><mt:Else>
                <$mt:Var name="__value__"$><$mt:Var name="currpage" value="$__value__"$>
            </mt:IfCurrentPage>
        </mt:PagerBlock>
最后是处理下一页的链接部分:
      <mt:IfMoreResults>
        <$mt:SetVar name="currpage" op="++"$>
        &nbsp;<a href="<$mt:Var name="search_link"$><$mt:Var name="currpage"$>.html"  rel="next" onclick="return swapContent();">下一页 &gt;</a>
        </mt:IfMoreResults>
进行完这些修改后,从第二页开始的动态生成的分页页面中的链接,以及所有的页码链接,都可以显示正确了。

倘若没有耐心读完前面逐步修改与分析的结果,也可把如下完整的修改后的分页模板代码粘贴到Main Index模板中,覆盖 从 <mt:Ignore> Create pagination 开始,直到 <a href="<$mt:Link template="archive_index"$>">Archive Index</a> 这部分代码即可。

修改后的分页效果可查看我的Blog第二页的显示效果
<mt:Ignore><!-- Create pagination navigation. Condition based upon if page is statically or dynamically rendered using the search_results variable. --></mt:Ignore>
<mt:Ignore><!-- pagination url mod by CNBorn, cnborn.net --></mt:Ignore>
<mt:SetVarBlock name="pagination_navigation">
    <mt:If name="search_results">
        <mt:Ignore><!-- Navigation for dynamic pages (same as navigation found in the Search Results system template). --></mt:Ignore>
        <mt:IfPreviousResults>
           <mt:PagerBlock>
            <mt:IfCurrentPage>
            <mt:Else>
                <$mt:Var name="currpage" value="$__value__"$>
            </mt:IfCurrentPage>
           </mt:PagerBlock>
           <$mt:SetVar name="currpage" op="--"$>
           <a href="<$mt:Var name="search_link"$><$mt:Var name="currpage"$>.html" rel="prev" onclick="return swapContent(-1);">&lt; 上一页</a>&nbsp;
           <$mt:SetVar name="currpage" op="++"$>
        <mt:Else>
           <$mt:Var name="currpage" value=1$>
        </mt:IfPreviousResults>
        <mt:PagerBlock>
            <mt:SetVarBlock name="pbpage"><$mt:Var name="__value__"$></mt:SetVarBlock>
                <mt:If name="pbpage" ne="$currpage"><a href="<$mt:Var name="search_link"$><$mt:Var name="pbpage"$>.html"><$mt:Var name="__value__"$></a></mt:If>
            <mt:IfCurrentPage><mt:Else>
                <$mt:Var name="__value__"$><$mt:Var name="currpage" value="$__value__"$>
            </mt:IfCurrentPage>
        </mt:PagerBlock>
        <mt:IfMoreResults>
        <$mt:SetVar name="currpage" op="++"$>
        &nbsp;<a href="<$mt:Var name="search_link"$><$mt:Var name="currpage"$>.html"  rel="next" onclick="return swapContent();">下一页 &gt;</a>
        </mt:IfMoreResults>
    <mt:Else>
        <mt:Ignore><!-- Navigation for statically published page. --></mt:Ignore>
        <mt:If name="archive_template">
            <$mt:ArchiveCount setvar="total_entries"$>
        <mt:Else>
            <$mt:BlogEntryCount setvar="total_entries"$>
        </mt:If>
        <mt:Ignore><!-- If blog contains more entries than the number of entries to display per page. --></mt:Ignore>
        <mt:If name="total_entries" gt="$entries_per_page">
            <mt:Ignore><!-- Set the total number of entries to iterate through the pages. --></mt:Ignore>
            <mt:Ignore><!-- IF total entries divided by entries per page is a whole number. --></mt:Ignore>
            <mt:If name="total_entries" op="%" value="$entries_per_page" eq="0">
                <mt:Ignore><!-- Set total pages to total entries divided by entries per page. --></mt:Ignore>
                <$mt:Var name="total_entries" op="/" value="$entries_per_page" setvar="total_pages"$>
            <mt:Else>
                <mt:Ignore><!-- Get the remainder when dividing total entries by entries per page. --></mt:Ignore>
                <$mt:Var name="total_entries" op="%" value="$entries_per_page" setvar="remainder"$>
                <mt:Ignore><!-- Subtract remainder from total entries. --></mt:Ignore>
                <$mt:Var name="total_entries" op="-" value="$remainder" setvar="total_entries"$>
                <mt:Ignore><!-- Determine total pages by dividing total entries (minus remainder) by entries per page. --></mt:Ignore>
                <$mt:Var name="total_entries" op="/" value="$entries_per_page" setvar="total_pages"$>
                <mt:Ignore><!-- Add one page to handle the remainder of entries. --></mt:Ignore>
                <$mt:SetVar name="total_pages" op="++"$>
            </mt:If>
            <mt:Ignore><!-- Loop through total pages, creating links to all but the first page (which is the current page). --></mt:Ignore>
            <mt:For from="1" to="$total_pages" step="1">
            <mt:If name="__first__">
                <$mt:Var name="__index__"$>
            <mt:Else>
                <a href="<$mt:Var name="search_link"$><$mt:Var name="__index__"$>.html"><$mt:Var name="__index__"$></a>
            </mt:If>
            </mt:For>
            <mt:Ignore><!-- Hard-coded link to the next page (page 2). --></mt:Ignore>
            &nbsp;<a href="<$mt:Var name="search_link"$>2.html" rel="next">下一页 &raquo;</a>
        </mt:If>
    </mt:If>
</mt:SetVarBlock>
<mt:Ignore><!-- Strip space and trim navigation code. --></mt:Ignore>
<$mt:Var name="pagination_navigation" strip=" " trim="1" setvar="pagination_navigation"$>

<div class="content-nav">
<mt:Ignore><!-- Output variable if exists. --></mt:Ignore>
<$mt:Var name="pagination_navigation" strip=" " trim="1" setvar="pagination_navigation"$>
<mt:If name="pagination_navigation">
    <div class="pagination-navigation">
        <$mt:Var name="pagination_navigation"$>
    </div>
</mt:If>
    <a href="<$mt:Link template="archive_index"$>">历史归档</a>
</div>

柬埔寨背包记二:吴哥的大门,暹粒

| No Comments
我于09年9月27日-10月8日在柬埔寨独自背包旅行,其间的经历和收获数不胜数。相关的经历我曾在10月底在 Beijing Open Party 上有过一次交流分享,现在把旅行途中以及后来的一些记录陆续发布到这里,请感兴趣的朋友关注这里的更新。要查看本系列更多文章,欢迎点击页面右侧的Cambodia标签。除正文中的照片以外,还可以在我的Footbig以及我的豆瓣相册中欣赏因篇幅原因未能在文章中出现的照片上一次交流分享的slide可以在我的slideshare页面看到。

早上6:00,Mekong Express公司的小车就到达住处,接我前往车站,7:30分巴士准时启程前往暹粒。虽说这家公司的车票比其它公司贵一些,但服务确实好得没话说。乘务小姐很漂亮,一路上途径的地方还有英语讲解。不过高棉英语版的讲解实在是听不懂......

金边到暹粒,沿NH5(柬埔寨的国道之一,但柬埔寨双车道的国道看起来实在是寒酸一些,时速大约可达到60公里)向西北, 路程共约5小时。途中除了欣赏乡村风情以外,最多的就是绿色田野配上零星的椰子树的热带美景,非常漂亮。中途停经小城磅同休息半小时,停车附近的摊位都在卖水果和小吃,游客们都在围观传说中的蜘蛛以及其它油炸昆虫,当然没见有人有胆量尝试、大都是远远拍照。自己在附近逛了逛,看了看柬埔寨特色的盗版盘摊位。在水果摊买了一种有壳的酸果子吃,至今不知是什么名字。剖开硬皮之后,里面有两粒果子,很软、有核、很酸,我很喜欢。

然后上车继续前往暹粒,又经过40分钟的车程,于12:45到达暹粒。暹粒的车站始终没有给我"这是一个城市"的感觉,我还以为是在某个小站加油呢。没有见到任何高层建筑,个别路段路况很差,尤其是看到我所住的旅馆街的土路,坑坑洼洼,不像自己印象中的任何一个城市。当然后来发现用通常对于城市的观点来映射暹粒完全是错误的,就居住和生活的感觉上来说,这是一个漂亮且舒适的城市。从地位上来说,暹粒又是吴哥旅游业的维持系统,作为开启游客吴哥之旅的地方,游客在暹粒居住,白天的时间出发游览吴哥,晚上就回到城里休息享受。从暹粒到吴哥景区入口的摩托车路程大约需要20分钟。从面积上来说,吴哥景区是暹粒城市的几十倍。(个人估算,还可能更多,因为吴哥景区实在太大了)

住在之前在网上预订的 Bun Kao Guesthouse,位于河东岸北部一个GH聚集的街道里面。接我的司机叫库依,一个16岁的小男孩,是旅店老板的侄子。看小伙子还是挺实在的,剩下几天就都坐他的车了。不过一开始他接我时开的是摩托,但这摩托我在金边体验过了,路远时会非常不舒服,而且也不方便拍照。就问他有没有TukTuk,他说有,于是从第二天开始,就坐上了TukTuk。不过后来才知道他的TukTuk是租来的。

baokunguesthouse_siemreap.JPG
当天安顿好以后,3点半出发前往吴哥售票处。没想到下午4:30才开始卖下一天开始的门票(买明天开始的票可以在当天下午买票后进入景区参观),于是两个人只好边聊天边等。聊了下接下来几天景点的计划,预计按照自己之前研究的旅行计划,第一天小圈,第二天大圈,而后待定。(后来实际的行程是第三天女王宫,高布斯滨,第四天 崩密列,罗洛寺,当时没有确定是因为根据自己游览的安排,因为后面的路途需要根据游览情况再考虑)

随后排队买票进入景区。购买了$40,可以在三天之内任意进入景区的门票。

这天傍晚的时间,现在想来没有充分利用好。首先前往向往已久的Angkor Wat,刚看到护城河和围墙,自己就已经无比激动,拿起相机就拍,其实当时连吴哥窟的五塔还都没看清呢。也没有进去,当时觉得自己还要在景区看3天,以后肯定会在这里花不少时间(而后的事实证明,3天的行程对于向我这样想要尽可能多得游览吴哥的人来说,无比紧张和急促)。没有什么内容的拍照费时许久,随后前往巴肯山看日落。可惜到那里时已经过了五点,不允许上山了。天色也黑下来,于是只好返城。

angkorwat_firstsight_siemreap.JPG
回来和飞机上遇到的两个北京女孩约好,前往酒吧街----暹粒市中心一条著名的商业街一同去吃高棉烤肉。之前先在酒吧街附近转了转。整条街是暹粒文化和商业氛围最浓厚的地方了,布满了高档餐馆。像Angelina Jolie曾光顾的Red Piano等。同时还有卖各种纪念商品的老市场及各种小店。在一个摊位上买了几条围巾(也称水布,后来发现对于爱出汗的我来说非常有用。$1一条);逛附近一些漂亮的店(旧书店,小工艺品店),不过性价比并不十分高。比较有趣的是小鱼理疗。当街一个养着小鱼的小水池,人们围座其间,脚泡在水里按摩,小鱼会自动帮忙吃掉死皮,我没有尝试。

barstreet_siemreap.JPG
然后在烤肉馆吃了一顿在柬埔寨吃的最贵的饭($10.5),柬式烤肉更类似于我们这边的火锅,肉类比较新颖:除了通常的牛羊猪鸡外,还有鳄鱼肉和蛇肉。同时还单点了一盘山羊肉。鳄鱼肉比较硬,山羊肉比鳄鱼肉更硬。总体来说味道不错,偏清淡。关于鳄鱼,在我住的Guesthouse房间后面,就是两个鳄鱼养殖池,这是当地的一个大产业。

khmerbarbecue_siemreap.JPG
这顿饭过后,困难开始了。天空忽然下起雨来,本以为在这个国家的阵雨季节,每场雨都像在金边碰到的第一场雨一样,下大约20分钟然后停止,于是就坐在餐馆里等(同行的两位女孩去马杀鸡了)。哪知这雨始终在以北京的最大雨量×2这样的强度下着,丝毫没有减弱的迹象。没有办法,只好打电话叫我的司机来接我。(街上的水太大了,下了一个小时已经开始形成了一条河。我的司机小朋友,特别叮嘱希望我别做其它司机的车 ,担心知道我住哪里后会把生意抢走,我告诉他让他放心;不过除了这点以外,让他来接我实际上还能省下一笔车费)于是给他打电话,没想到电话打不通(后来才发现黑莓上默认国家区号的原因,用电话本拨号是打不通的,必须自己手动拨号才可以)。没办法,只好给旅馆老板打电话,旅馆老板非常好,说马上就过来接我。不过新的问题是我还没有见过他(中午入住时他人不在)于是自己就只好等啊等啊,其间还认错了人上错了一个TukTuk。最后终于见到了老板Bao Kun。一个非常和蔼的大叔,开着个小摩托来接我了,还给我带了件雨衣。

回去的路可怕极了,本来路程并不远,但曾经的街道在晚间消失了,完全淹没在一片汪洋之中。小摩托能行驶在里面,已是足够幸运了。摩托后座没有扶手,道路非常颠簸,同时雨非常大,自己完全看不到东西。当时根本就不敢想象万一摩托车失控窜出去会怎么样。但最要命的问题,还不是这些,而是呼吸。我没有头盔,巨大的雨水在摩托车行进的过程中打在脸上,和被灌水的感觉是一样的,只有用力才能勉强吸口气。那时真是觉得苦不堪言,那段又黑暗又窒息又危险的路看起来就好像没有尽头一样。

不过真是要感谢老板冒着这样的大雨来接我(分文不取!),不但没有怨言,待我更是极好,十分感激,这一路想必他也并不比我坐在后座舒服多少。这段路上最明智的决定是把裤中兜的护照、吴哥门票和钱都转移到了上面的口袋里。因为到达Guesthouse的时候,除了被雨衣罩着部分和上口袋,其它地方基本完全湿透,鞋也完全成了落汤鸡(两天半后才完全干透)。

接下来的这个晚上,雨始终在以同样的频率下着,丝毫没有减弱。本来约定早晨4:30起床看日出,结果按时起床一看,仍然是哗哗的大雨,只好回去睡下。(后来才知道这就是东南亚地震后产生的,过境越、柬、泰三国的热带风暴。这次的雨水之大,若干年也难得一见,后面的游记中会再有描述)

早晨起床,吃早餐时和其它游客聊了下,有人说雨可能要以这种情况下若干天。心里沮丧至极。八点多时,看雨小了不少,为了不耽误更多的计划,咬咬牙决定出发,开始吴哥小圈的行程。

也就开始了这天同样经历重重困难的旅程。

未完待续

要查看本系列更多文章,欢迎点击页面右侧的Cambodia标签。本系列文章可能会省略一些旅行中的细节内容,如果您也打算出游,并还想了解更多详细信息的话,欢迎在网站页面留言,我会很高兴帮助您。