谈到移动设计,并不是所有的人都有足够的资源和时间去重头开发一个全新的移动网站或是重新设计成一个移动优先的网站。Mobilizing Web Sites一书的作者Kristofer Layon将在本文中介绍,如何一步一步地将一个已有的网站改造成一个移动网站。
在过去的几年中,我有幸参与了一些关于移动web设计的重要会议,如果你对移动市场感兴趣并且正在阅读这篇文章,你可能已经听过一些给人带来灵感的文章和一些技术介绍了,应该对如何在移动设备上设计新的网站有所了解了。
对于我们,最大的问题就是,在谈到移动网站的设计时,我们常常是重头来过,并希望能够设计出一个移动优先的网站…而我们还需要继续开发我们的桌面项目。或许我们中有些人是帮助维护和更新现有网站的设计人员。又或者,有些人是签约为客户维护和更新已有的网站。总之,我们的客户都是一些对移动感兴趣的人——但是需要考虑的是,他们是否给了我们足够的时间、资金或是其他我们需要的资源?
通常情况下,重头开发一个符合现在移动目标的全新的网站是一件很奢侈的事情。虽然Luke Wroblewski大肆宣传移动优先的理念,并且确实影响了很多人。但是对于那些已经被设计成移动靠后的网站又该如何是好?更糟糕的是,有时候我们可能会觉得我们的环境根本就不适合转变成移动网站,又该怎么办?
Boston Globes是一个移动优先设计得很棒的网站。但是它聘请了大量的设计顾问并且花费了几个月的时间去设计
开始时逐步提升请放心,并不是只有你感到无法重头开始。我认为Dan Cederholm在今年八月份已经深刻分析过这个问题了。
“对于一个已经存在的网站,让开发团队放下手头的工作重新开发一个网站是一件很奢侈的事情。我们现在在Dribbble上就碰到了这样的问题,我认为目前最好的办法就是找到一个折中的方法。我们将Dribbble原来在大屏幕上运行的代码和内容保留了下来,然后再对它进行改进…当时间、资源和资金更加充裕了以后,我们再从整体上重新构想一些东西,但就目前而言,逐步改进是最可行的方法。这是我们优先需要考虑的事情。”
事实上,关于web的研究显示,虽然现在有越来越多的网站正试图对移动平台做优化,大多数的网站并没有一个移动版本。dotMobi的研究显示,虽然前1,000名的网站中有40%拥有移动版本,但在前10,000名的网站中,这一比例下降到了30%,前500,000的网站中则只有19%了。
这意味着大多数人并没有做移动设计。这并不是一个小问题。研究表明,将近一半的移动用户在发现一个网站的移动版本做得不够令人满意以后,将不会再去访问这个网站,57%的用户表示不会向其他的用户推荐这个网站。所以,你准备如何逐步改进你的网站,让它变得更加移动友好?这其中应该包括调整布局的大小适应屏幕的尺寸,利用触屏功能重新设计导航系统,重新设计文本和图片的尺寸。这都可以在现有的设计中加以改善。这就好比是重塑一个房屋而不是完全新建一个房子。
如果你需要在渐进改进和等待时机重新设计之间选择一个方案,我建议你选择前者。虽然这种方案每一次的改进可能并不明显。但至少这种方法将使得更多的网站能够变成移动友好型,帮助用户拥有更好的体验,提升网站的业务。
另外,即使是很小的改进也能像重头设计一样具有创造性和重要性。
Dribbble并没有按照移动优先的原则重新设计,但是它同样具有很好的移动导航和浏览体验。
翻修vs新建谈到设计一个拥有良好移动体验的应用,就想到重头开始设计是一件很自然的想法。事实上,一提到设计,我们首先想到的就是设计一个全新的东西。因为能够创造一个全新的东西是促使和吸引我们去设计的原动力。图像、文本和代码:将一个空白的浏览器窗口用自己的设计让它变得丰富起来确实能给人带来创作的激情和满足感。
所以一想到要在一个已有网站的基础上进行改进和修补,的确有点令人觉得乏味。但它是否也能变得有创造性呢?其中的创造性又在哪里呢?
有时我们常常忘记了,其实web工作者和媒体、材料或是机械系统工作人员没有本质上的区别。人们的创造性体现在重头到尾设计一个全新的东西并在空白的画布上填充上各种新的想法——但这种情形往往是些个例,而不是常态。就拿建筑为例吧。
那些从小梦想着设计一些建筑的人们最后大多去了建筑学院,当他们完成了自己的课程,便成为了建筑师。类似的,那些喜欢手动实践的人可能会进入职业学校成为一名工匠、泥瓦匠或是技工。无论他们的角色是什么,那些希望能够参与建筑设计和构建领域的人就和我们想要出人头地的想法一样。他们想要建造一些让他们引以为傲的作品。通常梦想能够打造一个全新的建筑。
但是与全新的建筑相比,更多的还是翻新和改进工程。这才是大多数技工的主要工作。
所以,我们应该面对现实:常常是那些旧的作品变成了全新的东西,不是吗?在某种程度上讲,一个翻新或是重用的项目并不是一个旧的工程。它是一个使用了一些不同的组件,加上以前已有组件的新项目。
再看看web设计和开发领域。我们之所以会选择我们现在的工作就是因为我们认为我们能创造出一些新的东西。但是正如我之前所说的,现实往往不能尽如人意。
那该怎么做呢?
正如之前所举的建筑的例子,我们发现一个新的网站其实并不一定需要重新设计。特别是,移动web体验并不需要设计一个新的网站。移动靠后的理念有时可能行得通;我们不需要等到一个合适的时机再去开发一个移动优先的网站。其实,取代重新设计一个全新网站的方法可以从考虑先逐步改进现有的网站开始,在现有的网站设计中加入移动技术。
打破固定的宽度和网格限制到底该怎么做,又该从哪里入手?当你想将一个已有的网站改造成移动化的网站时,可能深深感到被固定的宽度和基于网格的设计限制了。
但如果你考虑移动优化——我更喜欢将它称之为mobilising ——如果一个网站使用CSS处理布局、位置和风格设计,使用HTML封装内容,那么它就已经为适应移动设备和环境做好准备了。
可能令人惊讶的是,即使你使用了固定宽度的CSS布局也能够做到这一点。对,固定宽度的CSS并不意味着它无法更改。只是需要修改几个条件设置。
即使为桌面浏览器设计的网站采用了固定的网格布局,那也不意味着它们需要重新设计以实现移动性
固定宽度的CSS框架做了两件事:
如果你要将一个固定宽度的网站改造成一个移动版本,你只需要指定新的单元,并且重新测量你的网站中已有HTML元素的大小,让他们在移动设备上能够更合适地呈现出来。这个策略可以逐步实施到你设计的各个方面:布局,导航,排版,图像和表格。
当你逐步开始改造你的网站时,你会发现其实web的内容具有很好的可塑性和响应能力。
Finland中的移动设计版本
在增量的移动改造中学习我认为当你熟悉了移动限制,技术和机遇并将它们应用到现有的网站之中后,你将学到更多的东西并且能够做更多的工作。你可以让用户评估你的工作,从移动展示和移动环境的角度更加全面地重新考虑你的内容,从而持续地提升你的应用的移动性。这样,一个更加全面的移动优先重新设计看起来也并没有那么困难了。这看起来应该就是一个正确的方法。
更重要的是,它看起来现在就是一个合适的时机可以开始改造了。
如果你先从用户体验,布局,导航开始,你将会学习到很多移动环境的东西。学习一些交互做得很好的移动网站将让你从一个全新的语境审视你的内容。可以将你改进的初步成果展示给你的同事或是其他访问者,并及时获取反馈。
从另一个角度看待这个问题,它并不是将所有的东西都打包起来,然后将这些内容全部放到一个行李箱中,你需要检查自己的行李。但是你无法真正的弄清楚你需要哪些东西,如何根据你拥有的容量决定你需要的内容范围,除非你知道你拥有的空间大小并且测试了如何让你的内容满足空间的限制。
同样的,如果你是对一个已有网站实施增量的改进,那你可以清楚地知道如何让web内容适应一个移动环境。所有你能想到的事情都能成为一些额外但是重要的工作:你的内容清单,对品牌呈现的审视,重新评估网站的目标。事实上,这个打包行李的比喻是相当贴切的。对最初移动设计的重新思考将让你增加对移动限制的认识。
因此,无论是采用迭代的方式在一个已有网站中增加额外的处理(使用媒体查询和CSS,创建一个用户可以自由选择的PHP的移动版本),还是通过jQuery Mobile之类的移动框架开发一个单独的移动web体验,现在都是时候尝试移动开发了。大多数的web目前还没有准备好适应移动设备上的浏览器。现在你是否应该发挥你的力量,让更多的人能够享受移动访问?
现在是时候准备让web移动化了!
文章来源:Mobilise your website
译文来源:http://www.webapptrend.com/
WebAppTrend是一个独立的技术博客,关注Web App前瞻和实践,以及智能浏览器发展
请大家在关注ITeye的同时,关注我们的新浪微博 @WebAppTrend,关注我们的腾讯微博@WebAppTrend,欢迎加入我们的Q Q群:193775364
看来很多人对于中文的乱码其实没有什么理解,我建议搜索一下几种编码的详细说明(GBK,UTF-8,GB2312),我很难简单解释清楚。实际上,几乎所有条码扫描软件都是外国人做的,其实几乎都是只支持UTF8的,或者少数支持韩文日文编码等,似乎都比较无视中国的汉字编码,而且中文的编码本身都比较多,估计外国人也比较头疼。
另外一个条码的短板就是缺少字符编码的定义,所以实际上所有的条码软件都是靠猜编码来显示的,也就是说条码数据读出来的,但是找不到合适的显示方式,那么你看到的就是乱码了,而实际上没有准确的方法可以猜出文字的编码,所以这些软件基本只能读出utf8了,接触过网站或者java开发的人基本上都习惯于用utf8编码,也就是避免出现乱码的原因了,但是大多数国内条码都比较不靠谱的(或者说由于不懂),用了默认的GBK编码,所以你看到包含中文的条码基本也就是乱码了。解决方法,也是用GBK解码就是了,至于由于错误用了utf8解gbk,是无法直接转化为正确的中文的,要么你自己按utf8的编码规则反向解回去,要么修改源码指定编码。
//************************************************************************************
这两天接了一个条码的项目,发现用zxing读取的条码中文部分都是乱码,先和android的项目同事沟通得知中文编码是GBK码,一开始认定是需要从gbk转utf8的问题,g到转换方法,但是死活不行,又试了很多编码都无法解开乱码,搞到11点多,无奈罢手。这几天寒流下来,天气很冷,出门整个人一缩,赶紧拉上拉链。倒霉催的拉链不知道哪里咬住边了,不上不下,加上搞了半天没弄成火气就大,很像一下把它扯下来。我不停对自己说,不要着急不要着急,生气扯坏东西解决不了任何问题,只会变得更遭,慢慢来,它只是一个拉链,不是吗?找了一个亮点的地方查看,外面没有卡住,由于卡紧脖子,没法翻开里面看,靠摸索没法感觉到哪里卡住。我慢慢上下撮把拉链弄开一点,虽然卡的紧,但是又耐心还是可以松开一些,直到可以翻转拉链头,才看到衣服一边的里子已经整个卡进拉链头的两边,难怪摸不出来,慢慢解开拉链,暗示自己,其实很简单的,长时间无法解决问题一般是解决的思路不对,或者忽略了某些可能的方向,找对了方向,问题往往很简单。首先做一个分析:
1 中文是GBK确定无疑
2 GBK转utf8的方法很简单,搜索结果很一致,说明方法没问题,那么问题应该是源字符串那里。
3 zxing默认是转成utf8出来的,可以比对一下转换前后和对应中文的内码
4 怀疑zxing识别内码有问题,需要根据3的结果判定是否着手分析其源码。
第二天通过比对中文各种编码的内码,发现zxing转utf8前的字符流已经和中文的各种内码完全不一样,向上追溯,发现其实zxing解析出条码流时会尝试判别编码,并进而转为utf8,后面再转utf8实际上已经没有必要了,最关键的是zxing其实的iso版其实无法识别GBK,所以做了一次错误的转码,这样转出来的数据,开发者不管怎么再转都无法复原为中文的了。恩,正如炒熟的种子永远无法发芽,如果老是在栽培方式上找问题,是永远无法获得答案的。
综上,正确的方向往往决定你解决问题的效率,冷静的分析问题很重要。附编码列表:
//************************************************************************************
http://hi.baidu.com/zhmsong/blog/item/9e167ffc623e96f5fc037f99.html
European languages
ASCII, ISO-8859-{1,2,3,4,5,7,9,10,13,14,15,16}, KOI8-R, KOI8-U, KOI8-RU, CP{1250,1251,1252,1253,1254,1257}, CP{850,866}, Mac{Roman,CentralEurope,Iceland,Croatian,Romania}, Mac{Cyrillic,Ukraine,Greek,Turkish}, Macintosh
Semitic languages
ISO-8859-{6,8}, CP{1255,1256}, CP862, Mac{Hebrew,Arabic}
Japanese
EUC-JP, SHIFT_JIS, CP932, ISO-2022-JP, ISO-2022-JP-2, ISO-2022-JP-1
Chinese
EUC-CN, HZ, GBK, CP936, GB18030, EUC-TW, BIG5, CP950, BIG5-HKSCS, BIG5-HKSCS:2001, BIG5-HKSCS:1999, ISO-2022-CN, ISO-2022-CN-EXT
Korean
EUC-KR, CP949, ISO-2022-KR, JOHAB
Armenian
ARMSCII-8
Georgian
Georgian-Academy, Georgian-PS
Tajik
KOI8-T
Kazakh
PT154, RK1048
Thai
TIS-620, CP874, MacThai
Laotian
MuleLao-1, CP1133
Vietnamese
VISCII, TCVN, CP1258
Platform specifics
HP-ROMAN8, NEXTSTEP
Full Unicode
UTF-8
UCS-2, UCS-2BE, UCS-2LE
UCS-4, UCS-4BE, UCS-4LE
utf-8, utf-8BE, utf-8LE
UTF-32, UTF-32BE, UTF-32LE
UTF-7
C99, JAVA
Full Unicode, in terms of uint16_t or uint32_t
(with machine dependent endianness and alignment)
UCS-2-INTERNAL, UCS-4-INTERNAL
Locale dependent, in terms of char or wchar_t
(with machine dependent endianness and alignment, and with semantics depending on the OS and the current LC_CTYPE locale facet)
char, wchar_t
When configured with the option --enable-extra-encodings, it also provides support for a few extra encodings:【额外支持:】
European languages
CP{437,737,775,852,853,855,857,858,860,861,863,865,869,1125}
Semitic languages
CP864
Japanese
EUC-JISX0213, Shift_JISX0213, ISO-2022-JP-3
Chinese
BIG5-2003 (experimental)
Turkmen
TDS565
Platform specifics
ATARIST, RISCOS-LATIN1
多谢
MediaPlayer类可用于控制音频/视频文件或流的播放。关于如何使用这个类的方法还可以阅读VideoView类的文档。
1.状态图
对播放音频/视频文件和流的控制是通过一个状态机来管理的。下图显示一个MediaPlayer对象被支持的播放控制操作驱动的生命周期和状态。椭圆代表MediaPlayer对象可能驻留的状态。弧线表示驱动MediaPlayer在各个状态之间迁移的播放控制操作。这里有两种类型的弧线。由一个箭头开始的弧代表同步的方法调用,而以双箭头开头的代表的弧线代表异步方法调用。
通过这张图,我们可以知道一个MediaPlayer对象有以下的状态:
1)当一个MediaPlayer对象被刚刚用new操作符创建或是调用了reset()方法后,它就处于Idle状态。当调用了release()方法后,它就处于End状态。这两种状态之间是MediaPlayer对象的生命周期。
1.1) 在一个新构建的MediaPlayer对象和一个调用了reset()方法的MediaPlayer对象之间有一个微小的但是十分重要的差别。在处于Idle状态时,调用getCurrentPosition(), getDuration(), getVideoHeight(), getVideoWidth(), setAudioStreamType(int), setLooping(boolean), setVolume(float, float), pause(), start(), stop(), seekTo(int), prepare() 或者 prepareAsync() 方法都是编程错误。当一个MediaPlayer对象刚被构建的时候,内部的播放引擎和对象的状态都没有改变,在这个时候调用以上的那些方法,框架将无法回调客户端程序注册的OnErrorListener.onError()方法;但若这个MediaPlayer对象调用了reset()方法之后,再调用以上的那些方法,内部的播放引擎就会回调客户端程序注册的OnErrorListener.onError()方法了,并将错误的状态传入。
1.2) 我们建议,一旦一个MediaPlayer对象不再被使用,应立即调用release()方法来释放在内部的播放引擎中与这个MediaPlayer对象关联的资源。资源可能包括如硬件加速组件的单态组件,若没有调用release()方法可能会导致之后的MediaPlayer对象实例无法使用这种单态硬件资源,从而退回到软件实现或运行失败。一旦MediaPlayer对象进入了End状态,它不能再被使用,也没有办法再迁移到其它状态。
1.3) 此外,使用new操作符创建的MediaPlayer对象处于Idle状态,而那些通过重载的create()便利方法创建的MediaPlayer对象却不是处于Idle状态。事实上,如果成功调用了重载的create()方法,那么这些对象已经是Prepare状态了。
2) 在一般情况下,由于种种原因一些播放控制操作可能会失败,如不支持的音频/视频格式,缺少隔行扫描的音频/视频,分辨率太高,流超时等原因,等等。因此,错误报告和恢复在这种情况下是非常重要的。有时,由于编程错误,在处于无效状态的情况下调用了一个播放控制操作可能发生。在所有这些错误条件下,内部的播放引擎会调用一个由客户端程序员提供的OnErrorListener.onError()方法。客户端程序员可以通过调用MediaPlayer.setOnErrorListener(android.media.MediaPlayer.OnErrorListener)方法来注册OnErrorListener.
2.1) 一旦发生错误,MediaPlayer对象会进入到Error状态。
2.2) 为了重用一个处于Error状态的MediaPlayer对象,可以调用reset()方法来把这个对象恢复成Idle状态。
2.3) 注册一个OnErrorListener来获知内部播放引擎发生的错误是好的编程习惯。
2.4) 在不合法的状态下调用一些方法,如prepare(),prepareAsync()和setDataSource()方法会抛出IllegalStateException异常。
3) 调用setDataSource(FileDescriptor)方法,或setDataSource(String)方法,或setDataSource(Context,Uri)方法,或setDataSource(FileDescriptor,long,long)方法会使处于Idle状态的对象迁移到Initialized状态。
3.1) 若当此MediaPlayer处于其它的状态下,调用setDataSource()方法,会抛出IllegalStateException异常。
3.2) 好的编程习惯是不要疏忽了调用setDataSource()方法的时候可能会抛出的IllegalArgumentException异常和IOException异常。
4) 在开始播放之前,MediaPlayer对象必须要进入Prepared状态。
4.1) 有两种方法(同步和异步)可以使MediaPlayer对象进入Prepared状态:要么调用prepare()方法(同步),此方法返回就表示该MediaPlayer对象已经进入了Prepared状态;要么调用prepareAsync()方法(异步),此方法会使此MediaPlayer对象进入Preparing状态并返回,而内部的播放引擎会继续未完成的准备工作。当同步版本返回时或异步版本的准备工作完全完成时就会调用客户端程序员提供的OnPreparedListener.onPrepared()监听方法。可以调用MediaPlayer.setOnPreparedListener(android.media.MediaPlayer.OnPreparedListener)方法来注册OnPreparedListener.
4.2) Preparing是一个中间状态,在此状态下调用任何具备边影响的方法的结果都是未知的!
4.3) 在不合适的状态下调用prepare()和prepareAsync()方法会抛出IllegalStateException异常。当MediaPlayer对象处于Prepared状态的时候,可以调整音频/视频的属性,如音量,播放时是否一直亮屏,循环播放等。
5) 要开始播放,必须调用start()方法。当此方法成功返回时,MediaPlayer的对象处于Started状态。isPlaying()方法可以被调用来测试某个MediaPlayer对象是否在Started状态。
5.1) 当处于Started状态时,内部播放引擎会调用客户端程序员提供的OnBufferingUpdateListener.onBufferingUpdate()回调方法,此回调方法允许应用程序追踪流播放的缓冲的状态。
5.2) 对一个已经处于Started 状态的MediaPlayer对象调用start()方法没有影响。
6) 播放可以被暂停,停止,以及调整当前播放位置。当调用pause()方法并返回时,会使MediaPlayer对象进入Paused状态。注意Started与Paused状态的相互转换在内部的播放引擎中是异步的。所以可能需要一点时间在isPlaying()方法中更新状态,若在播放流内容,这段时间可能会有几秒钟。
6.1) 调用start()方法会让一个处于Paused状态的MediaPlayer对象从之前暂停的地方恢复播放。当调用start()方法返回的时候,MediaPlayer对象的状态会又变成Started状态。
6.2) 对一个已经处于Paused状态的MediaPlayer对象pause()方法没有影响。
7) 调用stop()方法会停止播放,并且还会让一个处于Started,Paused,Prepared或PlaybackCompleted状态的MediaPlayer进入Stopped状态。
7.1) 对一个已经处于Stopped状态的MediaPlayer对象stop()方法没有影响。
8) 调用seekTo()方法可以调整播放的位置。
8.1) seekTo(int)方法是异步执行的,所以它可以马上返回,但是实际的定位播放操作可能需要一段时间才能完成,尤其是播放流形式的音频/视频。当实际的定位播放操作完成之后,内部的播放引擎会调用客户端程序员提供的OnSeekComplete.onSeekComplete()回调方法。可以通过setOnSeekCompleteListener(OnSeekCompleteListener)方法注册。
8.2) 注意,seekTo(int)方法也可以在其它状态下调用,比如Prepared,Paused和PlaybackCompleted状态。此外,目前的播放位置,实际可以调用getCurrentPosition()方法得到,它可以帮助如音乐播放器的应用程序不断更新播放进度
9) 当播放到流的末尾,播放就完成了。
9.1) 如果调用了setLooping(boolean)方法开启了循环模式,那么这个MediaPlayer对象会重新进入Started状态。
9.2) 若没有开启循环模式,那么内部的播放引擎会调用客户端程序员提供的OnCompletion.onCompletion()回调方法。可以通过调用MediaPlayer.setOnCompletionListener(OnCompletionListener)方法来设置。内部的播放引擎一旦调用了OnCompletion.onCompletion()回调方法,说明这个MediaPlayer对象进入了PlaybackCompleted状态。
9.3) 当处于PlaybackCompleted状态的时候,可以再调用start()方法来让这个MediaPlayer对象再进入Started状态。