豆瓣界面新改版,“菲兹定律”帮你解读背后思路

mikel阅读(1246)

.

  豆瓣改版之后,关于豆瓣导航条的设计特性引起了大家的广泛讨论,其中,菲兹定律在用户界面设计的应用再次吸引了大家的关注和重视。

海旭Roy的文章深入浅出地向我们介绍了菲兹定律极其在产品界面中的应用实例,通过这篇文章我们可以获得的认识在于,新改版后的豆瓣界面,从某种角度而言就是遵循“菲兹定律”不断优化用户操作的产物。

今天说说费兹定律(Fitts’ law),网上有很多资料,算是老生常谈。下面看一张图,网上找到的,算是fitts在手机终端上的一些应用吧。

 

 一,什么是Fitts定律

物体从开始的位置移动到最后的目标所需的时间取决于两个因素:到目标的距离A和目标的大小。

t = a + b log2 (2A / W)

其中a(=0.230s),b(=0.166s)是经验参数,A=指示位置与目标的距离,W=目标尺寸。它们依赖于具体的指点设备的物理特性,以及操作人员和环境等因素。

 

结论:目标越大,指向越快,时间越短。同样地,目标越近,指向越快,时间越短。也就是说,定位一个目标的时间,取决于目标与当前位置的距离,以及目标的大小(在特定场景下,当然还会有其他因素)。

 二,菲兹定律的应用

目前应用在PC端和移动终端产品的设计上。

1、在设计用户界面的过程中,按钮以及一些GUI控件的尺寸应设计得比较合理,相比而言,点击小的控件会相对困难一些。因此在当前的软件及网站的GUI设计过程中,工具栏的图标都在变大,另外一些文字性说明的按钮背景区域也在增大。

2、不论鼠标移到什么地方,由于鼠标都可以一直保持在屏幕的边缘,因此边缘或是角落区域相比其他区域更容易到达。在Mac OS X产品系统界面设计中的Dock设计,顶端的菜单栏设计,以及Windows XP系统界面的左下角“开始”按钮和Mac OS X系统界面的菜单栏设计均是这个理论的实际应用体现。

 

3、弹出菜单会比下拉菜单选择得更快,因为用户避免了在菜单间滑动。

4、饼状菜单会比线型菜单更易选择,而且错误率更低,这里面有两个原因:

饼状菜单的每个菜单项和菜单中心的距离都一致

饼状菜单的每个菜单项的楔形目标区域都非常大,一般都会扩展到屏幕的边缘

 

5、目标点的位置实际上是边缘的一些像素加上边缘之外的全部面积。所以,这样看来目标点就足够大了。原因是基于fitts定律,它的一个分母的增大了而使得界面的效率提高了。

 

案例:

 

三,菲兹定律告诉我们的道理

1、我们想要更容易点击到控件,就应该放在屏幕的边缘或角落里。让常用的控件更大,更容易辨别;

2、使用屏幕的边缘和角落让控件有效扩大,永远不要把控件放在离屏幕边缘或角落一个像素远的地方;

3、边缘之外的地方,也可以算作目标点的面积,这样一来,目标的面积就被无限的放大了,也更方便用户操作。

来源;http://tech2ipo.com/57051

[转载]设计引导---一个鸭子游戏引发的设计(多态,继承,抽象,接口,策略者模式) - 肅 - 博客园

mikel阅读(1113)

[转载]设计引导—一个鸭子游戏引发的设计(多态,继承,抽象,接口,策略者模式) – 肅 – 博客园.

  这篇博文是从实际生活中,提炼出来的设计理念,它现在是骨架,现在我加以代码实例,完成程序的血肉,以求让大家活生生的体会设计中的精髓。

自从我们学习面向对象编程以来,它方便了我们的思维思考模式,一个事物具备什么,就以对应的属性及方法加之。

( ̄▽ ̄) 没有什么难的,但是你学到的是最基础的语法和连自己都不是很了解的语言,用一段C语言程序,你可以很轻松的把它改成C#,JAVA等,这有什么难的?大多 数程序员们扭曲了C#语言,把C的语法都移植到C#上(在我不了解C#的时候,我自己都这么做过),错了不可怕,可怕的是错了还不肯改。

语言是一种工具,学会了都是想通的,但是设计思想不同决定了语言的本质区别。

进入正题,一步一步来剖析一个简单的鸭子游戏程序。

首先设计一个鸭子对象,是不是?大致这样:

复制代码
 1     public class Duck  2  {  3         void quack(){  4             //...鸭子都会叫
 5  }  6         void swim(){  7            //...都会游泳
 8  }  9         void Display() { 10         //...外观
11  } 12     }
复制代码

然后鸭子游戏中有各种鸭子一边游泳戏水,一边呷呷叫,各种鸭子都继承Duck类哦,游戏在预料之中运行。

这应该是标准的OO(Object Oriented)技术吧?游戏完美运行中………

目前鸭子会叫会游泳,都在水里多没意思?来个创新吧:

丑小鸭也能飞上青天??o(∩_∩)o

现在想要鸭子飞,那么就要给鸭子添加一个飞行方法,好比这样:

复制代码
    public class Duck { void quack(){ //...鸭子都会叫
 } void swim(){ //...都会游泳
 } void Display() { //...外观
 } void Fly() { //...飞行
 } }
复制代码

方法已加,游戏中的小鸭子们可以飞咯。

现在问题,才刚刚出现:

在演示程序的时候,“橡皮假鸭”在屏幕上飞来飞去,游戏里面有各种各样的鸭子。

当没有Fly()的时候,小鸭子们可以很平稳的运行。在父类中加上Fyl(),会导致所有的子类都具备Fly(),连那些不该具备的子类也无法免除,所以:

对代码所做的局部修改,影响层面可不只是局部。

看看这张图,说不定和你的想法不谋而合:

覆盖掉“橡皮鸭”的飞行方式。这是个不错的选择,这样一来,“橡皮鸭”也不会到处乱飞了~~(注意哦“橡皮鸭”会叫的–“吱吱”)。

游戏中现在又加入一种鸭子~问题又来啦~~

现在加入成员是-“诱饵鸭”(DecoyDuck)它是木头做的假鸭,它不会飞当然也不会叫~

OK,现在对于这个新成员,就这么做:

继续覆盖它的方法,它只有老老实实的在水里面游!

你们觉得这种繁琐的工作,什么时候才是个头呢?鸭子种类无限,你的噩梦无限~继承这个解决方法,看来果断不行啊,要换要换。

你觉得这个设计怎么样:

我定义一些接口,目前先做两个,一个Flyable,一个Quackable:

Duck类也改掉,只包含两个方法:Swim(),Display():

然后让不同的子类再继承Duck类的时候,分别实现一下Fly()和Quack(),接口也用上了,你觉得怎么样?

好像有点用,但是,再换个大的角度想,子类继承实现的那些Fly(),Quack()都是些重复代码,然而,重复代码是可以接受的,但是,在你维护的时候,假如有30个Duck子类吧,要稍稍修改一下那个Fly(),有没有觉得可维护性瞬间就低到下限?

在这个新的设计方法中,虽然解决了“一部分”问题,但是,这造成了代码无法复用!有没有觉得?还有更可怕的哦,会飞的鸭子,那飞行动作可不是千篇一律的,来个空翻360°旋转这个动作,你又要怎么做?o(∩_∩)o

不管你在何处工作,用何种编程语言,在软件开发上,一直伴随你的那个不变真理是什么? (把你想到的答案,写在评论上吧^_^,期待你的回答

把这个先前的设计都清零……

现在我们知道使用继承并不能很好的解决问题,因为鸭子的行为在子类里不断地改变,并且让那些子类都有这些行为是不恰当的,Flyable和 Quackable接口似乎不错,解决了问题(只有会飞的鸭子才继承Flyable),但是这依旧让你有很多任务去做,你依旧不能做到代码复用,你在维护 的时候,依旧要往下追踪,一 一去修改对应的行为。

对于这个问题,现在真正有个设计原则,能解决这个问题,它能实现代码复用,能添加和修改使系统变得更有弹性。

设计原则:

    找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。

这是些理论知识,对于骨架,我会丰满出它的羽翼。继续看吧,你会有收获!

现在,是时候取出Duck类中的变化的部分了!

目前可变的是fly和quack相关部分,它们会变化,现在单独把这两个行为从Duck类中分开,建立一种组新类代表每个行为。

先做个飞行行为的接口:

1    public interface FlyBehavior 2  { 3         void Fly(); 4     }

呷呷叫行为的接口:

1    public interface QuackBehavior 2  { 3        void quack(); 4     }

是否听说过这么一个设计理念:

  针对接口编程,而不是针对实现编程。

而“针对接口编程”真正的意思是“针对抽象类编程”。

“针对接口编程”的关键就在多态。利用多态,程序可以在针对抽象类编程,执行时会根据实际状况执行到真正的行为,不会被绑死在抽象类的行为上。

再深挖一点,“针对抽象类编程”这句话,可以更明确地说成“变量的声明类型,应该是抽象类型,这可以是一个抽象类,或是一个接口”!不理解没关系!接下来我们用程序来让大家慢慢吃透这个概念!

举个传统的例子:

针对实现编程:

1 Dog d = new Dog(); 2 d.bark();//“汪汪”叫行为

针对接口或抽象类编程:

Animal animal = new Dog(); animal.makeSound();//这个方法实现“汪汪”叫

这个不明白?没关系,有图:

现在让我们来重新实现鸭子游戏中的设计吧!

先设计飞行行为:

复制代码
    class FlyWithWings:FlyBehavior { public void Fly() { Console.WriteLine("我会飞啦~!"); } } class FlyNoWay : FlyBehavior { public void Fly() { //什么都不做,它不会飞
 } }
复制代码

我把两个类放在一起了,这方便大家阅读,实际上应该分开的。

再看看“呷呷”叫行为:

 

复制代码
 1     class Quack : QuackBehavior  2  {  3        public void quack()  4  {  5             Console.WriteLine("呷呷!");  6  }  7  }  8 
 9     class Squeak : QuackBehavior 10  { 11        public void quack() { 12             Console.WriteLine("吱吱!");//橡皮鸭
13  } 14  } 15 
16     class MuteQuack:QuackBehavior 17  { 18        public void quack() 19  { 20             Console.WriteLine(".......");//"诱饵鸭"不会叫
21  } 22     }
复制代码

行为做好了~来实现Duck类

 

复制代码
 1     public abstract class Duck  2  {  3         public FlyBehavior flybehavior;  4         public QuackBehavior quackbehavior;  5 
 6         public void performQuack() {  7  quackbehavior.quack();  8  }  9         public void performFly() 10  { 11  flybehavior.Fly(); 12  } 13         public virtual void Swim(){ 14             Console.WriteLine("~~游~~"); 15  } 16         public virtual void Display(){} 17     }
复制代码

结构很简单,不是吗?定义QuackBehavior,FlyBehavior,每只鸭子都会引用实现QuackBehavior接口对象,让它们来处理鸭子的行为。

想要呷呷叫的效果,就要quackbehavior对象去呷呷叫就可以了,我们现在不用再关心quackbehavior接口的对象是什么,只要关系Duck如何叫就行了。

这个quackbehavior接口可以重用了哦。有没有发现?在什么地方可以重用呢?思考下,我后面再提。
好了,现在来具体实现鸭子实体了:

复制代码
 1     public class MallarDuck : Duck  2  {  3         public MallarDuck() {  4             quackbehavior = new Quack();  5             flybehavior = new FlyWithWings();  6  }  7         public override void Display()  8  {  9             Console.WriteLine("我是一只美丽的绿头鸭!"); 10  } 11     }
复制代码

o(∩_∩)o大功就要告成了,  看Program:

复制代码
1         static void Main(string[] args) 2  { 3             MallarDuck mallard = new MallarDuck(); 4  mallard.Display(); 5  mallard.Swim(); 6  mallard.performQuack(); 7  mallard.performFly(); 8 
9         }
复制代码

一目了然,这个程序要做什么,怎么做,很简单吧?

看看运行结果:

代码也贴完了,程序确实可以运行,现在看下这个设计的最后一个概念:

  多用组合,少用继承。

正如你看见的,使用组合建立系统具有很大的弹性,不仅仅将算法族封装成类,更可以在“运行时动态地改变行为”。

不知道什么是“运行时动态地改变行为”??

好,那我再演示一个,就拿那美丽的绿头鸭做例子:

Duck类最新修改:

复制代码

 1  public abstract class Duck  2  {  3         public FlyBehavior flybehavior;  4         public QuackBehavior quackbehavior;  5 
 6         public void performQuack() {  7  quackbehavior.quack();  8  }  9         public void performFly() 10  { 11  flybehavior.Fly(); 12  } 13         public virtual void Swim(){ 14             Console.WriteLine("~~游~~"); 15  } 16         public virtual void Display(){} 17 
18         public void SetFlyBehavior(FlyBehavior flyb)//额外添加
19  { 20             flybehavior = flyb; 21  } 22 
23     }
复制代码

然后我再添加一个火箭动力:

复制代码
    class FlyRockePowered : FlyBehavior { public void Fly() { Console.WriteLine("打了鸡血!4200米/秒,加速飞行!"); } }
复制代码

看看Program:

复制代码
 1     class Program  2  {  3         static void Main(string[] args)  4  {  5             MallarDuck mallard = new MallarDuck();  6  mallard.Display();  7  mallard.Swim();  8  mallard.performQuack();  9  mallard.performFly(); 10 
11             mallard.SetFlyBehavior(new FlyRockePowered()); 12  mallard.performFly(); 13  } 14     }
复制代码

结果:

动态添加了吧?修改一下很容易吧?

至于那个quackbehavior接口重用问题

鸭鸣器知道吧?猎人用这个东西模拟鸭子叫,引诱野鸭,这个不是个很好的重用吗?o(∩_∩)o  更多重用只局限于你的想象~

如果你认真看完了这个,那么下面这个奖章是给予你的:

你学会了策略者设计模式o(∩_∩)o

你再也不用担心系统遇到任何变化。

 策略者模式

  定义了算法族,分别封装起来,让它们之间可以相互替换,此模式让算法的变化独立于使用算法的用户。

 

[转载]Knockoutjs快速入门 - yinzixin - 博客园

mikel阅读(1065)

[转载]Knockoutjs快速入门 – yinzixin – 博客园.

Knockoutjs是一个JavaScript实现的MVVM框架。主要有如下几个功能:

1. Declarative bindings

2. Observables and dependency tracking

3. Templating

  1. 它对于分离前台的业务逻辑和视图简化数据绑定过程有显著的作用。闲言少叙,直接看例子,如何下载也不说了,如果用VS开发的话用Nuget就可以一键搞定。
  2. 1. 基本绑定和依赖跟踪

首先需要定义一个ViewModel:

<script type="text/javascript"> function ViewModel() { this.firstName = "Zixin"; this.lastName = "Yin"; } </script>

然后是一个用来显示这个ViewModel的View:

<div> <p data-bind="text: firstName"></p> <p data-bind="text: firstName"></p> <input data-bind="value: firstName"/> <input data-bind="value: lastName"/> </div>

 

从这个view中可以看到声明式绑定的含义,只需要在标签上使用data-bind属性,就可以将数据的值绑定到相应的地方。有了View和ViewModel还需要代码将这两者关联起来:

 ko.applyBindings(new ViewModel());

 

将他们放到一起,注意,applyBinding那代码必须在页面全部加载完之后执行。页面显示为:

image

下面再看observables,这个功能不是与生俱来的,必须要把View Model设置为observable,方法如下:

function ViewModel() { this.firstName = ko.observable("Zixin"); this.lastName = ko.observable("Yin"); }

其他都不需要改变,这时候,如果改变输入框中的值,当焦点离开的时候,可以发现p中的值也跟着改变了:

image

下面再看dependency tracking,也就是如果一个值依赖多各值,其中任何一个值发生变化,它都会自动发生变化. 这是通过computed方法实现的,代码如下:

function ViewModel() { this.firstName = ko.observable("Zixin"); this.lastName = ko.observable("Yin"); this.fullName = ko.computed(function () { return this.lastName() + " " + this.firstName(); },this); } 

注意获得一个observable的值是一个函数调用。这样当first 或者last name发生变更的时候fullName也会自动跟着变更。

image

也可以通过代码改变observable的值,页面会自动刷新:

function ViewModel() {  //……… this.capitalizeLastName = function () { this.lastName(this.lastName().toUpperCase()); } } 

在页面上添加一个按钮:

<button data-bind="click: capitalizeLastName">Caps</button>

点击按钮之后便会出发viewmodel的capitalizeLastName方法,要改变一个observable的值的方法就是将新值作为函数调用的参数。点击之后:

image

 

2. 列表绑定

加入我们有如下的订单ViewModel,使用observableArray来跟踪数组的变化。

var products=[{name:"Thinkpad X1",price:9000}, {name:"Hp ProBook",price:5555}, {name:"Mouse",price:45} ]; function Order() { var self = this; self.items = ko.observableArray([ //This data should load from server new Item(products[0], 1), new Item(products[1],2)]); self.price = ko.computed(function () { var p=0; for (var i = 0; i < self.items().length; i++) { var item = self.items()[i]; p += item.product.price * item.amount(); } return p; }, self); }

Order里面的Item实际上应该是从服务器获得的,Item定义如下:

function Item(product, amount) { var self = this; this.product = product; this.amount = ko.observable(amount); this.subTotal = ko.computed(function () { return self.amount() * self.product.price; }, self); }

 

ViewModel准备好之后,就可以实现View。这次需要使用foreach绑定,如下:

<table> <thead> <tr> <td>Name</td> <td>Amount</td> <td>Price</td> </tr> </thead> <tbody data-bind="foreach: items"> <tr> <td data-bind="text: product.name"></td> <td><select data-bind="options:[1,2,3,4,5,6],value: amount"></select></td> <td data-bind="text: subTotal"></td> </tr> </tbody> </table> <h3>Order Price:<span data-bind="text: price"></span></h3> 

 

这样一个基本的订单页面就好了,能够选择数量,价格会自动更新:

image

下面给订单加上增删产品的功能,先给Order加上如下的方法:

function Order() { //…… self.remove = function (item) { self.items.remove(item); } self.addMouse = function () { self.items.push(new Item(products[2],1)); } }

 

给表格加上一列删除按钮:

<td><a href="#" data-bind="click: $root.remove">Remove</a></td>

再在表格底部加上一个增加产品的按钮:

<button data-bind="click: addMouse">Add a Mouse</button> 

 

这时候,observableArray的功能就体现了,当你点击删除按钮或者底部的按钮的时候,页面节点会跟着变化,而不需要手动更新DOM节点,这使得前端的JS大大简化。

image

[转载]C#实现QQ助手(登录QQ,发消息,查任意QQ资料)附源码下载 - 苏飞-C#.Net - 博客园

mikel阅读(1042)

[转载]C#实现QQ助手(登录QQ,发消息,查任意QQ资料)附源码下载 – 苏飞-C#.Net – 博客园.

C#实现QQ登录发消息,加好友,查资料等功能
C#实现QQ登录,发消息等功能
提供下源代码下载:http://www.ctdisk.com/file/13565788

这个好些人使用不了,说登录不上,那么我有个办法一定可以

你新注册一个QQ,记住一定不要在电脑上登录,先在手机上登录一下,然后再拿你申请的这个“QQ到我的软件上登录是一定可以的,我亲测试的。希望大家注意这点啊

我给大家提供下我刚注册的QQ是可以的QQ2859992477  密码是cckan.net

大家别忘记给个评论或者建议哦

1.登录
2.好友列表
3.查任意QQ资料(所有QQ都能查询出来哦)
4.加好友
5.删除好友
6.修改登录状态
7.退出
8.发消息
我们先来看看实现效果吧
登录之后的效果

查询好友资料效果

这个只是给大家提供一个思路吧,可以用来练手,实现使用还需要修改才行,不过我喜欢里面的可以查询任意QQ的资料,是任意QQ,而不是任意好友哦,

也就是说我们可以查询所有QQ的资料,这个功能我相信应该还是有一些价值的吧。或许有朋友会用的上,

代码是可以直接下载的,单击上面的网址直接就可以下载。

————————————————————- 签名部分 ————————————————————————

签名:做一番一生引以为豪的事业;在有生之年报答帮过我的人;并有能力帮助需要帮助的人;    

软件开发,功能定制,请联系我给我留言 QQ:361983679 Email:sufei.1013@163.com  MSN:sufei.1013@163.com

[转载]非常全面的浏览器useragent判断代码 - Rover.Tang - 博客园

mikel阅读(991)

[转载]非常全面的浏览器useragent判断代码 – Rover.Tang – 博客园.

什么是User Agent? 懂一点网页制作的人应该都明白。简单的说,User Agent就是用来识别浏览器名称、版本、引擎以及操作系统等信息的内容。于是乎,User Agent的判断就成为识别浏览器的关键,不仅仅如此,移动互联网开发势头迅猛,那么通过User Agent判断桌面端设备或移动设备就变的很为重要。当然,通过User Agent也可以用来改善一定的兼容性,比如判断得到用户用IE6浏览器那么就是用不同的代码。这些,我想许多的WEB开发中已经成为考虑的第一个问题。

那么,有没有一种好的判断User Agent的代码呢?网上应该有很多,比较简单的直接判断下iOS和Android,然后进入不一样的页面。比如百度地图就是如此,桌面设备访问http://map.baidu.com/地址,而移动设备就访问http://map.baidu.com/mobile/地址,你用桌面设备访问移动网址则还会自动跳回到桌面地址。这就是通过判断User Agent做到的效果。

我 没有去细究百度地图是如何判断桌面设备和移动设备的,但我想要找到一个比较全面的User Agent判断代码。不知道大家是否知道有一个叫html5test的网站,其中要作用就是判断你的浏览器对HTML5的支持程度,但同时,他还提供了浏 览器和设备信息。我在桌面端通过Firefox、Chrome、IE、Safari等测试都很正确,在移动设备端,Apple iPod下测试了Safari、Opera mini和UC,除了UC无法识别外其他都OK,另外在Nokia E63下也做了测试,默认浏览器OK,UC无法识别。

既然HTML5TEST已经做的如此全面,连浏览器、引擎、设备类型和设备名称等都 一应俱全,那就说明在User Agent上的判断应该是做的非常不错的,至少,是我现今看到的最好的了。然后又看到HTML5TEST本身是开源的,那就下载下来分析一下吧。看过后, 没有想到,HTML5TEST竟然封装的那么好,一个JS包含了基本上全部的User Agent判断,这个js文件,可以直接浏览这里

我把这个JS单独拿出来,再加上几句html,做了个浏览器测试的页面,大家可以通过访问这个页面来查看自己的浏览器信息、设备信息等,效果请见下图。地址:http://rovertang.com/labs/useragent/

useragent

我想,通过这一个js,得到了浏览器信息、设备类型和设备名称,接下来怎么做就看你自己的了,不过也有一个小小的担心:一个大于50K的js文件,对页面的加载似乎是有点压力的。

顺便说一下,发现IE对HTML5的DOCTYPE标签支持不是很好,有测试不通过的现象。万恶的IE确实挺让人恼火的。

最后推荐一个FireFox的组件吧,我想许多人都用过,就是User Agent Switcher,作用呢就是在FF下伪装成其他浏览器,比如iPhone的浏览器,这样就可以直接在FF下浏览移动网页进行代码分析了。

Rover Tang
Mail:rover.tang[at]qq.com
Twitter(Weibo):rovertang
http://RoverTang.com

[转载]移动终端web开发必备知识 - shawn.xie - 博客园

mikel阅读(838)

[转载]移动终端web开发必备知识 – shawn.xie – 博客园.

来源:腾讯 ISUX

移动设备的用户越来越多,每天Android手机的激活量都已经超过130万台,所以我们面向移动终端的WebAPP也开始跟进了。本文主要介绍webapp的开发与调试的相关知识和经验,以及给出几种可选的解决方案。

一、基本概念

(1) CSS pixels与device pixels

CSS pixels: 浏览器使用的抽象单位, 主要用来在网页上绘制内容。

device pixels: 显示屏幕的的最小物理单位,每个dp包含自己的颜色、亮度。

等值的 CSS pixels在手机屏幕上占多大的位置,这不是固定的,这取决于很多属性。经过分析和总结,我们可以得出这么一条公式: 1 CSS pixels = (devicePixelRatio)^2 device pixels (^2是平方的意思,至于 devicePixelRatio是什么东西,后面会讲解) 。

(2) PPI/DPI

PPI,有时也叫DPI,所表示的是每英寸所拥有的像素(pixel)数目,数值越高,即代表显示屏能够以越高的密度显示图像。(注:这里的像素, 指的是device pixels。)搞清楚了PPI是什么意思,我们就能很容易理解PPI的计算方式了,我们需要首先算出手机屏幕的对角线等效像素,然后处以对角线(我们平 常所说的手机屏幕尺寸就是说的手机屏幕对角线的长度),就可以得到PPI了。准确的计算公示大家可以参照下图。比较有意思的是,根据公式计算出来的 iPhone 4的PPI为330,要比苹果官方公布的326要高一点点。

同理,以HTC G7为例,480*800的分辨率,3.7英寸,算出来就是252的PPI。

(3) 密度决定比例

我们计算PPI就是为了知道一部手机设备是属于哪个密度区间的,因为不同的密度区间,对应着不同的默认缩放比例,这是一个很重要的概念。

由上图可知,PPI在120-160之间的手机被归为低密度手机,160-240被归为中密度,240-320被归为高密度,320以上被归为超高密度(Apple给了它一个上流的名字——retina)。

这些密度对应着一个特定的缩放比例值,拿我们最熟悉的iphone4或4s来说,它们的PPI是326,属于超高密度的手机。当我们书写一个宽度为 320px的页面放到iphone中显示,你会发现,它竟然是满宽的。这是因为,页面被默认放大了两倍,也就是640px,而iphone4或4s的宽, 正是640px。

图中把高密度的一类圈起来,是因为这是Android手机的统计数据,在国内安卓手机市场中,高密度的设备占了绝大多数的市场份额,这是很重要的一点,也是我们做安卓端webapp要注意的关键点。

(4) viewport的使用

viewport总共有5个属性,分别如下:

复制代码
<meta name="viewport" content=" height = [ pixel_value |device-height] , width = [ pixel_value |device-width ] , initial-scale = float_value , minimum-scale = float_value , maximum-scale = float_value , user-scalable =[yes | no] , target- densitydpi = [ dpi_value | device-dpi| high-dpi | medium-dpi | low-dpi] " />
复制代码

在这些属性里面,我们重点关注target-densitydpi,这个属性可以改变设备的默认缩放。 medium-dpi是target-densitydpi的默认值,如果我们显式定义target-densitydpi=device-dpi,那么 设备就会按照真实的dpi来渲染页面。打个比方说,一张320*480的图片,放在iphone4里面,默认是占满屏幕的,但如果定义了target- densitydpi=device-dpi,那么图片只占屏幕的四分之一(二分之一的平方),因为iphone4的分辨率是640*960。

二、解决方案

(1) 简单粗暴

如果我们按照320px宽的设计稿去制作页面,并且不做任何的设置,页面会默认自动缩放到跟手机屏幕相等的宽度(这是由于 medium-dpi是target-densitydpi的默认值,和不同密度对应不同缩放比例所决定的,这一切都是移动设备自动完成的 )。所以这种解决方案,简单,粗暴,有效。但有一个致命的缺点,对于高密度和超高密度的手机设备,页面(特别是图片)会失真,而且密度越多,失真越厉害。

(2) 极致完美

在这种方案中,我们采用 target-densitydpi=device-dpi,这样一来,手机设备就会按照真实的像素数目来渲染,用专业的话来说,就是1 CSS pixels = 1 device pixels。比如对于 640*960的 iphone,我们就可以做出 640*960的页面,在iphone上显示也不会有滚动条。当然,对于其他设备,也需制作不同尺寸的页面,所以这种方案往往是使用媒体查询来做成响应式 的页面。这种方案可以在特定的分辨率下完美呈现,但是随着要兼容的不同分辨率越多,成本就越高,因为需要为每一种分辨率书写单独的代码。下面举个简单的例 子:

复制代码
<meta name="viewport"content="target- densitydpi =device-dpi, width=device-width " /> #header { background:url (medium-density-image.png); } @media screen and (- webkit -device-pixel-ratio:1.5) { /* CSS for high-density screens */ #header { background:url (high-density-image.png);} } @media screen and (- webkit -device-pixel-ratio:0.75) { /* CSS for low-density screens */ #header { background:url (low-density-image.png);}
复制代码

(3) 合理折中

针对安卓设备绝大多数是高密度,部分是中密度的特点,我们可以采用一个折中的方案:我们对480px宽的设计稿进行还原,但是页面制却做成 320px宽(使用background-size来对图片进行缩小),然后,让页面自动按照比例缩放。这样一来,低密度的手机有滚动条(这种手机基本上 已经没有人在用了),中密度的手机会浪费一点点流量,高密度的手机完美呈现,超高密度的手机轻微失真(超高密度的安卓手机很少)。这种方案的优点非常明 显:只需要一套设计稿,一套代码(这里只是讨论安卓手机的情况)。

三、开发调试

(1) weinre远程实时调试

Web开发者经常使用Firefox的firebug或者Chrome的开发人员工具进行Web调试,包括针对JavaScript,DOM元素和CSS样式的调试。但是,当我们期望为移动Web站点或应用进行调试时,这些工具就很难派上用场。

weinre就是一个帮助我们在桌面来远程调试运行在移动设备浏览器内的Web页面或应用的调试工具。weinre是WEb INspector REmote的简写,现在是Apache的一个开源项目,托管在github

下面将介绍如此在日常工作使用它。

首先,我们要下载weinre的jar包——项目官方已经找不到该jar文件,网上能够找到,这里建议搭建个独立的web服务器,jar运行后是一个本地的服务器,和web服务器差不多~~

然后通过运行dos命令来启动它(请注意在你的电脑上已经安装有JDK)。运行命令如下,需要把路径改成你的实际文件位置:

java -jar d:\tools\weinre-jar\weinre.jar –httpPort 8081 –boundHost -all- (httpPort是指定服务端口,boundHost参数说明可以使用IP访问,all参数代表支持所有的host)。

访问localhost:8081,如果看到如下的页面,说明weinre已经启动成功:

输入Debug client user interface地址(调试客户端UI地址)。本例中即:http://localhost:8081/client/#anonymous,其 中#anonymous是默认的调试id(Debug id)。如果这个weinre调试服务器只是由你一个人使用,那么你可以使用默认的Debug id:anonymous。 启动的weinre调试客户端ui如下图:

在需要调试的页面加入中以下脚本:<script type=”text/JavaScript” src=”http://localhost:8081/target/target-script-min.js#anonymous”>& lt;/script>,注意把localhost换成手机能够访问的真实IP地址。当手机访问这个页面时,weinre客户端就会检测到目标设 备,然后就可以对它进行调试了。

因为手机上不方便截图,我这里就用两个浏览器窗口来展示效果,其实手机上的效果跟右边是一样的。

(2) AVD模拟器调试

静态页面并不能满足我们的需求,很多实际效果比如touch事件,滚动事件,键盘输入事件等,都需要在真实的环境下测试,这时就需要用到模拟器。就 像我们测试ie6一样,AVD模拟器可以类比于PC上的虚拟机,当我们需要测试某一特定的机型时,我们可以新建一个AVD,进行一系列的测试。不过使用 AVD的前提是已经部署好android的开发环境,这个需要JDK + android SDK + Eclipse + ADT,还是稍微有点繁琐。

(3)手机抓包与配host

在PC上,我们可以很方便地配host,但是手机上如何配host,这是一个问题。

这里主要使用fiddler和远程代理,实现手机配host的操作,具体操作如下:

1.首先,保证PC和移动设备在同一个局域网下;

2.PC上开启fiddler,并在设置中勾选“allow remote computers to connect”

3.手机上设置代理,代理IP为PC的IP地址,端口为8888(这是fiddler的默认端口)。通常手机上可以直接设置代理,如果没有,可以去下载一个叫ProxyDroid的APP来实现代理的设置。

4此时你会发现,用手机上网,走的其实是PC上的fiddler,所有的请求包都会在fiddler中列出来,配合willow使用,即可实现配host,甚至是反向代理的操作。

 总结

以上就是我们在实际开发中积累的一些经验和技巧,希望能够给大家一些帮助,如果你有好的方法或者工具,也请在留言中分享~~

[转载]不同泛型参数区分的独立类型 - 老赵点滴 - 追求编程之美

mikel阅读(1129)

[转载]不同泛型参数区分的独立类型 – 老赵点滴 – 追求编程之美.

相对于的Java的“类型擦除(Type Erasure)”来说,.NET中的泛型可谓是真正的泛型,这让我们可以有能力区分运行时所使用的不同的具体类型,大大增强了程序设计的性能和表现能力。

打个比方,在Java 8中终于引入了Lambda表达式,但是由于它的伪泛型只能是一个“引用类型”而不能是“基础类型”,因此我们没法从int数组发起函数式操作,最后也没法回到List<char>这种类型(事实上这种类型在Java中根本不存在)。这除了影响编程体验和表达能力以外,对于内存和性能都有大量额外的开销。试想,谁希望在找出符合条件的一万个int数值的时候,必须额外创建一万个Integer对象,导致堆上增加几百上千K的空间,还有一万个对象带来的GC压力?当然,这次我们暂时不谈这方面,还是来谈谈.NET中“真泛型”这一特点所带来的编程便利。

在.NET中,我们编写一个泛型类型的时候,只会给出一个泛型类型的定义,例如List<T>,我们检查typeof(List<>).IsGenericDefinition也会得到true。然而,程序在真正运行的时候,使用的都是提供了具体泛型参数的类型,例如List<int>或是List<string>。我们没法创建一个“泛型定义”的实例或是访问它的静态成员等等,最多使用反射来访问它的信息。

在运行过程中,.NET运行时会(在第一次使用时)为不同的值类型创建一份不同的代码,而让所有的引用类型共享同一份代码。这是因为,假如T是值类型,那么生成的代码操作的都是栈上的数据,需要操作的字节数会有所不同,而引用类型都只需要操作16或32字节的地址,是一致的。当然,理论上List<long>List<DateTime>是可以共享代码的,因为它们其实都只是一个64字节的长整型,但是还是有些情况,尽管都是64字节的长度,如List<long>List<double>就不能共享代码。因此,运行时就统一为不同的值类型都创建不同的代码了。这的确会带来一定的额外开销,但在我看来,相比“真泛型”带来的便利,这点开销完全是值得的。想要了解更多这方面的内容,可以参考著名的Joe Duffy同学《On generics and (some of) the associated overheads》这篇文章。

不过无论执行的代码是否共享,不同具体类型参数的类型都是各自独立的,它们各有各的元数据,各有各的需方法表等等,因此它们的静态成员也是各自独立的。之前我也写过这方面的文章,例如它可能会让人上当,也可以利用这点写出高效的实现。这里我可以举出后者的另一个例子,例如在.NET中的ConcurrentDictionary实现中需要知道当前TValue类型的读写操作是否是原子的,它的实现就是这样的:

// Whether TValue is a type that can be written atomically (i.e., with no danger of torn reads) private static readonly bool s_isValueWriteAtomic = IsValueWriteAtomic();

/// <summary> /// Determines whether type TValue can be written atomically /// </summary> private static bool IsValueWriteAtomic()
{
    Type valueType = typeof(TValue);

    // // Section 12.6.6 of ECMA CLI explains which types can be read and written atomically without // the risk of tearing. // // See http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-335.pdf // bool isAtomic =
        (valueType.IsClass)
        || valueType == typeof(Boolean)
        || valueType == typeof(Char)
        || valueType == typeof(Byte)
        || valueType == typeof(SByte)
        || valueType == typeof(Int16)
        || valueType == typeof(UInt16)
        || valueType == typeof(Int32)
        || valueType == typeof(UInt32)
        || valueType == typeof(Single);

    if (!isAtomic && IntPtr.Size == 8)
    {
        isAtomic |= valueType == typeof(Double) || valueType == typeof(Int64);
    }

    return isAtomic;
}

我相信,假如让很多同学来实现这部分逻辑的话,就会创建IsValueWriteAtomic这样的静态方法,然后在需要的时候反复调用。但事实上,由于不同的泛型参数所带来的具体类型完全独立,因此我们完全可以像.NET那样将这个函数的结果保存在一个静态变量中,然后每次访问即可。

这个特性有时还可以帮助我们简化一些代码,举个最简单的例子:

public class SingletonBase<T> where T : new()
{
    public static readonly T Instance = new T();
}

public class MySingleton1 : SingletonBase<MySingleton1>
{
    // ... }

public class MySingleton2 : SingletonBase<MySingleton2>
{
    // ... }

这样我们就可以不用在每个类型中加上一个只读的Instance静态成员了。当然,这个例子简单地几乎没有实用意义,我们以后会来讨论更有价值的使用案例。

[原创]ASP.NET MVC 中 过滤图片样式的解析问题

mikel阅读(1623)

目前项目中总是ASP.NET MVC总是按照{controller}/{action}的route规则提示xxx.jpg xxx.js解析错误

于是修改了route规则

public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

routes.IgnoreRoute("{*fileType}", new { fileType = @".*\.(gif|js|jpg|png|xml|ico|swf|jpeg|bmp|txt|asp|rar?)" });
}

IgnoreRoute的规定的是按照正则表达式判断符合解析规则的请求,不再使用ASP.NET mvc自定义的路由规则解析,这里定义了文件类型符合要求的都直接访问。

[转载]SQLite数据库管理的相关命令 - Simple Simle - 博客园

mikel阅读(1331)

[转载]SQLite数据库管理的相关命令 – Simple Simle – 博客园.

  1.创建数据库

启动命令行,通过输入如下命令打开Shell模式的CLP:

SQLite3 test.db

虽然我们提供了数据库名称,但如果该数据库不存在,SQLite实际上就未创建该数据库,直到在数据库内部创建一些内容时,SQLite才创建该数据库。

2.创建数据表

sqlite> create table Member(id integer primary key, name text, age integer,addr text);

注:id为主键,该列默认具备自动增长的属性。

3.插入数据

sqlite> insert into Member values(0,’wwl’,21,’上海’);//id=0的列必须不存在,否则会出错

或者sqlite> insert into Member(name,age,addr) values(‘wwl’,21,’上海’);

3.查询数据

sqlite>.mode column

sqlite>.headers on

sqlite> select * from Member;

注:select语句前面的两个命令(.headers和.mode)用于改善显示格式,可以不要。

4.创建视图和索引

sqlite> create view schema as select * from Member;

sqlite> create index Member_Idx on Member(id)

5.导出数据

使用.dump命令可以将数据库对象导出成SQL格式。不带任何参数时,.dump将整个数据库导出为数据库定义语言(DDL)和数据库操作语 言(DML)命令,适合重新创建数据库对象和其中的数据。如果提供了参数,Shell将参数解析作为表名或视图,导出任何匹配给定参数的表或视图,那些不 匹配的将被忽略。

默认情况下.dump 命令的输出定向到屏幕。如:.dump

如果要将输出重定向到文件,请使用.dump[filename]命令,此命令将所有的输出重定向到指定的文件中。若要恢复到屏幕的输出,只需要执行.output stdout命令就OK了。

sqlite>.output file.sql

sqlite>.dump

sqlite>.output stdout

注:如果file.sql不存在,将在当前工作目录中创建该文件。如果文件存在,它将被覆盖。

6.导入数据

有两种方法可以导入数据,用哪种方法取决于要导入的文件格式。如果文件由SQL语句构成,可以使用.read命令导入文件中包含的命令。如果文 件中包含由逗号或其他分隔符分割的值(comma-swparated values,CSV)组成,可使用.import[file][table]命令,此命令将解析指定的文件并尝试将数据插入到指定的表中。

.read命令用来导入.dump命令创建的文件。如果使用前面作为备份文件所导出的file.sql,需要先移除已经存在的数据库对象,然后用下面的方法重新导入:

sqlite>drop table Member;

sqlite>drop view schema;

sqlite>.read file.sql

7.备份数据库

有两种方式可以完成数据库的备份,具体使用哪一种取决于你希望的备份类型。SQL转储许是移植性最好的备份。

生成转储的标准方式是使用CLP.dump命令:sqlite3 test.db .dump >test.sql

在Shell中,可以将输出重定向到外部文件,执行命令,恢复到屏幕输出,如:

sqlite>.output file.sql

sqlite>.dump

sqlite>.output stdout

sqlite>.exit

同样,容易将SQL转储作为CLP的输入流实现数据库导入:

sqlite3 test.db <test.sql

备份二进制数据库知识比复制文件稍多做一点工作。备份之前需要清理数据库,这样可以释放一些已删除对象不再使用的空间。这数据库文件就会变小,因此二进制的副本也会较小:

sqlite3 test.db vacuum

cp test.db test.Backup

8.其它命令

sqlite>select last_insert_rowid();  //获得最后插入的自动增长量值

sqlite>.tabes            //返回所有的表和视图

sqlite>.indices Member       //查看一个表的索引

sqlite>.schema Member       //得到一个表或视图的定义(DDL)语句,如果没有提供表名,则返回所有数据库对象(table,view,index,triger)的定义语句

[转载]Javascript用正则表达式替换字符串中的图片地址(img src) - dudu - 博客园

mikel阅读(976)

[转载][Javascript]用正则表达式替换字符串中的图片地址(img src) – dudu – 博客园.

今天开发中遇到一个问题:如何替换一段HTML字符串中包含的所有img标签的src值?

开始想到的解决方法是:

content.replace(/<img [^>]*src=['"]([^'"]+)[^>]*>/gi, function (match) { console.log(match); });

输出结果是:

<img src="http://static.cnblogs.com/images/logo_small.gif" alt="" width="142" height="55" />

得到的是整个img标签,但我期望得到的是src中的网址,这样只需在function(match)中返回新地址就行了。

于是,卡在这里了。。。

后来,通过Google搜索关键字“JavaScript replace callback”,在stackoverflow中找到了“replace callback function with matches”,才知道function(match)还有其他参数(详见developer.mozilla.org)。

然后,改为下面的代码,问题就解决了。

content.replace(/<img [^>]*src=['"]([^'"]+)[^>]*>/gi, function (match, capture) { console.log(capture); });

输出结果:

http://static.cnblogs.com/images/logo_small.gif

搞定!