最近在做深度主题,要实现类似小米那种在主题包中设置dimension值,然后在系统中替换原值的功能。
特地研究了一下Android系统中dimension类型的值的存储方式以及相关的转换算法。
在Android中,我们可以在values文件夹中定义各种资源,其中有一种就是dimension。
dimension是一个包含单位(dp、dip、sp、pt、px、mm、in)的尺寸,可以用于定义视图的宽度、字号等。如下图所示。
<?xml version="1.0" encoding="utf-8"?> <resources> <dimen name="textview_height">25dp</dimen> <dimen name="textview_width">150dp</dimen> <dimen name="ball_radius">30dp</dimen> <dimen name="font_size">16sp</dimen> </resources>
<TextView android:layout_height="@dimen/textview_height" android:layout_width="@dimen/textview_width" android:textSize="@dimen/font_size"/>
而在代码中,我们可以通过getDimension方法获取到资源文件中定义的dimension值。
Resources res = getResources(); float fontSize = res.getDimension(R.dimen.font_size);
从上图可以发现,不论之前在资源文件中定义的dimension是什么单位,在代码中均转换成了float类型的数值。
但两个dimension值,如果数值部分相同,但单位不同,显然转换后的float值应该不同。
为了证明以上结论,我做了一个实验。
首先,定义一组dimension,数值部分相同,单位不同。
<?xml version="1.0" encoding="utf-8"?> <resources> <dimen name="tdp">10dp</dimen> <dimen name="tdip">10dip</dimen> <dimen name="tsp">10sp</dimen> <dimen name="tpt">10pt</dimen> <dimen name="tpx">10px</dimen> <dimen name="tmm">10mm</dimen> <dimen name="tin">10in</dimen> </resources>
然后,在代码中分别使用getDimension与getValue方法获取这些dimension的值。
Resources r = getResources(); TypedValue tv = new TypedValue(); r.getValue(R.dimen.tdp, tv, true); System.out.println("10dp="+r.getDimension(R.dimen.tdp)+" data="+Integer.toBinaryString(tv.data)); r.getValue(R.dimen.tdip, tv, true); System.out.println("10dip="+r.getDimension(R.dimen.tdip)+" data="+Integer.toBinaryString(tv.data)); r.getValue(R.dimen.tsp, tv, true); System.out.println("10sp="+r.getDimension(R.dimen.tsp)+" data="+Integer.toBinaryString(tv.data)); r.getValue(R.dimen.tpt, tv, true); System.out.println("10pt="+r.getDimension(R.dimen.tpt)+" data="+Integer.toBinaryString(tv.data)); r.getValue(R.dimen.tpx, tv, true); System.out.println("10px="+r.getDimension(R.dimen.tpx)+" data="+Integer.toBinaryString(tv.data)); r.getValue(R.dimen.tmm, tv, true); System.out.println("10mm="+r.getDimension(R.dimen.tmm)+" data="+Integer.toBinaryString(tv.data)); r.getValue(R.dimen.tin, tv, true); System.out.println("10in="+r.getDimension(R.dimen.tin)+" data="+Integer.toBinaryString(tv.data));
最后,可以从下图看到输出的结果。
从上图可以看出,不同单位情况下,即使数值相同,转换成的float值也是千差万别。
但从后面的data值中,我们却发现,对于不同的单位,其最高位大部分是相同的,仅仅是后面几位有所区别。我们可以大胆地猜想:“dimension在系统中是以数值+单位的形式存储的。”
分析了Android的Resoureces类的getDimension方法的源码后我们发现,对于不同的dimension,在使用getValue获取到对应的int值之后,会通过TypedValue的complexToDimension方法将其转换为float。
complexToDimension方法主要是调用applyDimension方法,将getValue获取到的int值,拆成单位和数值两部分,然后根据单位的不同,对数值进行处理。
其中, (data>>COMPLEX_UNIT_SHIFT)&COMPLEX_UNIT_MASK的作用是将该int值与上0xf,以获取其最低4位,这4位是单位。而complexToFloat则是使用该int值的最高24位当作数值,4-7位作为radix,进行计算,转成float。
最后,在applyDimension中根据单位的不同,将float乘上不同的系数。如dip/dp需乘上屏幕系数,sp需乘上字号的缩放系数,pt、in、mm等也是根据相应的算法进行换算。(从COMPLEX_UNIT_PX直接返回float可以看出,该方法是将数值转成像素数)
至此,我们之前的猜想大致上是正确的,只是需要加上一个radix部分。即“dimension在系统中是以数值+4位radix+4位单位的形式存储的”。
因此,为了实现将形如“10dip”的dimension转成getValue返回的int值,我们需要进行以下处理(参考com.androi.layoutlib.bridge.impl.ResourceHelper的parseFloatAttribute方法)。
首先,将dimension字符串拆成数值与单位两部分,并将数值转成浮点数。
从上文可以知道,dimension的数值部分在实际存储时,系统只提供了24位存储空间。再考虑到一个dimension是有符号的,可正可负。故最高位表示正负。因此,真正的数值只有23位进行存储。
所以,接下来,对于负数要先转成对应的正数,并乘上223加0.5(四舍五入)后转成long。(即仅保留整数和小数部分各23位)
接着,对转换后的long值进行判断。
若最低23位为0,即小数部分为0,标记radix为TypedValue.COMPLEX_RADIX_23p0,shift为23,即只保留整数部分。
若最高41位为0,即整数部分为0(java中long以8个字节存储),标记radix为TypedValue.COMPLEX_RADIX_0p23,shift为0,即只保留小数部分。
若最高33为0,即整数部分最多只有8位有效,标记radix为TypedValue.COMPLEX_RADIX_8p15,shift为8,即只保留整数部分8位,小数部分15位。
若最高25位为0,即整数部分最多只有16位有效,标记radix为TypedValue.COMPLEX_RADIX_16p7,shift为16,即只保留整数部分16位,小数部分7位。
若以上都不符合,说明小数部分不为0,整数有效部分超过16位,则标记radix为TypedValue.COMPLEX_RADIX_23p0,shift为23,即只保留整数部分。
然后,根据shift将转换后的long值进行右移,取最低24位,转为int。若原值为负数,将右移的数转成负数后再取最低24位。
最后,将最终数值左移8位,最低4-7位或上radix,0-3位或上单位。
上图给出了123113.51213dp的处理流程,按照算法,该dimension值会被转换成31516929存储在系统中。为了验证算法的正确性,笔者做了以下实验。
首先,在资源文件中添加一个值为123113.51213dp的dimension。
<?xml version="1.0" encoding="utf-8"?> <resources> <dimen name="tdp">123113.51213dp</dimen> </resources>
然后,在代码中获取该值。
Resources r = getResources(); TypedValue tv = new TypedValue(); r.getValue(R.dimen.tdp, tv, true); System.out.println("123113.51213dp="+r.getDimension(R.dimen.tdp)+" data="+(tv.data));
最后,查看输出结果,完全符合!
至此,dimension的处理算法分析完毕。
相关推荐
Dimension
通过研究一致互反判断矩阵与模糊互补判断矩阵的转换关系,给出把一致互反矩阵转换成模糊互补矩阵且权向量排序不变的方法,基于这种转换的保序性,提出了一种介于层次分析法与模糊层次分析法之间的新的层次分析方法,...
We give a tutorial overview of several geometric methods for dimension reduction. We divide the methods into projective methods and methods that model the manifold on which the data lies. For ...
Three_dimensiona ThreadConnection,基于ANSYS分析得出的三维分析
A FlowLayout for Android, which allows child views flow to next row when there is no enough space. The spacing between child views can be calculated by the FlowLayout so that the views are evenly ...
书名:《Android编程入门很简单》(清华大学出版社.王勇)。 压缩打包成2部分,这是第1部分。 本书是一本与众不同的Android学习读物,是一本化繁为简,把抽象问题具体化,把复杂问题简单化的书。本书避免出现...
Two Dimension Signal and image processing MIT Textbook 麻省理工教材
Android文字轮播控件 现在的绝大数APP特别是类似淘宝京东等这些大型APP都有文字轮播界面,实现循环轮播多个广告词等功能;这种控件俗称“跑马灯”,而TextBannerView已经实现了可垂直跑、可水平跑的跑马灯了。 效果...
dimension-2400_owner's manual中文版,老电脑的资料
Business Objects Merge Dimension
Data Quality: The Accuracy Dimension is about assessing the quality of corporate data and improving its accuracy using the data profiling method. Corporate data is increasingly important as companies ...
CurveView 轻量级、可高度定制化的折线图 效果演示 显示全部 滚动支持 特点 支持样式定制 使用 adapter 方式集成数据,用法简单,极易理解 支持点上 8 个方向同时添加...dimension ...dimension ...dimension
Matlab 计算曲线的分型维数fractal dimension,FD
PDAL-Dimension.cpp c++ pdal lidar library
利用 Ogata's modified thinning算法模拟Multi_dimension Hawkes process
Ising model (2-dimension) fortran code
修改过去混沌算法中的参数选择问题,使算法变得更准确.
在新建的FORM上使用系统的Dimension display功能
English version, cover some InfoCube modeling best practices like M:N relationship of Master data, one or two dimensions, partitioning
Rather, Imagining the Tenth Dimension is a mind-expanding exercise that could change the way you view this incredible universe in which we live. Read the book whose companion website (tenthdimension...