说下自己以前用的地方
代码如下
//attrs.xml下有如下的属性
<attr name="listviewBackgroundTwocolour" format="reference" />
//styles.xml下有这样的属性,可以看到2个主题,对应的背景图片是不一样的
<style name="AppTheme" parent="AppBaseTheme">
<item name="listviewBackgroundTwocolour">@drawable/my_two_color_bg_selector</item>
</style>
<style name="AppTheme2" parent="AppBaseTheme">
<item name="listviewBackgroundTwocolour">@drawable/my_two_color_bg_selector2222222</item>
</style>
那么怎么根据主题不同获取到不同的图片了?如下
至于里边咋把值给TypeValue的,点进去看到最后就是个native方法,所以不研究了。我们只要知道这样可以获取到我们要的就行
TypedValue themedBackground = new TypedValue();
if (getTheme().resolveAttribute(R.attr.listviewBackgroundTwocolour, themedBackground, true))
{
mBackgroundDrawable=getDrawable(themedBackground.resourceId);
}
else
{
mBackgroundDrawable = null;
}
还有这种获取某种类型对应的像素大小
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;
}
今天为啥突然研究这个
是因为随手点进去了RotateAnimation的源码引发的。
平时用这个类,从来没用过这个带AttributeSet参数的构造方法,
TypedArray 的getFloat,getString这些方法都常见啊,可第一次看到这个peekValue,好奇下就点进去看看。
public RotateAnimation(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.obtainStyledAttributes(attrs,
com.android.internal.R.styleable.RotateAnimation);
mFromDegrees = a.getFloat(
com.android.internal.R.styleable.RotateAnimation_fromDegrees, 0.0f);
mToDegrees = a.getFloat(com.android.internal.R.styleable.RotateAnimation_toDegrees, 0.0f);
Description d = Description.parseValue(a.peekValue(
com.android.internal.R.styleable.RotateAnimation_pivotX));
mPivotXType = d.type;
mPivotXValue = d.value;
d = Description.parseValue(a.peekValue(
com.android.internal.R.styleable.RotateAnimation_pivotY));
mPivotYType = d.type;
mPivotYValue = d.value;
a.recycle();
initializePivotPoint();
}
首先去源码里拉下RotateAnimation的style
可以看到pivot有两种类型,float和fraction,小数和分数
<!-- Drawable used to rotate another drawable. -->
<declare-styleable name="RotateDrawable">
<attr name="visible" />
<attr name="fromDegrees" format="float" />
<attr name="toDegrees" format="float" />
<attr name="pivotX" format="float|fraction" />
<attr name="pivotY" format="float|fraction" />
<attr name="drawable" />
</declare-styleable>
//自己写几次就知道了
com.android.internal.R.styleable.RotateAnimation_fromDegrees 这个其实就是个1,
也就是上边定义的attr的索引位置,从0开始的,
com.android.internal.R.styleable.RotateAnimation_toDegrees 就是2
中间插点例子说下R.styleable.xxx 所对应的index咋算的
比如我们定义了一个
<declare-styleable name="SwipeCaptchaView">
<attr name="captchaWidth" format="dimension" />
<attr name="captchaHeight" format="dimension" />
<attr name="matchDeviation" format="dimension" />
</declare-styleable>
然后打开R文件就能看到下边的id了,需要补充说下,数字肯定是从0开始的,但不一定第一个就是0,比如上边的captchaWidth 不一定是0,为啥这样说了?比如先定义captchaHeight,这时候其实它就是0,完事你新弄一个captchaWidth放到它上边它也是1,索引应该是添加的attr的顺序,
public static final int[] SwipeCaptchaView = {
0x7f01006e, 0x7f01006f, 0x7f010070
};
public static final int SwipeCaptchaView_captchaHeight = 1;
public static final int SwipeCaptchaView_captchaWidth = 0;
public static final int SwipeCaptchaView_matchDeviation = 2;
继续往下看
public TypedValue peekValue(int index) {
//从上边style可以看到这个index就是个3或者4,后边分析都按照3来说,就是那个pivotX
if (mRecycled) {
throw new RuntimeException("Cannot make calls to a recycled instance!");
}
final TypedValue value = mValue;
if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) {
return value;
}
return null;
}
/*package*/ static final int STYLE_NUM_ENTRIES = 6;
/*package*/ static final int STYLE_TYPE = 0;
/*package*/ static final int STYLE_DATA = 1;
/*package*/ static final int STYLE_ASSET_COOKIE = 2;
/*package*/ static final int STYLE_RESOURCE_ID = 3;
/*package*/ static final int STYLE_CHANGING_CONFIGURATIONS = 4;
/*package*/ static final int STYLE_DENSITY = 5;
public static final int TYPE_NULL = 0x00;
private boolean getValueAt(int index, TypedValue outValue) {
//这里的index是乘以6以后的值
final int[] data = mData;//这里看到它用的是TypeArray里的一个变量赋值的,看来还得回去头看TypeArray的数据哪里来的
final int type = data[index+AssetManager.STYLE_TYPE];
if (type == TypedValue.TYPE_NULL) {
return false;
}
outValue.type = type;
outValue.data = data[index+AssetManager.STYLE_DATA];
outValue.assetCookie = data[index+AssetManager.STYLE_ASSET_COOKIE];
outValue.resourceId = data[index+AssetManager.STYLE_RESOURCE_ID];
outValue.changingConfigurations = data[index+AssetManager.STYLE_CHANGING_CONFIGURATIONS];
outValue.density = data[index+AssetManager.STYLE_DENSITY];
outValue.string = (type == TypedValue.TYPE_STRING) ? loadStringValueAt(index) : null;
return true;
}
下边分析下为啥上边会乘以6?
本来索引是0,1,2,3,4乘以6以后就成了0,6,12,18,24了
上边代码,有没有发现STYLE_NUM_ENTRIES 这个后边还有6个变量,从STYLE_TYPE =0到STYLE_DENSITY =5
STYLE_NUM_ENTRIES=6就是这样来的,加入后边有7种TYPE,这个估计就成了7了。
还不明白,继续说。
上图的data数组长度就是R.styleable.RotateAnimation这个里边attr的个数乘以6.
而文章下面会告诉你系统最终会把我们布局文件里设置的属性解析到这个数组里的。
每一个attr解析出来的数据都有6样,就是上边的style_type,style_data,style_cookie,style_resource_id,style_configurations,style_density 这6种。
也就是每6位存储一个attr相关的数据。
这就是为啥上边的index本来是0,1,2,3之类的,为啥经过getValueAt方法的时候把index乘以6
因为getValueAt方法里就是从data数组里6位6位的取数据的。
data数组的0到5索引存的是index为0的数据
data数组的6到11索引存的是index为1的数据【把本来的index乘以6,刚好从6开始】
data数组的12到17索引存的是index为2的数据把本来的index乘以6,刚好从12开始】
我们知道typeArray是这样来的
TypedArray a = context.obtainStyledAttributes(attrs,
com.android.internal.R.styleable.RotateAnimation);
继续点进去走到这里
public TypedArray obtainStyledAttributes(AttributeSet set,
@StyleableRes int[] attrs, @AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
final int len = attrs.length;
final TypedArray array = TypedArray.obtain(Resources.this, len);//我们看下这个
// XXX note that for now we only work with compiled XML files.
// To support generic XML files we will need to manually parse
// out the attributes from the XML file (applying type information
// contained in the resources and such).
//下边这里又是native方法,中断不看了。
final XmlBlock.Parser parser = (XmlBlock.Parser)set;
AssetManager.applyStyle(mTheme, defStyleAttr, defStyleRes,
parser != null ? parser.mParseState : 0, attrs, array.mData, array.mIndices);
上边返回的array就是TypedArray.obtain(Resources.this, len),只不过通过native方法给赋值了而已
static TypedArray obtain(Resources res, int len) {
final TypedArray attrs = res.mTypedArrayPool.acquire();
if (attrs != null) {
//省略,感觉应该不会走这里,因为看了上边pool也没有赋值过
return attrs;
}
return new TypedArray(res,
new int[len*AssetManager.STYLE_NUM_ENTRIES],//6乘以6,上边解释过为啥乘以6拉。
new int[1+len], len);
}
typeValue的构造方法看下
/*package*/ TypedArray(Resources resources, int[] data, int[] indices, int len) {
mResources = resources;
mMetrics = mResources.mMetrics;
mAssets = mResources.mAssets;
mData = data;就是上边的6*6数组
mIndices = indices;//长度7的数组
mLength = len;//长度6
}
最后看下如何通过typeValue获取到我们需要的type和value,再往下具体的获取value那些就不看了,不是16进制就是左移啊右移的,看着晕
static Description parseValue(TypedValue value) {
Description d = new Description();
if (value == null) {
d.type = ABSOLUTE;
d.value = 0;
} else {
if (value.type == TypedValue.TYPE_FRACTION) {
d.type = (value.data & TypedValue.COMPLEX_UNIT_MASK) ==
TypedValue.COMPLEX_UNIT_FRACTION_PARENT ?
RELATIVE_TO_PARENT : RELATIVE_TO_SELF;
d.value = TypedValue.complexToFloat(value.data);
return d;
} else if (value.type == TypedValue.TYPE_FLOAT) {
d.type = ABSOLUTE;
d.value = value.getFloat();
return d;
} else if (value.type >= TypedValue.TYPE_FIRST_INT &&
value.type <= TypedValue.TYPE_LAST_INT) {
d.type = ABSOLUTE;
d.value = value.data;
return d;
}
}
d.type = ABSOLUTE;
d.value = 0.0f;
return d;
}
其实看到最后对于这个fraction是比较模糊的,他到底是咋算出来是相对自己还是parent的?
而且也不知道fraction在xml里是咋写的。。
android:pivotY="200%"
赶紧百度下:https://zhidao.baidu.com/question/1669305667360145787.html
看完这个写个200%知道fraction是这样写的,可我还是不懂它咋区分是RELATIVE_TO_PARENT还是RELATIVE_TO_SELF的?
所以啊进行了如下的测试
<widget.TaiJiView
android:id="@+id/taiJiView1"
android:pivotX="1"
android:pivotY="200%"
android:fromDegrees="0"
android:toDegrees="360"
android:layout_width="300dp"
android:layout_height="300dp" />
//我在我写的自定义view里添加了如上的几个animation的attr属性,然后获取
int[] rotate= {android.R.attr.fromDegrees,android.R.attr.toDegrees,android.R.attr.pivotX,android.R.attr.pivotY};
TypedArray typedArray=context.obtainStyledAttributes(attrs, rotate);
float fromDegress=typedArray.getFloat(0, -1);
float toDegress=typedArray.getFloat(1, -1);
float pivotX=typedArray.getFloat(2, -1);
float pivotY=typedArray.getFraction(3, 200, 50, 33);
TypedValue typedValue=typedArray.peekValue(3);
System.err.println(fromDegress+"=========="+toDegress+"======="+pivotX+"=="+pivotY);
System.err.println("type=="+typedValue.type+"==="+typedValue.data +"=="+ TypedValue.complexToFloat(typedValue.data));
typedArray.recycle();
打印结果
0.0==========360.0=======1.0==400.0
type==6===512==2.0
public static final int TYPE_FRACTION = 0x06;//6就表示fraction的
来分析下结果,fromDegress ,toDegress,pivotX 都是普通的float,没啥看的,就来分析下Y
* @param base The base value of this fraction. In other words, a
* standard fraction is multiplied by this value.
* @param pbase The parent base value of this fraction. In other
* words, a parent fraction (nn%p) is multiplied by this
* value.
public float getFraction(int index, int base, int pbase, float defValue)
//里边最后调用了这个方法
public static float complexToFraction(int data, float base, float pbase)
{
switch ((data>>COMPLEX_UNIT_SHIFT)&COMPLEX_UNIT_MASK) {
case COMPLEX_UNIT_FRACTION:
return complexToFloat(data) * base;
case COMPLEX_UNIT_FRACTION_PARENT:
return complexToFloat(data) * pbase;
}
return 0;
}
看下这个方法,以及结果,
我们xml写的200%,那么就是2了?最后打印的是400,那么就是乘以200了?也就是说它用了base这个参数。为毛用这个,不用后边那个。不知道。
我又试着改成20%想着是不是比100小就成了P拉?哈哈,结果不是。
不过啊,改着改着就突然想起来,我们平时在anim文件下写动画的xml文件的时候不也有这种200%p的吗,赶紧就在后边加个p,果然结果就变成了100,加了p它就用了后边pbase参数。
这个时候终于知道动画是咋区分是RELATIVE_TO_PARENT还是RELATIVE_TO_SELF
明显的,200%p,这种带个p的就是相对parent拉,不带的这种200%就是相对self拉。