使用obtainStyledAttributes获取多个样式属性

8

我正在尝试从我的代码中获取android命名空间的几个样式属性。这里我附上相关摘录。AttributeSet attrs是传递给任何自定义TextView的参数。

private static final int[] ATTRS = new int[] { android.R.attr.textSize,
   android.R.attr.text, android.R.attr.textColor,
   android.R.attr.gravity };

private void processAndroidAttributes(final Context context,
   final AttributeSet attrs) {
   final TypedArray a = context.obtainStyledAttributes(attrs, ATTRS);
   try {

       final String text = a.getString(1);
       myTextView.setText(text);
       final float textSize = a.getDimensionPixelSize(0, DEFAULT_TEXT_SIZE);
       myTextView.setTextSize(textSize);
}

我的问题是,我想要读取int[] ATTRS中描述的4个属性。如您所见,我已将textSize作为该数组的第一个元素。原因很简单-如果我将其与数组中的第二个位置交换,则其值无法正确读取(而是加载提供的默认值)。另一方面,文本在ATTRS数组中的任何位置上都可以正确加载。我不敢尝试调整gravity和textColor的位置偏好,但是这种排列方式它们无法正常工作。
有人可以解释一下为什么会出现获取属性时不稳定的行为吗?
5个回答

13

看起来这是API的一个bug。我测试了4个属性:gravitytexttextSizetextColor。以下是我使用的代码:

private void processAndroidAttributes(final Context context, final AttributeSet attrs) {
    ATTRS = new Integer[] {
               android.R.attr.textSize, android.R.attr.text,
               android.R.attr.textColor, android.R.attr.gravity };
    Arrays.sort(ATTRS);
    do {
        printPermutation(ATTRS);
        tryApplyingStyles(context, attrs, ATTRS);
        ATTRS = nextPermutation(ATTRS);
    } while ((ATTRS = nextPermutation(ATTRS)) != null);
}

方法processAndroidAttributes尝试应用所有列出的属性,并检查它们是否被应用或使用了默认值。对于每次迭代,我打印数组ATTRS的内容以及是否使用了实际值(用1标记)或使用了默认值(用0标记)。运行上述代码后,我得到以下输出结果(每次迭代都会打印几行):

[16842901,16842904,16842927,16843087]
1111
[16842901,16842904,16843087,16842927]
1110
[16842901,16842927,16842904,16843087]
1101
[16842901,16842927,16843087,16842904]
1101
[16842901,16843087,16842904,16842927]
1100
[16842901,16843087,16842927,16842904]
1100
[16842904,16842901,16842927,16843087]
1011
[16842904,16842901,16843087,16842927]
1010
[16842904,16842927,16842901,16843087]
1011
[16842904,16842927,16843087,16842901]
1011
[16842904,16843087,16842901,16842927]
1010
[16842904,16843087,16842927,16842901]
1010
[16842927,16842901,16842904,16843087]
1001
[16842927,16842901,16843087,16842904]
1001
[16842927,16842904,16842901,16843087]
1001
[16842927,16842904,16843087,16842901]
1001
[16842927,16843087,16842901,16842904]
1001
[16842927,16843087,16842904,16842901]
1001
[16843087,16842901,16842904,16842927]
1000
[16843087,16842901,16842927,16842904]
1000
[16843087,16842904,16842901,16842927]
1000
[16843087,16842904,16842927,16842901]
1000
[16843087,16842927,16842901,16842904]
1000
[16843087,16842927,16842904,16842901]
1000

正如您所见,这几乎就像是期望数组已排序,只有几个离群值。我认为这是Android API中的一个错误,因为obtainStyledAttributes的文档没有指定期望任何属性ID的顺序。


有趣的是,我得出了相同的结论。奇怪的是,这种行为没有在其他地方记录或描述。 - miha

2

我认为仅按升序迭代一次并不简单 - 当我迭代所有排列时,得到的一些结果并不符合该要求。 - Boris Strandjev

1
我确认所讨论的问题,并提供一个简单的解决方案,以便今后参考。详情请参见其他答案。
注意2件事情:
  1. int[] attrs数组的值按字母顺序排序
  2. 我们调用obtainStyledAttributes(AttributeSet, int[], int int)

    public static int[] getCommonStyledAttributes() {
      return new int[] {
        R.attr.bottom, //0
        R.attr.height, //1
        R.attr.layout, //2
        R.attr.left, //3
        R.attr.paddings, //4
        R.attr.right, //5
        R.attr.top, //6
        R.attr.width //7
      };
    }
    
    TypedArray a = context.getTheme().obtainStyledAttributes(paramAttributeSet, getCommonStyledAttributes(), 0, 0);
        this.mWidth = a.getInt(7, 0);
        this.mHeight = a.getInt(1, 0);
        this.left = a.getInt(3, 0);
        this.top = a.getInt(6, 0);
        this.right = a.getInt(5, 0);
        this.bottom = a.getInt(0, 0);
        this.type = a.getInt(2, 0);
        a.recycle();
    

谢谢确认。然而,我更建议对属性进行排序,而不是硬编码特定的值。 - Boris Strandjev
关键在于它必须被排序。而且由开发人员决定是否要通过实用方法或硬编码自动完成。对我来说,硬编码非常适合我的情况。 - AAverin

1

当我尝试获取3种不同类型的4个属性时,遇到了相同的问题。始终只有一种类型的属性传递到数组中,而且这总是它们中的第一个。 例如,如果您声明属性为{int,String,Color}:

private static final int[] ATTRS = new int[] { android.R.attr.textSize,
   android.R.attr.text, android.R.attr.textColor }

obtainStyledAttributes 方法将 int 属性传递给你的 TypedArray,其余属性将为 null。

如果你将顺序更改为 {String、Color、int}:

private static final int[] ATTRS = new int[] {android.R.attr.text, android.R.attr.textColor, android.R.attr.textSize }

您将只获取 TypedArray 中的字符串属性,其余属性将为 null。

看起来 TypedArray 只能容纳一个类型,因此您需要声明更多的 TypeArrays - 每种属性类型一个。

就像这样:

private static final int[] stringAttrs = new int[] {android.R.attr.text};
private static final int[] intAttrs = new int[] {android.R.attr.textSize};
private static final int[] colorAttrs = new int[] {android.R.attr.textColor, android.R.attr.background};

    private void processAndroidAttributes(final Context context,
       final AttributeSet attrs) {
       final TypedArray stringA = context.obtainStyledAttributes(attrs, stringAttrs);
       final TypedArray intA = context.obtainStyledAttributes(attrs, intAttrs);
       final TypedArray colorA = context.obtainStyledAttributes(attrs, colorAttrs);

       myTextView.setText(stringA.getString(0));
       myTextView.setTextSize(intA.getDimensionPixelSize(0, DEFAULT_TEXT_SIZE));
       myTextView.setTextColor(colorA.getColor(0, Color.WHITE));
       myTextView.setBackgroundColor(colorA.getColor(1, Color.WHITE));

       stringA.recycle();
       intA.recycle();
       colorA.recycle();

    }

1
你尝试过对属性进行排序吗?因为在我的情况下,我至少不需要分离数组... - Boris Strandjev
这正是我的实验告诉我的 :) 考虑给我提出的解决方案点赞。 - Boris Strandjev
按名称字母顺序排序属性,无法得到正确的结果。@BorisStrandjev的方法是正确的。例如:{android.R.attr.padding, android.R.attr.paddingBottom, android.R.attr.paddingEnd, android.R.attr.paddingHorizontal, android.R.attr.paddingLeft, android.R.attr.paddingRight, android.R.attr.paddingStart, android.R.attr.paddingTop, android.R.attr.paddingVertical,},在调用obtainStyledAttributes时使用这些将不会准确工作,比如当您只在xml中指定了paddingTop时。 - Vikhram

0

我觉得可能是因为int[]数组通常来自于xml,编译时会排序,例如在这里检查用例:

如何定义自定义小部件的主题(样式)项目

其中数组来自R.styleable.CustomImageButton而不是手工制作。

我能理解他们使用和期望排序数组,因为在编译时对xml资源进行了大量的优化工作,以便在运行时能够高效地使用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接