尝试在代码中获取属性值会返回不正确的值

时间:2014-08-20 10:30:42

标签: android android-layout

我希望从样式资源中提取多个属性(仅对属于TextAppearance组的属性感兴趣)

样式定义如此

<style name="Label" parent="@android:style/TextAppearance.Small">
    <item name="android:textColor">@color/floatlabel_text</item>
    <item name="android:textSize">8dp</item>
    <item name="android:textStyle">bold</item>
</style>

首先尝试

首先我尝试了TextView(第663-731行)如何实现它,但后来我发现我们无法访问 com.android.internal.R

部分解决方案

这就是我切换到此解决方案的原因:https://stackoverflow.com/a/7913610/3922891

所以我创建了textAppearanceAttr来替换com.android.internal.R.styleable.TextAppearance(只包含我感兴趣的10/13 TextAppearance属性)

int[] textAppearanceAttr = new int[]{    
        android.R.attr.textColor,
        android.R.attr.textSize,
        android.R.attr.typeface,
        android.R.attr.fontFamily,
        android.R.attr.textStyle,
        android.R.attr.textAllCaps,
        android.R.attr.shadowColor,
        android.R.attr.shadowDx,
        android.R.attr.shadowDy,
        android.R.attr.shadowRadius};

以下是我如何使用它。我得到了样式的资源ID(资源由clTextAppearance属性引用)

   int ap = a.getResourceId(R.styleable.CustomLabelLayout_clTextAppearance, android.R.style.TextAppearance_Small);
   TypedArray appearance = mContext.obtainStyledAttributes(ap, textAppearanceAttr);

以下是我获取属性的方法(仍然按照上面的链接回答):

    mLabelTextColor = appearance.getColorStateList(0);
    mLabelTextSize = appearance.getDimensionPixelSize(1, 15);
    mLabelTypeface = appearance.getInt(2, -1);
    mLabelFontFamily = appearance.getString(3);
    mLabelTextStyle = appearance.getInt(4, -1);
    (5 more...)

当前问题

似乎只有第一个属性被设置,所有其他属性都设置为默认值或null。

似乎有效的黑客

个别数组:

int[] textSizeAttr = new int[] { android.R.attr.textSize};
int[] textStyleAttr = new int[] { android.R.attr.textStyle};

获得类似的属性

    appearance.recycle();
    appearance = mContext.obtainStyledAttributes(ap, textSizeAttr);
    mLabelTextSize = appearance.getDimensionPixelSize(0, 15);
    appearance.recycle();
    appearance = mContext.obtainStyledAttributes(ap, textStyleAttr);
    mLabelTextStyle = appearance.getInt(0, -1);
    appearance.recycle();

现在这样做是浪费。

问题

  1. 我想知道为什么一次性获取所有属性都不起作用。
  2. 是否有解决方案(不需要进行所有额外工作)?
  3. 编辑1

    我在这里发现了类似的内容:https://stackoverflow.com/a/13952929/3922891 由于某种原因,它的工作原理。直到我向数组添加更多属性,然后一切都变得麻烦。

    示例:

     int[] attrs = {android.R.attr.textColor,
                android.R.attr.textSize,
                android.R.attr.background,
                android.R.attr.textStyle,
                android.R.attr.textAppearance,
                android.R.attr.textColorLink,
                android.R.attr.orientation,
                android.R.attr.text};
    

    如果我使用上面的数组得到文本就可以了。

    String text = ta.getString(7);
    

    但是,如果我将数组更改为以下它失败(用android.R.attr.shadowColor替换android.R.attr.orientation)

    int[] attrs = {android.R.attr.textColor,
                android.R.attr.textSize,
                android.R.attr.background,
                android.R.attr.textStyle,
                android.R.attr.textAppearance,
                android.R.attr.textColorLink,
                android.R.attr.shadowColor,
                android.R.attr.text};
    

    为什么会这样? (问题#1)

3 个答案:

答案 0 :(得分:11)

我想我知道它为什么会发生。看起来如果ID没有排序,你会遇到问题。例如,textColor具有最低int值,这就是为什么它开始工作放置在数组中的第一个位置。

如果您使用样式查看R.java,您会看到android资源编译器已为您排序了ID。这就是为什么它在attrs.xml中声明样式时总是有效的,如果您手动创建了ID数组,它可能无效。

我认为有一个性能原因可以对ID进行排序。如果对它们进行了排序,则可以使用一次遍历而不是N次遍历来从AttributeSet读取属性。

更新: 我看了一下源代码,它证明了我的想法。 Context.obtainStyledAttributes()调用JNI方法AssetManager.applyStyle()。 你可以在这里找到来源:

https://android.googlesource.com/platform/frameworks/base.git/+/android-4.3_r2.1/core/jni/android_util_AssetManager.cpp

在第1001行,您将找到一个while循环,其中ix(提取的XML属性数组中的索引)总是递增并且永远不会重置为0.这意味着textColor是数组中的最后一个索引(变量&# 34; src&#34;在代码中)然后我们永远不会达到那个属性。

答案 1 :(得分:6)

感谢@PrivatMamtora和@igret调查此事! 如果问题是必须订购ID,这应该没问题。

private static final int ATTR_PADDING = android.R.attr.padding;
private static final int ATTR_TEXT_COLOR = android.R.attr.textColor;
private static final int ATTR_TEXT_SIZE = android.R.attr.textSize;

private void loadAttributes(Context context, AttributeSet attrs) {
    int[] ids = { ATTR_PADDING, ATTR_TEXT_COLOR, ATTR_TEXT_SIZE};
    Arrays.sort(ids); // just sort the array

    TypedArray a = context.obtainStyledAttributes(attrs, ids);
    try {
        padding = a.getDimensionPixelSize(indexOf(ATTR_PADDING, ids), padding);
        textColor = a.getColor(indexOf(ATTR_TEXT_COLOR, ids), textColor);
        textSize = a.getDimensionPixelSize(indexOf(ATTR_TEXT_SIZE, ids), textSize);
    } finally {
        a.recycle();
    }
}

private int indexOf(int id, int[] ids) {
    for (int i = 0; i < ids.length; i++) {
        if (ids[i] == id) {
            return i;
        }
    }
    throw new RuntimeException("id " + id +  " not in ids"); 
}

答案 2 :(得分:5)

让它像这样工作:我定义了一个新的styleable

<?xml version="1.0" encoding="utf-8"?>
<resources>     
    <declare-styleable name="Label" >
        <attr name="android:textColor" />
        <attr name="android:textSize" />
        <attr name="android:textStyle" />
        <attr name="android:typeface" />
    </declare-styleable>
</resources>

然后我的styles.xml:

<resources xmlns:android="http://schemas.android.com/apk/res/android">
    <style name="Label" parent="@android:style/TextAppearance.Small">
        <item name="android:textColor">#12345678</item>
        <item name="android:textSize">8dp</item>
        <item name="android:textStyle">bold</item>
        <item name="android:typeface">serif</item>
    </style>    
</resources>

最后测试:

public class TextAppearanceTest extends AndroidTestCase {

    public void test() {
        TypedArray a = getContext().obtainStyledAttributes(R.style.Label, R.styleable.Label);
        assertTrue(a.getColor(R.styleable.Label_android_textColor, -1) != -1);
        assertTrue(a.getDimensionPixelSize(R.styleable.Label_android_textSize, -1) != -1);
        assertTrue(a.getInt(R.styleable.Label_android_typeface, -1) != -1);
    }
}