Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Library's style attributes have no values even though they are explicitly set

I made a library with a custom view that inflates a layout when created. Views in the layout are styled with style="?attr/labelStyle" or any other attribute.

The attribute is declared the library's attrs.xml:

<attr name="myViewStyle" format="reference"/>

<declare-styleable name="MyView">
    <attr name="labelStyle" format="reference|color"/>
</declare-styleable>

I have set a default value to this attribute in the library's styles.xml:

<style name="MyViewStyle">
    <item name="labelStyle">@style/LabelStyle</item>
</style>

<style name="LabelStyle">
    <item name="android:textColor">?android:attr/textColorPrimary</item>
    <item name="...">...</item>
</style>

And finally in the library's themes.xml:

<style name="MyViewStyleLight" parent="Theme.AppCompat.Light">
    <item name="myViewStyle">@style/MyViewStyle</item>
</style>

Now this was the library's default styles, but it is overridden in the main project styles.xml

<style name="AppTheme" parent="Theme.AppCompat.Light">
    <item name="myViewStyle">@style/MyViewStyleCustom</item>
</style>

<style name="MyViewStyleCustom" parent="MyViewStyleLight">
    <item name="android:textColor">@color/gray</item>
    <item name="...">...</item>
</style>

The custom view code:

public MyView(Context context) {
    this(context, null, R.attr.myViewStyle, 0);
}

public MyView(Context context, @Nullable AttributeSet attrs) {
    this(context, attrs, R.attr.myViewStyle, 0);
}

public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
    this(context, attrs, defStyleAttr, 0);
}

public MyView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
    super(createThemeWrapper(context, R.attr.myViewStyle, R.style.MyViewStyleLight),
            attrs, defStyleAttr, defStyleRes);
    initLayout();
}

private static Context createThemeWrapper(Context context, int styleAttr, int defaultStyle) {
    final TypedArray ta = context.obtainStyledAttributes(new int[]{styleAttr});
    int style = ta.getResourceId(0, defaultStyle);
    ta.recycle();
    return new ContextThemeWrapper(context, style);
}

private void initLayout() {
    LayoutInflater inflater = LayoutInflater.from(getContext());
    inflater.inflate(R.layout.my_view, this);
    ...
}

I explain about the ContextThemeWrapper below. Now the app crashes on the line where the layout gets inflated. Here's the important part of the crash log:

android.view.InflateException: Binary XML file line #0: Binary XML file line #0: Error inflating class com.example.MyView
      at android.view.LayoutInflater.inflate(LayoutInflater.java:539)
      at android.view.LayoutInflater.inflate(LayoutInflater.java:423)
      [...]
    Caused by: java.lang.UnsupportedOperationException: Failed to resolve attribute at index 13: TypedValue{t=0x2/d=0x7f030057 a=-1}
      at android.content.res.TypedArray.getDrawable(TypedArray.java:867)
      [...]

The layout inflater can't find the attribute's value. When I tried to get the attribute by code, it returns nothing. The attribute actually exists, only it has no value set to it even though I have clearly set one.

How exactly I am supposed to style my library? I am almost certain that I did every thing the same as the SublimePicker library but it just won't work. There's a little difference in the part with the ContextThemeWrapper, but it probably isn't the problem. I feel like I forgot a tiny thing somewhere that makes the attribute have no value, something is not connected, I don't know.

I know this is a very long question, but it cannot be more concise, I simplified everything as much as I could. I changed most of the information that was in the previous version of my question, making it completely different. The two answers are not relevant at all now, not that they ever were. The bounty was automatically rewarded.

If that could help someone I can add a download to my actual project, but as I said this simplified example has the exact same form as my project.

like image 975
Nicolas Avatar asked Nov 26 '17 02:11

Nicolas


2 Answers

This answer is based on what I understand from your question and conversation between you and Vinayak B. If I misinterpret ,Please correct me.

there is difference in style.xml in both place app as well as lib. addition I have removed theme.xml as well as changes in constructor of MyView.java for default style

I have changed following things

  • overridden in the main project styles.xml

    <style name="AppTheme" parent="Theme.AppCompat.Light">
        <item name="myViewStyle">@style/MyViewStyleCustom</item>
    </style>
    
    <style name="MyViewStyleCustom" parent="MyViewStyle">
        <item name="labelStyle">@style/LabelStyle123</item>
    </style>
    
    <style name="LabelStyle123">
        <item name="android:textColor">#f00</item>
    </style>
    
  • lib styles.xml

    <resources>
        <style name="MyViewStyle">
            <item name="labelStyle">@style/LabelStyle</item>
            <item name="TextStyle">@style/textStyle</item>
        </style>
    
        <style name="LabelStyle">
            <item name="android:textColor">#00f</item>
        </style>
    
        <style name="textStyle">
            <item name="android:textColor">#009</item>
        </style>
    </resources>
    
  • MyView.java - changed constructor of and set default MyViewStyle if no any attribute come from application.

    public MyView(Context context) {
        this(context, null, R.attr.myViewStyle,  R.style.MyViewStyle);
    }
    
    public MyView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, R.attr.myViewStyle,  R.style.MyViewStyle);
    }
    
    public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        this(context, attrs, defStyleAttr, R.style.MyViewStyle);
    }
    
    public MyView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(createThemeWrapper(context, defStyleAttr,defStyleRes), attrs, defStyleAttr, defStyleRes);
        initLayout();
    }
    
    private static Context createThemeWrapper(Context context, int styleAttr, int defaultStyle) {
        final TypedArray ta = context.obtainStyledAttributes(new int[]{styleAttr});
        int style1 = ta.getResourceId(0, defaultStyle);
        ta.recycle();
        return new ContextThemeWrapper(context, style1);
    }
    

so either it will take default labelStyle if it is not overridden in main activity style or overridden labelStyle

like image 54
Archit Sureja Avatar answered Nov 16 '22 04:11

Archit Sureja


This answer is based on what I understand from your question. If I misinterpret ,Please correct me.

First of all myTextColor is an attribute name in your library. not a attribute value. You supposed to give a value for myTextColor when ever you using this library. Otherwise there may occur 'InflateException' . You can avoid this by following way.

<YourCustomeView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:myTextColor="#000"/>

1. Set myTextColor value directly when you use outside the library.

OR

  1. In your library where you using this myTextColor attribute, check if this attribute have value or not. If it doesn't have any value then use a default value for myTextColor

     private void init(@Nullable AttributeSet attrs) {
                 TypedArray ta = getContext().obtainStyledAttributes(attrs, 
                                 R.styleable.MyLibrary);
            boolean hasRawRes = ta.hasValue(R.styleable.myTextColor);
            if(hasRawRes){
              // Use `myTextColor` attr here
            }else{
              // use default color
            }
    

    }

UPDATE ANSWER

This answer for the updated question

First of all you are trying to fetch a attr value from your library to your project using ?attr/ .Which does not going to work. because

Your project using Theme.AppCompat theme as (I'm guessing) parent theme for your Activities. When you use ?attr inside that activity, you can only fetch attribute values of Theme.AppCompat. But you are trying to fetch ?attr/labelStyle which is not a attribute of Theme.AppCompat rather than it's your library attribute. That's why you are getting that crash. If you want to use any style from your library to your project you can use @style tag

For example

 style="@style/labelStyle"

If it's not what you are looking for ,Please kindly share your source code.So I can understand more on this problem.

like image 38
Vinayak B Avatar answered Nov 16 '22 04:11

Vinayak B