在做自定义控件的时候,通过TypeArray获取控件的margin或padding值的时候,出现了getDimension、 getDimensionPixelOffset 、getDimensionPixelSize三种方法获取,这三者都有什么区别,看源码
源码分析
getDimension方法源码
public float getDimension(@StyleableRes int index, float defValue) {
if (mRecycled) {
throw new RuntimeException("Cannot make calls to a recycled instance!");
}
//不用管,因为三个方法都有同样的代码
final int attrIndex = index;
index *= AssetManager.STYLE_NUM_ENTRIES;
final int[] data = mData;
final int type = data[index+AssetManager.STYLE_TYPE];
if (type == TypedValue.TYPE_NULL) {//为空时,走默认值
return defValue;
} else if (type == TypedValue.TYPE_DIMENSION) {//自己指定了值走此方法
return TypedValue.complexToDimension(
data[index + AssetManager.STYLE_DATA], mMetrics);
} else if (type == TypedValue.TYPE_ATTRIBUTE) {//走异常
final TypedValue value = mValue;
getValueAt(index, value);
throw new UnsupportedOperationException(
"Failed to resolve attribute at index " + attrIndex + ": " + value);
}
//走异常
throw new UnsupportedOperationException("Can't convert value at index " + attrIndex
+ " to dimension: type=0x" + Integer.toHexString(type));
}
getDimensionPixelOffset源码
public int getDimensionPixelOffset(@StyleableRes int index, int defValue) {
if (mRecycled) {
throw new RuntimeException("Cannot make calls to a recycled instance!");
}
//不用管,因为三个方法都有同样的代码
final int attrIndex = index;
index *= AssetManager.STYLE_NUM_ENTRIES;
final int[] data = mData;
final int type = data[index+AssetManager.STYLE_TYPE];
if (type == TypedValue.TYPE_NULL) {//为空时,走默认值
return defValue;
} else if (type == TypedValue.TYPE_DIMENSION) {//自己指定了值走此方法
return TypedValue.complexToDimensionPixelOffset(
data[index + AssetManager.STYLE_DATA], mMetrics);
} else if (type == TypedValue.TYPE_ATTRIBUTE) {//走异常
final TypedValue value = mValue;
getValueAt(index, value);
throw new UnsupportedOperationException(
"Failed to resolve attribute at index " + attrIndex + ": " + value);
}
throw new UnsupportedOperationException("Can't convert value at index " + attrIndex
+ " to dimension: type=0x" + Integer.toHexString(type));
}
getDimensionPixelSize源码
public int getDimensionPixelSize(@StyleableRes int index, int defValue) {
if (mRecycled) {
throw new RuntimeException("Cannot make calls to a recycled instance!");
}
//不用管,因为三个方法都有同样的代码
final int attrIndex = index;
index *= AssetManager.STYLE_NUM_ENTRIES;
final int[] data = mData;
final int type = data[index+AssetManager.STYLE_TYPE];
if (type == TypedValue.TYPE_NULL) {//为空时,走默认值
return defValue;
} else if (type == TypedValue.TYPE_DIMENSION) {//自己指定了值走此方法
return TypedValue.complexToDimensionPixelSize(
data[index+AssetManager.STYLE_DATA], mMetrics);
} else if (type == TypedValue.TYPE_ATTRIBUTE) {
final TypedValue value = mValue;
getValueAt(index, value);
throw new UnsupportedOperationException(
"Failed to resolve attribute at index " + attrIndex + ": " + value);
}
throw new UnsupportedOperationException("Can't convert value at index " + attrIndex
+ " to dimension: type=0x" + Integer.toHexString(type));
}
当我们指定了具体值的情况下,会给我们返回
getDimension返回float值
return TypedValue.complexToDimension(
data[index + AssetManager.STYLE_DATA], mMetrics);
getDimensionPixelOffset返回int值
return TypedValue.complexToDimensionPixelOffset(
data[index + AssetManager.STYLE_DATA], mMetrics);
getDimensionPixelSize返回int值
return TypedValue.complexToDimensionPixelSize(
data[index+AssetManager.STYLE_DATA], mMetrics);
getDimension返回是float,getDimensionPixelOffset、getDimensionPixelSize返回int
TypedValue调用的方法传递的参数都是相同的,再跟进去看下
getDimension的方法
public static float complexToDimension(int data, DisplayMetrics metrics)
{
return applyDimension(
//转换成px、dp、px单位对应得值
(data>>COMPLEX_UNIT_SHIFT)&COMPLEX_UNIT_MASK,
complexToFloat(data),
//用于获取屏幕密度
metrics);
}
getDimensionPixelOffset方法
public static int complexToDimensionPixelOffset(int data,
DisplayMetrics metrics)
{
//返回和getDimension的一样,只是getDimension返回的float值,这里强转成int值
return (int)applyDimension(
(data>>COMPLEX_UNIT_SHIFT)&COMPLEX_UNIT_MASK,
complexToFloat(data),
metrics);
}
getDimensionPixelSize方法
public static int complexToDimensionPixelSize(int data,
DisplayMetrics metrics)
{
//在getDimension、getDimensionPixelOffset方法里一样,都有此方法,只不过这里是单独写了,便于四舍五入
final float value = complexToFloat(data);
//和前两个都一样
final float f = applyDimension(
(data>>COMPLEX_UNIT_SHIFT)&COMPLEX_UNIT_MASK,
value,
metrics);
//这里做了四舍五入
final int res = (int) ((f >= 0) ? (f + 0.5f) : (f - 0.5f));
if (res != 0) return res;
//单独把complexToFloat方法抽离出来,目的就是当res是0时用
if (value == 0) return 0;
if (value > 0) return 1;
return -1;
}
applyDimension方法
public static float applyDimension(int unit, float value,
DisplayMetrics metrics)
{
switch (unit) {
case COMPLEX_UNIT_PX:
return value;
case COMPLEX_UNIT_DIP:
return value * metrics.density;
case COMPLEX_UNIT_SP:
return value * metrics.scaledDensity;
case COMPLEX_UNIT_PT:
return value * metrics.xdpi * (1.0f/72);
case COMPLEX_UNIT_IN:
return value * metrics.xdpi;
case COMPLEX_UNIT_MM:
return value * metrics.xdpi * (1.0f/25.4f);
}
return 0;
}
当给定值是px,则直接返回给定的值
当给定值是dp,则需乘以屏幕密度再返回
当给定值是sp,则需乘以缩放因子再返回
注意:一般情况下(不修改系统字体大小),metrics.density和metrics.scaledDensity是一个值。
测试
在values->attrs.xml下定义
<declare-styleable name="CustomView">
<attr name="layout_marginTop" format="dimension"/>
<attr name="layout_marginRight" format="dimension"/>
<attr name="layout_marginBottom" format="dimension"/>
</declare-styleable>
在布局中定义
<com.cn.liuyz.customviewdemo.CustomView
android:id="@+id/cv"
android:layout_width="match_parent"
android:layout_height="200dp"
custom:layout_marginTop="8.5dp"
custom:layout_marginRight="8.5px"
custom:layout_marginBottom="8.5sp"/>
在自定义View中定义
private void initView(Context context, AttributeSet attrs) {
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CustomView, 0, 0);
//分别获取getDimension、getDimensionPixelOffset、getDimensionPixelSize值
float top1 = typedArray.getDimension(R.styleable.CustomView_layout_marginTop, 0f);//dp
int top2 = typedArray.getDimensionPixelOffset(R.styleable.CustomView_layout_marginTop, 0);//dp
int top3 = typedArray.getDimensionPixelSize(R.styleable.CustomView_layout_marginTop, 0);//dp
float right1 = typedArray.getDimension(R.styleable.CustomView_layout_marginRight, 0f);//px
int right2 = typedArray.getDimensionPixelOffset(R.styleable.CustomView_layout_marginRight, 0);//px
int right3 = typedArray.getDimensionPixelSize(R.styleable.CustomView_layout_marginRight, 0);//px
float bottom1 = typedArray.getDimension(R.styleable.CustomView_layout_marginBottom, 0f);//sp
int bottom2 = typedArray.getDimensionPixelOffset(R.styleable.CustomView_layout_marginBottom, 0);//sp
int bottom3 = typedArray.getDimensionPixelSize(R.styleable.CustomView_layout_marginBottom, 0);//sp
typedArray.recycle();
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
float density = metrics.density;
float scaledDensity = metrics.scaledDensity;
Log.d("liuyz:", "density=" + density + ", scaledDensity=" + scaledDensity);
Log.d("liuyz:top", "getDimension= "+top1+", getDimensionPixelOffset="+top2+",getDimensionPixelSize="+top3);
Log.d("liuyz:right", "getDimension= "+right1+", getDimensionPixelOffset="+right2+",getDimensionPixelSize="+right3);
Log.d("liuyz:bottom", "getDimension= "+bottom1+", getDimensionPixelOffset="+bottom2+",getDimensionPixelSize="+bottom3);
}
在布局中传入marginTop=8.5dp、marginRight=8.5px、marginBottom=8.5sp 打印结果如下:
com.cn.liuyz.customviewdemo D/liuyz:: density=2.75, scaledDensity=2.75
com.cn.liuyz.customviewdemo D/liuyz:top: getDimension= 23.375, getDimensionPixelOffset=23,getDimensionPixelSize=23
com.cn.liuyz.customviewdemo D/liuyz:right: getDimension= 8.5, getDimensionPixelOffset=8,getDimensionPixelSize=9
com.cn.liuyz.customviewdemo D/liuyz:bottom: getDimension= 23.375, getDimensionPixelOffset=23,getDimensionPixelSize=23
由打印结果得知
- top值给定的是dp,getDimension得到是float值,getDimensionPixelOffset是把float强制成int值,getDimensionPixelSize是四舍五入的float值
- right给定的px,安装top值得出的规则,getDimension= 8.5,getDimensionPixelOffset强转成int值是8,getDimensionPixelSize四舍五入值为9
总结
不同点
- getDimension 返回float值
- getDimensionPixelOffset 将float强转为int值返回
- getDimensionPixelSize 将float值四舍五入成int值返回
相同点
- 当给定值单位为px,给定值直接返回
- 当给定值单位为dp或px,给定值乘以屏幕密度后返回