关于Flutter 大小所使用的单位,官方文档没有给出非常明确的解释,因此一直存在模糊的说法,许多从事安卓开发者直接将之解释为安卓开发所用的单位dp
,我认为这是非常不明智且不准确的说法,这个不准确不在于实质的数值,而在于概念的混淆!这样极容易对初学者造成误导,从事web前端或iOS原生开发的人,并没有dp
的概念,当他们学习Flutter时,必须强行去理解dp的概念,且在iOS或web平台上时也解释为dp
,那就是错误的。
应当如何理解Flutter 的大小单位?
官方文档中有对 devicePixelRatio属性的描述,devicePixelRatio
即每个逻辑像素的设备像素数,其中有一句概括的话
设备像素也被称为物理像素。逻辑像素也被称为与设备无关或与分辨率无关的像素。
也就是说,物理像素px = 逻辑像素 * devicePixelRatio
在另一篇专门写给Android 开发者的文档中 Flutter for Android developers,有如下说明
Flutter follows a simple density-based format like iOS. Assets might be 1.0x, 2.0x, 3.0x, or any other multiplier. Flutter doesn’t have dps but there are logical pixels, which are basically the same as device-independent pixels.
翻译过来,就是:Flutter像iOS一样遵循一个简单的基于密度的格式。Assets 可能是1.0x,2.0x,3.0x,或者其他任何倍数。Flutter没有dps,但有逻辑像素,这与设备独立像素基本相同。
到这里我们大概能明白Flutter官方的意思,Flutter框架希望提供一个新的尺寸单位的概念,称为逻辑像素,然后让大家忘记原生开发中的单位。这是因为Flutter作为一个跨平台的框架,必须抽离出一个新的单位,用以适配不同的平台,如果还去使用原生的单位概念,就会造成混淆或屏幕适配的问题。
结论,在Flutter的语境下,不应该将逻辑像素直接描述为原生开发中的单位概念
Flutter的逻辑像素是如何计算出来的?
关于devicePixelRatio
属性的值,一直没有明确的资料说明,许多人使用比较朴素的办法,直接打印不同平台的值用以发现规律。我认为这不是一个好主意,作为一个专业的程序员,应该从源码中找到答案,实际上devicePixelRatio
值的计算很容易找到对应的源码
Android 平台
Flutter 引擎源码 shell/platform/android/io/flutter/view/FlutterView.java
public FlutterView(Context context, AttributeSet attrs, FlutterNativeView nativeView) {
super(context, attrs);
Activity activity = getActivity(getContext());
if (activity == null) {
throw new IllegalArgumentException("Bad context");
}
if (nativeView == null) {
mNativeView = new FlutterNativeView(activity.getApplicationContext());
} else {
mNativeView = nativeView;
}
dartExecutor = mNativeView.getDartExecutor();
flutterRenderer = new FlutterRenderer(mNativeView.getFlutterJNI());
mIsSoftwareRenderingEnabled = mNativeView.getFlutterJNI().nativeGetIsSoftwareRenderingEnabled();
mMetrics = new ViewportMetrics();
// 通过Java代码获取平台中的density值
mMetrics.devicePixelRatio = context.getResources().getDisplayMetrics().density;
// ...... 省略 ......
}
获取到density
值后,又通过JNI将值传给引擎层的C++代码 源码 shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java
// 通过java的jni本地方法传给C++
private native void nativeSetViewportMetrics( long nativePlatformViewId, float devicePixelRatio, int physicalWidth, int physicalHeight, int physicalPaddingTop, int physicalPaddingRight, int physicalPaddingBottom, int physicalPaddingLeft, int physicalViewInsetTop, int physicalViewInsetRight, int physicalViewInsetBottom, int physicalViewInsetLeft, int systemGestureInsetTop, int systemGestureInsetRight, int systemGestureInsetBottom, int systemGestureInsetLeft);
这里对C++代码就不在追踪,有兴趣可以去看engine/shell/platform/android/platform_view_android_jni_impl.cc
在Flutter中,devicePixelRatio
属性由ui.Window
类提供,我们知道,这个Window正是Flutter Framework连接宿主操作系统的接口。因此,dart代码中获取的devicePixelRatio
属性正是引擎层从原生平台中获取的。
iOS 平台
引擎源码 engine/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm
- (void)viewDidLayoutSubviews {
CGSize viewSize = self.view.bounds.size;
CGFloat scale = [UIScreen mainScreen].scale;
// Purposefully place this not visible.
_scrollView.get().frame = CGRectMake(0.0, 0.0, viewSize.width, 0.0);
_scrollView.get().contentOffset = CGPointMake(kScrollViewContentSize, kScrollViewContentSize);
// First time since creation that the dimensions of its view is known.
bool firstViewBoundsUpdate = !_viewportMetrics.physical_width;
// 在iOS 上,device_pixel_ratio 的值是一个缩放比
_viewportMetrics.device_pixel_ratio = scale;
_viewportMetrics.physical_width = viewSize.width * scale;
_viewportMetrics.physical_height = viewSize.height * scale;
// ....... 省略 .......
}
可以看到,device_pixel_ratio
的值是[UIScreen mainScreen].scale
,关于这个值,苹果的开发者文档有描述 UIScreen 的 scale
该值反映了从默认逻辑坐标空间转换到本界面设备坐标空间所需的比例系数。默认的逻辑坐标空间是用点来衡量的。对于Retina显示器,比例因子可能是3.0或2.0,一个点可以分别用9个或4个像素表示。对于标准分辨率显示器,比例系数为1.0,一个点等于一个像素。
简单说就是
scale == 1
:代表320 x 480 的分辨率(iphone4之前的设备,非Retain屏幕)scale == 2
:代表640 x 960 的分辨率(Retain屏幕)scale == 3
:代表1242 x 2208 的分辨率
Web 平台
引擎源码 engine/lib/web_ui/lib/src/engine/window.dart
@override
double get devicePixelRatio => _debugDevicePixelRatio != null
? _debugDevicePixelRatio
: browserDevicePixelRatio;
/// Returns device pixel ratio returned by browser.
static double get browserDevicePixelRatio {
double ratio = html.window.devicePixelRatio;
// Guard against WebOS returning 0.
return (ratio == null || ratio == 0.0) ? 1.0 : ratio;
}
可以看到,调用的是html.window.devicePixelRatio
,这里的html.window
实际上是Dart语言SDK中的类,描述的是浏览器中的window。关于浏览器中的devicePixelRatio
属性值,可以看Dart 官方文档给出的解释 devicePixelRatio
- IE and Firefox don’t support the property at all. I assume the next versions will implement it.
- Opera desktop on retina devices give 1, while it should be 2. I assume the next Opera version will fix this.
- Opera Mobile 10 does not support it, but 12 implements the property correctly.
- UC always gives 1, but UC is quite confused when it comes to viewport properties.
- Chrome implemented this property only recently on retina devices. Chrome 19 incorrectly returns 1; Chrome 22 correctly returns 2.
- MeeGo WebKit (Nokia N9/N950) does something horrible: it changes the value from 1 to 1.5 when you apply a meta viewport.
视频课程
我们研究透彻了Flutter中的尺寸单位,最现实的意义就是可以透彻的理解屏幕适配,特别是当美术设计师将一副带标注的设计稿丢给我们时,我们如何转换成Flutter中的尺寸来实现UI页面。
具体的屏幕适配知识,请观看博主的公开课视频,从此透彻理解移动开发中的UI适配,再也不怕跨平台适配的所有问题了!
B站链接如下,您觉得有用麻烦三连支持哦
完整的视频课程如下:
公众号:编程之路从0到1
今天的文章Flutter 大小单位详解分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/14368.html