Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
254 views
in Technique[技术] by (71.8m points)

android - Attempting to get attribute values in code returns incorrect values

I wish to extract multiple attributes from a style resource (only interested in attributes that fall in the TextAppearance Group)

Style defined as so

<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>

First try

First I tried how TextView(lines 663-731) has it implemented, but then I found out we don't have access to com.android.internal.R

Partial Solution

Which is why I switched to this solution: https://stackoverflow.com/a/7913610/3922891

So I created textAppearanceAttr to replace com.android.internal.R.styleable.TextAppearance (only contains 10/13 TextAppearance attributes I am interested in)

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};

Here is how I used it. I get the style's resource id (resource is referenced by a clTextAppearance attribute)

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

And here is how I get the attributes (still following the answer at the above link):

    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...)

Current Issue

It seems that only the first attribute gets set, every other either sets with the default or null.

A hack that seems to work

Individual arrays:

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

And get attributes like so

    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();

Now doing this is such a waste.

Questions

  1. I would like to know why getting all the attributes at once doesn't work.
  2. Is there a solution (where all the extra work is not necessary)?

EDIT 1

I found something similar here: https://stackoverflow.com/a/13952929/3922891 And for some reason it works. Until I add more attributes to the array then everything becomes kerfuffle.

Example:

 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};

If I get text using the above array it works.

String text = ta.getString(7);

But if I change the array to the below it fails (replaced android.R.attr.orientation with android.R.attr.shadowColor)

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};

Why is this happening? (Question #1)

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

I think I have an idea why it's happening. Looks like if IDs are not sorted, you get issues. textColor for example has the lowest int value, that's why it starts working being placed on the first position in the array.

If you take a look at R.java with your stylable, you'll see that android resource compiler has sorted the IDs for you. That's why it always works if you declare stylable in attrs.xml and may not work if you manually created the arrays of IDs.

I believe there is a performance reason for IDs to be sorted. If they are sorted then attributes can be read from the AttributeSet using one traversal instead of N traversals in case of N ids.

UPDATE: I took a look at the source code and it proves my idea. Context.obtainStyledAttributes() calls JNI method AssetManager.applyStyle(). You can find source here:

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

On line 1001 you'll find a while loop where ix (index in the extracted XML attributes array) is always incremented and never reset to 0. This means if textColor is the last index in the array (variable "src" in the code) then we'll never get to that attribute.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...