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.
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
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
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With