Html的适配
无论是iOS还是Android,利用webview为基础,html5+css3来做app已经十分广泛.而优点也十分明显(之前一片文章中提到过)。
个人认为,这种app开发中存在2个大问题
1. js执行效率及内存泄漏问题(问题比较大,需要逐步探索优化)
2. css适配问题
这里主要记录下自己实践过的css适配问题,以备以后调研及开发使用
css适配:
1. 屏幕像素值适配(不常用,需要配合密度来适配)
这种情况可在请求页面时代入参数,分辨率及密度值,由服务器端决定吐不同css,可以解决全部适配问题
最近在android适配中发现新问题,
三星 N7000(2.3.6和4.0系统显示也不一致;4.0会把小css显示大,2.3.6会显示比4.0小),2.3.6是刷入的系统
2. 实际页面像素值适配(常用)
第二种在应用时也有不同方式
a. 根据document.width,加载不同css
b. meta,根据不同device-width与device-height加载不同css
方式a,比较好用,可以解决全部适配问题,但是需要加入脚本,实时判断;css加载慢时会导致屏幕闪动效果明显
方式b,android会出现第一次页面加载时css加载不对的情况,至今没找到解决办法,各位谁有解决方法可以指教
css文件:
css文件可区别编写
ios:2套;一套小分辨率的,主要适用iphone3GS之前的和ipod touch;大分辨率主要应用在iphone4之后的及ipad上。大小2套之间总体差距为1倍,但细节会有调整。
重要问题:ios webview有一个属性会导致横竖屏旋转时缩放页面,此属性需要关闭(具体怎么做去问ios开发人员);如果此属性不关闭,需要css中适配(横屏orientation:landscape ,竖屏orientation:portrait),工作量会成倍增长。
android:3套;分别适配屏幕宽和高<500,宽和高都>720及其他
另,最好能够保持sdk的方向,如果可以横竖转换,页面中不要有拖拽相关的组件。
附分辨率及代表机型:
320*480
代表机型 IPHONE1,touch
360*640
代表机型 诺基亚5230
480*640
代表机型 多普达钻石
480*800
代表机型 三星I9000
640*960
代表机型 IPHONE4
540*960
代表机型 HTC G14
768*1024
代表机型 ipad2
720*1280
代表机型 三星I9250
800*1280
但是js获取的宽高是400×640
代表机型 三星 N7000
1536*2048
但是js获取的宽高是768×1024
代表机型 ipad3
1136×640
但是js获取的宽高是320×568
iphone5,touch5
、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、
Android/IOS 中向webview 注入一整个javascript文件的方法
我们都知道Android中向webview注入js代码可以通过webview.loadUrl("javascript:xxx")来实现,然后就会执行javascript后面的代码。但是当需要注入一整个js文件的时候,貌似就有点麻烦了。
不过理清以下思路,方法其实也很简单,如下:
我们通过在webview的onPageFinished方法中执行js代码注入:
第一种:
当webview加载完之后,读取整个js文件中的内容,然后将整个文件内容以字符串的形式,通过webview.loadUrl("javascript:fileContentString")注入
[mw_shl_code=java,true]
URL url = new URL("http://www.123.456/789.js");
in = url.openStream();
byte buff[] = new byte[1024];
ByteArrayOutputStream fromFile = new ByteArrayOutputStream();
FileOutputStream out = null;
do {
int numread = in.read(buff);
if (numread <= 0) {
break;
}
fromFile.write(buff, 0, numread);
} while (true);
String wholeJS = fromFile.toString();[/mw_shl_code]
[mw_shl_code=java,true]
@Override
public void onPageFinished(WebView view, String url)
{
super.onPageFinished(view, url);
webview.loadUrl("javascript:" + wholeJS);
}[/mw_shl_code]
第二种:
页面加载完之后,直接向webview对应的html中加入<script>便签,并包含要注入的js的Url地址,如下:
[mw_shl_code=java,true]
String js = "var newscript = document.createElement(\"script\");";
js += "newscript.src=\"http://www.123.456/789.js\";";
js += "document.body.appendChild(newscript);";[/mw_shl_code]
[mw_shl_code=java,true]
@Override
public void onPageFinished(WebView view, String url)
{
super.onPageFinished(view, url);
webview.loadUrl("javascript:" + js);
}[/mw_shl_code]
后记:上面两种方式中,第二种方法更加简单方便一点。不过第二种方法也有问题,当你注入完JS之后你想要立即调用其中的方法,第一种方法没问题可以调用到。但是第二种方法中,你要确保注入的<script>便签对应的js文件加载完才可调用成功。
解决:在第二种方法中为加入script标签添加onload事件,确保该script已加载完成。代码可更改如下:
[mw_shl_code=java,true]
String js = "var newscript = document.createElement(\"script\");";
js += "newscript.src=\"http://www.123.456/789.js\";";
js += "newscript.onload=function(){xxx();};"; //xxx()代表js中某方法
js += "document.body.appendChild(newscript);";[/mw_shl_code]
IOS中也一样,按照同样的思路然后在-(void)webViewDidFinishLoad:(UIWebView *)webView 中使用[webView stringByEvaluatingJavaScriptFromString:@"xxx"];即可 。
、、、、、、、、、、、、、、、、、、、、、、、、、、
JavaScript是前端开发的主要语言,我们可以通过编写JavaScript程序来判断浏览器的类型及版本。JavaScript判断浏览器类型一般有两种办法,一种是根据各种浏览器独有的属性来分辨,另一种是通过分析浏览器的userAgent属性来判断的。在许多情况下,值判断出浏览器类型之后,还需判断浏览器版本才能处理兼容性问题,而判断浏览器的版本一般只能通过分析浏览器的userAgent才能知道。
浏览器类型
⑴浏览器特有属性
⑵根据userAgent
浏览器版本
⑴根据userAgent
对于手机浏览器判断
1.如何判断是否为移动终端 利用正则match,
匹配navigator.userAgent是否含有字符串AppleWebKit*****Mobile
安卓qq浏览器HD版 只有AppleWebKit
2手机语言版本的判断
使用navigator.browserLanguage 便可得出windows phone语言版本,
当然可恶的小小手机语言版本也有兼容性的差异,兼容Mozilla,以及AppleWebKit内核的浏览器访问其语言版本,它会列出 navigator.language
CODE:
<script type="text/javascript">
var browser={
versions:function(){
var u = navigator.userAgent, app = navigator.appVersion;
return { //移动终端浏览器版本信息
trident: u.indexOf('Trident') > -1, //IE内核
presto: u.indexOf('Presto') > -1, //opera内核
webKit: u.indexOf('AppleWebKit') > -1, //苹果、谷歌内核
gecko: u.indexOf('Gecko') > -1 && u.indexOf('KHTML') == -1, //火狐内核
mobile: !!u.match(/AppleWebKit.*Mobile.*/), //是否为移动终端
ios: !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/), //ios终端
android: u.indexOf('Android') > -1 || u.indexOf('Linux') > -1, //android终端或uc浏览器
iPhone: u.indexOf('iPhone') > -1 , //是否为iPhone或者QQHD浏览器
iPad: u.indexOf('iPad') > -1, //是否iPad
webApp: u.indexOf('Safari') == -1 //是否web应该程序,没有头部与底部
};
}(),
language:(navigator.browserLanguage || navigator.language).toLowerCase()
}
document.writeln("语言版本: "+browser.language);
document.writeln(" 是否为移动终端: "+browser.versions.mobile);
document.writeln(" ios终端: "+browser.versions.ios);
document.writeln(" android终端: "+browser.versions.android);
document.writeln(" 是否为iPhone: "+browser.versions.iPhone);
document.writeln(" 是否iPad: "+browser.versions.iPad);
document.writeln(navigator.userAgent);
</script>
比较特别的地方
UC浏览器没有安卓报头,只返回:linux ,这里粗略的根据linux来判断是安卓(前提必须满足是移动终端,UC这点是满足的)
安卓QQ浏览器HD版检测的结果是:mac, Safari
。。。。。。。。。。。。。。。。。。。。。。
如果你在为Android开发Web应用或者在为移动设备重新设计一个Web应用,你需要仔细考虑在不同设备上你的页面看起来是怎样的。因为Android设备有不同款型,因此你需要考虑影响你的页面在Android设备上展示的一些因素。
注意: 该文档中考虑的特性只被Android 2.0 以及更高版本上的Android Browser application(由默认Android平台提供的)和WebView(用以展现web页面的框架工具集)支持。在Android上运行的第三方浏览器可能并不支持这些用来控制viewport和分辨率的特性。
当为Android设备设计页面时,有两个需要考虑的基本因素:
Viewport的大小以及web page的规模 |
当Android Browser加载一个页面的时候,默认是以”overview mode”加载,以提供一个放大的页面。你可以通过定义viewport的默认尺寸或者是viewport的初始规模来改写这一行为。你同样可以控制用户放大或缩小页面的程度。用户也可以在浏览器设置中屏蔽overview mode,这样的情况下你就不应该假设你的页面是以overview mode加载的。相反,你应该为你的页面定制合适的viewport大小和规模。
然而,当你的页面在WebView中展现的时候,页面是以最大化形式加载的(而不是”overview mode”)。也就是说,它是以页面默认大小展示的,而不是放大以后的页面(即使用户屏蔽了overview mode,页面也是如此展示)。
设备屏幕的分辨率
Android设备的屏幕分辨率会影响web页面展现的分辨率和展现大小。(有三种屏幕分辨率:低、中、高。)Android浏览器和WebView通过缩放页面来适应不同屏幕分辨率,这样所有的设备都是以默认大小即中分辨率的大小来展示web页面的。如果在你的web页面中,图像是很重要的一部分,那么你就需要密切关注在不同分辨率下发生的缩放,因为图像缩放可能会带来模糊以及像素化的问题。
为了在所有分辨率下都能提供最好的视觉效果,你需要通过提供你的页面的目标分辨率的viewport元数据来控制缩放,并通过使用CSS或者Javascript来为不同分辨率提供不同图像。
这篇文档剩下的部分讲述了你该如何考虑这些影响并为不同类型的屏幕提供一个好的设计。
Viewport是指用以展现你的页面的区域。尽管viewport的可见区域和屏幕大小是匹配的,但是它有着自己的尺寸(dimensions),这一尺寸决定了页面上可见的像素点。也就是说,一个web页面在扩张到整个屏幕之前占用的像素数据是由viewport的尺寸(dimensions)来定义的,而不是设备屏幕的尺寸。例如,尽管一个设备的屏幕宽480像素,但是viewport宽800像素,那么这个web页面需要在800像素宽的屏幕上才能完全展现。
你可以在HTML中使用 <meta> tag(这个tag必须包含在文档的<head>中)来为你的页面定义viewport的性质。你可以在 <meta> tag的content 属性中,定义多个viewport性质。例如,你可以定义viewport的高和宽,页面的最初大小,以及目标屏幕分辨率。content 属性中的每个viewport性质必须以逗号相隔。
例如,下面的HTML片段指定了viewport宽度必须严格和屏幕宽度匹配,并禁用了放大功能:
- <head>
- <title>Example</title>
- <meta name=”viewport” content=”width=device-width, user-scalable=no” />
- </head>
这是个定义两个viewport性质的例子。下面的语法显示了所有受支持的viewport性质及各个性质接受的数据基本属性:
- <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]
- " />
下面的部分讨论了如何使用这些viewport性质以及可以赋给这些性质的值到底是怎样。
Figure 1. 一个web页面,其中有320像素宽的图像,在Android Browser中呈现,没有设置viewport元数据(开启了"overview mode",viewport默认为800像素宽)
Viewport的height 和 width性质让你可以指定viewport大小(即页面在扩张到屏幕之前可见的大小)。
跟上面提到的一样,Android Browser默认以”overview mode”加载页面(除非这一模式被用户禁用),将最小的viewport宽度定义为800像素。因此,如果你的web页面定义的宽度为320像素的话,那么你的页面看起来就比屏幕小(除非你的物理屏幕是320像素宽的,因为viewport模拟出了一个800像素宽的可绘图区域),就如figure 1中所示。为避免这一影响,你需要显式定义viewport的width与你设计的web页面的宽度匹配。
例如:如果你的web页面是设计为320像素宽的,那么你就需要为viewport的width指定相同大小如下:
- <meta name="viewport" content="width=320" />
在这个例子中,你的web页面和屏幕宽度大小刚好是匹配的,因此页面宽度和viewport的width是一致的。
注意: 大于10,000的width值将被忽略,小于或等于320的值将会使得width的值等于设备的宽度(下面将会讨论)。大于10,000或者小于200的height值将被忽略。
为了展现这个性质是如何影响页面大小的,figure 2展示了一个web页面,在这里,web页面中包含一个320像素宽的图像,但是viewport的width设置为400.
注意:如果你设置viewport的width与页面宽度匹配而设备屏幕大小和这些尺寸不匹配的话,web页面将仍然占满整个屏幕,即使设个设备屏幕是低分辨率或者高分辨率的,因为Android Browser和WebView 默认将web页面缩放到中等分辨率屏幕大小(如同你在figure 2中看到的一样,图中对比了高分辨率和中等分辨率设备)屏幕分辨率在Defining the viewport target density中有更多讨论。
除了将viewport尺寸定义为精确的数值以外,你还可以将其设置为永远和设备屏幕尺寸匹配,即将viewport的height和width分别赋值为device-height 和device-width。这在你开发一个有着活动大小的web应用的时候是非常合适的,这能使这个web应用的页面好像是固定的(和每个屏幕宽度都精确匹配)。例如:
- <meta name="viewport" content="width=device-width" />
设置viewport尺寸永远和屏幕尺寸匹配结果如figure 3所示。需要注意的是,这样的设置会导致图片缩放到与屏幕匹配,即使当前设备和target density(默认情况下是中等分辨率)并不匹配。因此,figure 3中的高分辨率设备的图片在中等分辨率的设备上放大了,以便和屏幕宽度匹配。
注意:如果你希望device-width 和 device-height和设备的物理屏幕的像素匹配,而不是通过缩放web页面来和target density匹配,那么你就必须包含一个target-densitydpi性质并将其赋值为 device-dpi。这在Defining the viewport density中将会有更多讨论。否则,只使用device-width 和 device-height来定义viewport大小的话会让你的页面自动适应每个屏幕,但是你的图片也会缩放以便适应不容屏幕分辨率。
Viewport规模确定了页面的缩放程度。Viewport性质能让你以下面的方式指定页面缩放程度:
初始缩放(initial-scale)
即页面初始缩放程度。这是一个浮点值,是页面大小的一个乘数。例如,如果你设置初始缩放为“1.0”,那么,web页面在展现的时候就会以target density分辨率的1:1来展现。如果你设置为“2.0”,那么这个页面就会放大为2倍。
默认的初始缩放值是通过计算让页面和viewport大小匹配。因为默认viewport宽度是800像素,如果设备屏幕分辨率宽度小于800,那么初始缩放值在默认情况下是小于1.0的,以便和屏幕上的800像素宽的页面匹配。
最小缩放(minimum-scale)
即允许的最小缩放程度。这是一个浮点值,用以指出页面大小与屏幕大小相比的最小乘数。例如,如果你将这个值设置为“1.0”,那么这个页面将不能缩小,因为最小值和 target density为1:1的关系。
最大缩放(maximum-scale)
即允许的最大缩放程度。这也是一个浮点值,用以指出页面大小与屏幕大小相比的最大乘数。例如,如果你将这个值设置为“2.0”,那么这个页面与target size相比,最多能放大2倍。
用户调整缩放(user-scalable)
即用户是否能改变页面缩放程度。如果设置为yes则是允许用户对其进行改变,反之为no。默认值是yes。如果你将其设置为no,那么minimum-scale 和 maximum-scale都将被忽略,因为根本不可能缩放。
所有的缩放值都必须在0.01–10的范围之内。
例如:
- <meta name="viewport" content="initial-scale=1.0" />
这个元数据将初始缩放值定义为和viewport的target density相比为满屏。
一个屏幕像素密度是由屏幕分辨率决定的,通常定义为每英寸点的数量(dpi)。Android支持三种屏幕像素密度:低像素密度,中像素密度,高像素密度。一个低像素密度的屏幕每英寸上的像素点更少,而一个高像素密度的屏幕每英寸上的像素点更多。Android Browser和WebView默认屏幕为中像素密度。
因为默认target density是中像素密度,因此当用户拥有一个低像素或者高像素密度的屏幕时,Android Browser和 WebView会缩放页面,以便它们能在中等像素密度的屏幕以合适的大小展示。更具体来说,Android Browser和 WebView会在高像素密度设备上将页面放大约1.5倍(因为高像素密度设备上的像素点更小),而在低像素密度设备上将页面缩小为约0.75倍(因为低像素密度设备上的像素点更大)。
由于默认缩放,figure 1,2,3展现了同样物理大小的web页面在高像素密度设备和中等像素密度设备上的效果(高像素密度设备上的web页面放大到实际的1.5倍,以便和target density匹配)。这会给图像带来一些问题。比如,尽管一个图像在中等像素密度和高像素密度设备上看起来大小一样,但是高像素密度设备上的图像看起来更为模糊,因为这个图像本来是为320像素宽而设计的,但却被拉到了480像素宽。
你可以通过使用viewport的target-densitydpi性质来改变目标屏幕像素密度。可以赋给它的值如下所列:
例如,为了防止Android Browser和WebView 根据不同屏幕的像素密度对你的页面进行缩放,你可以将viewport的target-densitydpi 设置为 device-dpi。当你这么做了,页面将不会缩放。相反,页面会根据当前屏幕的像素密度进行展示。在这种情形下,你还需要将viewport的width定义为与设备的width匹配,这样你的页面就可以和屏幕相适应。例如:
- <meta name="viewport" content="target-densitydpi=device-dpi, width=device-width" />
Figure 4 展示了使用这些设置的一个web页面——在高像素密度设备上,这个页面看起来小一些了,因为它的物理像素点比中等像素密度设备上的像素点要小,而又没有缩放发生,因此320像素宽的图像在两个界面上都只占用了320像素宽。(如果你想要根据屏幕像素密度来定制你的web页面的话,你就应该如此定义viewport,并使用CSS 或者 JavaScript来为不同像素密度设备提供不同图像。)
Android Browser和WebView支持一个CSS的media特性,让你能为特定像素密度的设备来创建styles——这个media特性就是 -webkit-device-pixel-ratio CSS media feature。你赋给这个特性的值应该是”0.75″, “1″, 或 “1.5″,来分别指出styles是针对低像素密度、中等像素密度和高像素密度的。
例如:你可以为不容像素密度创建样式列表stylesheets如下:
- <link rel="stylesheet" media="screen and (-webkit-device-pixel-ratio: 1.5)" href="hdpi.css" />
- <link rel="stylesheet" media="screen and (-webkit-device-pixel-ratio: 1.0)" href="mdpi.css" />
- <link rel="stylesheet" media="screen and (-webkit-device-pixel-ratio: 0.75)" href="ldpi.css" />
或者,在一个样式表中指定不同的styles:
- #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);
- }
- }
注意:#header默认的style是将图片应用于中等像素密度的设备,以支持Android2.0以下的设备,这些设备是不支持-webkit-device-pixel-ratio的。
根据你设置的viewport性质不同,你要调整的对不同像素密度的styles的风格也应该不同。为了让你的页面能在不同像素密度下都有合适的styles,你需要将viewport的宽度设置为与设备匹配。即:
- <meta name="viewport" content="target-densitydpi=device-dpi, width=device-width" />
通过这种方式,Android Browser和 WebView就不会对你的页面进行缩放,并且viewport的width能与设备的width精确匹配。这一设置效果如figure 4所示。然而,通过使用-webkit-device-pixel-ratio ,你可以应用不同的styles。例如,在figure 5中,展示了一个使用如上viewport设置并使用了一些CSS的页面,在这个CSS中,定义将高分辨率的图像用于高像素密度的屏幕。
Android Browser和 WebView支持一个文档对象模型(DOM)特性,可以让你查询当前设备的像素密度——即DOM的window.devicePixelRatio 特性。这个特性的值指定了当前设备的缩放因子。例如,如果window.devicePixelRatio的值是“1.0”,则这个设备是一个中等像素密度的设备,默认不缩放;如果window.devicePixelRatio的值是“1.5”,则这个设备是一个高像素密度的设备,默认以1.5倍缩放;如果window.devicePixelRatio的值是“0.75”,则这个设备是一个低像素密度的设备,默认以0.75倍缩放。当然,Android Browser 和WebView 是根据页面的target density进行缩放的,和上文讨论的一样,其默认target是中等像素密度,但是你可以修改这个target,调整你的页面在不同屏幕分辨率下的缩放方式。
例如:你可以像下面这样通过Javascript来查询设备像素密度:
- if (window.devicePixelRatio == 1.5)
- {
- alert("This is a high-density screen"); }
- else if (window.devicePixelRatio == 0.75)
- {
- alert("This is a low-density screen");
- }