Resources$NotFoundException in Graphical Layout ADT preview (but app actually Works) - android

My problem is that loading an array of strings defined in XML works in the app but will result in an error in the ADT Graphical Layout preview.
Now I can't see any graphics in the Graphical Layout because of this error, and it's difficult to work with other graphics.
But the view is loading and displaying the strings fine if I build and run my app.
So I suppose my code is correct but either:
I am missing some limitations of the Graphical Layout preview and some workaround
or perhaps I'm missing something obvious and doing things wrong even if it seems to work in the app
I have a custom view where I get an array defined by me in an array.xml file.
public class ScoreTable extends View {
[...]
#Override
protected void onDraw(Canvas canvas) {
[...]
int score_vals[] = getResources().getIntArray(R.array.score_vals);
[...]
}
[...]
}
My array is defined in res/values/array.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<array name="score_vals">
<item >10</item>
<item >20</item>
<item >50</item>
</array>
</resources>
Graphical Layout is blank and says:
Int array resource ID #0x7f050000
Exception details are logged in Window > Show View > Error Log
But of course I have "public static final int score_vals=0x7f050000;" in R.java!
The details of this error are in a 50-deep stack, but resumes to this:
android.content.res.Resources$NotFoundException: Int array resource ID #0x7f050000
at android.content.res.Resources.getIntArray(Resources.java:405)
at com.threecats.poker.ScoreTable.onDraw(ScoreTable.java:53)
at android.view.View.draw(View.java:6740)
[...]
So, should getResources().getXXXArray() work in the context of a ADT Graphical Layout preview?
I would like to mention that I tried with both "array" and "array-integer" in the XML, and both work in the app but not in the preview.
Also I tried to save the Context from the constructor of the view in a private Context member... didn't help either.

Your code is alright but unfortunately there are still some bugs in ADT plugin and there is one of them. Layout Editor has troubles with rendering custom views. I had the same issue and the only workout I have found is checking View.isInEditMode and initializing int array in some other way but not from resources. So your code will look like this:
int score_vals[];
if (isInEditMode()) {
score_vals = { 10, 20, 50 };
} else {
score_vals = getResources().getIntArray(R.array.score_vals);
}
And by the way don't create or load any resources in your onDraw methods. I suppose getResources().getIntArray uses some sort of caching but anyway your perfomance may suffer.

I found a kind of a workaround whereby you have to hijack android's own attributes to get access to resources in the designer.
The following should provide the idea, but you would have to find a native android property of type int[]
This custom view XML should render in the graphical layout preview while using resources
<!-- Could override individual attributes here too rather than using a style -->
<com.github.espiandev.showcaseview.ShowcaseView
style="#style/ShowcaseView"/>
styles.xml - Style specifying some of the resources to use
<style name="ShowcaseView" parent="match_fill">
<!--# Cling drawable -->
<item name="android:src">#drawable/cling</item>
<!--# Title #-->
<item name="android:contentDescription">#string/showcase_title</item>
<!--# Description #-->
<item name="android:description">#string/showcase_description</item>
<!--# Button Text #-->
<item name="android:text">#string/ok</item>
<item name="sv_titleTextColor">#33B5E5</item>
<item name="sv_detailTextColor">#FFFFFF</item>
<item name="sv_backgroundColor">#3333B5E5</item>
<item name="sv_buttonBackgroundColor">#3333B5E5</item>
<item name="sv_buttonForegroundColor">#33B5E5</item>
</style>
attrs.xml - Custom attribute definition compatible with design-time preview
<!-- The android attrs assume the corresponding android format / data type -->
<declare-styleable name="ShowcaseView">
<!--# Cling drawable -->
<attr name="android:src"/>
<!--# Title #-->
<attr name="android:contentDescription"/>
<!--# Description #-->
<attr name="android:description"/>
<!--# Button Text #-->
<attr name="android:text"/>
<attr name="sv_backgroundColor" format="color|reference" />
<attr name="sv_detailTextColor" format="color|reference" />
<attr name="sv_titleTextColor" format="color|reference" />
<attr name="sv_buttonBackgroundColor" format="color|reference" />
<attr name="sv_buttonForegroundColor" format="color|reference" />
</declare-styleable>
ShowcaseView.java - Using the custom attributes in the custom view
public ShowcaseView(Context context) {
this(context, null, R.styleable.CustomTheme_showcaseViewStyle);
}
public ShowcaseView(Context context, AttributeSet attrs) {
this(context, attrs, R.styleable.CustomTheme_showcaseViewStyle);
}
public ShowcaseView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// Get the attributes for the ShowcaseView
final TypedArray styled = context.getTheme().obtainStyledAttributes(attrs, R.styleable.ShowcaseView, 0, 0);
showcase = styled.getDrawable(R.styleable.ShowcaseView_android_src);
titleText = styled.getString(R.styleable.ShowcaseView_android_contentDescription);
subText = styled.getString(R.styleable.ShowcaseView_android_description);
buttonText = styled.getString(R.styleable.ShowcaseView_android_text);
backColor = styled.getInt(R.styleable.ShowcaseView_sv_backgroundColor, Color.argb(128, 80, 80, 80));
detailTextColor = styled.getColor(R.styleable.ShowcaseView_sv_detailTextColor, Color.WHITE);
titleTextColor = styled.getColor(R.styleable.ShowcaseView_sv_titleTextColor, Color.parseColor("#49C0EC"));
styled.recycle();
// Now make use of the fields / do further initialization ..
}

Related

How to read a TextAppearance attribute programmatically [duplicate]

Currently I'm using either a WebView or a TextView to show some dynamic data coming from a webservice in one of my apps.
If the data contains pure text, it uses the TextView and applies a style from styles.xml.
If the data contains HTML (mostly text and images) it uses the WebView.
However, this WebView is unstyled. Therefor it looks a lot different from the usual TextView.
I've read that it's possible to style the text in a WebView simply by inserting some HTML directly into the data. This sounds easy enough, but I would like to use the data from my Styles.xml as the values required in this HTML so I won't need to change the colors et cetera on two locations if I change my styles.
So, how would I be able to do this? I've done some extensive searching but I have found no way of actually retrieving the different style attributes from your styles.xml. Am I missing something here or is it really not possible to retrieve these values?
The style I'm trying to get the data from is the following:
<style name="font4">
<item name="android:layout_width">fill_parent</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:textSize">14sp</item>
<item name="android:textColor">#E3691B</item>
<item name="android:paddingLeft">5dp</item>
<item name="android:paddingRight">10dp</item>
<item name="android:layout_marginTop">10dp</item>
<item name="android:textStyle">bold</item>
</style>
I'm mainly interested in the textSize and textColor.
It is possible to retrieve custom styles from styles.xml programmatically.
Define some arbitrary style in styles.xml:
<style name="MyCustomStyle">
<item name="android:textColor">#efefef</item>
<item name="android:background">#ffffff</item>
<item name="android:text">This is my text</item>
</style>
Now, retrieve the styles like this
// The attributes you want retrieved
int[] attrs = {android.R.attr.textColor, android.R.attr.background, android.R.attr.text};
// Parse MyCustomStyle, using Context.obtainStyledAttributes()
TypedArray ta = obtainStyledAttributes(R.style.MyCustomStyle, attrs);
// Fetch the text from your style like this.
String text = ta.getString(2);
// Fetching the colors defined in your style
int textColor = ta.getColor(0, Color.BLACK);
int backgroundColor = ta.getColor(1, Color.BLACK);
// Do some logging to see if we have retrieved correct values
Log.i("Retrieved text:", text);
Log.i("Retrieved textColor as hex:", Integer.toHexString(textColor));
Log.i("Retrieved background as hex:", Integer.toHexString(backgroundColor));
// OH, and don't forget to recycle the TypedArray
ta.recycle()
The answer #Ole has given seems to break when using certain attributes, such as shadowColor, shadowDx, shadowDy, shadowRadius (these are only a few I found, there might be more)
I have no idea as to why this issue occurs, which I am asking about here, but #AntoineMarques coding style seems to solve the issue.
To make this work with any attribute it would be something like this
First, define a stylable to contain the resource ids like so
attrs.xml
<resources>
<declare-styleable name="MyStyle" >
<attr name="android:textColor" />
<attr name="android:background" />
<attr name="android:text" />
</declare-styleable>
</resources>
Then in code you would do this to get the text.
TypedArray ta = obtainStyledAttributes(R.style.MyCustomStyle, R.styleable.MyStyle);
String text = ta.getString(R.styleable.MyStyle_android_text);
The advantage of using this method is, you are retrieving the value by name and not an index.
The answers from Ole and PrivatMamtora didn't work well for me, but this did.
Let's say I wanted to read this style programmatically:
<style name="Footnote">
<item name="android:fontFamily">#font/some_font</item>
<item name="android:textSize">14sp</item>
<item name="android:textColor">#color/black</item>
</style>
I could do it like this:
fun getTextColorSizeAndFontFromStyle(
context: Context,
textAppearanceResource: Int // Can be any style in styles.xml like R.style.Footnote
) {
val typedArray = context.obtainStyledAttributes(
textAppearanceResource,
R.styleable.TextAppearance // These are added to your project automatically.
)
val textColor = typedArray.getColorStateList(
R.styleable.TextAppearance_android_textColor
)
val textSize = typedArray.getDimensionPixelSize(
R.styleable.TextAppearance_android_textSize
)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val typeface = typedArray.getFont(R.styleable.TextAppearance_android_fontFamily)
// Do something with the typeface...
} else {
val fontFamily = typedArray.getString(R.styleable.TextAppearance_fontFamily)
?: when (typedArray.getInt(R.styleable.TextAppearance_android_typeface, 0)) {
1 -> "sans"
2 -> "serif"
3 -> "monospace"
else -> null
}
// Do something with the fontFamily...
}
typedArray.recycle()
}
I took some inspiration from Android's TextAppearanceSpan class, you can find it here: https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/text/style/TextAppearanceSpan.java
I was not able to get the earlier solutions to work.
My style is:
<style name="Widget.TextView.NumPadKey.Klondike" parent="Widget.TextView.NumPadKey">
<item name="android:textSize">12sp</item>
<item name="android:fontFamily">sans-serif</item>
<item name="android:textColor">?attr/wallpaperTextColorSecondary</item>
<item name="android:paddingBottom">0dp</item>
</style>
The obtainStyledAttributes() for android.R.attr.textSize gives String results of "12sp" which I then have to parse. For android.R.attr.textColor it gave a resource file XML name. This was much too cumbersome.
Instead, I found an easy way using ContextThemeWrapper.
TextView sample = new TextView(new ContextThemeWrapper(getContext(), R.style.Widget_TextView_NumPadKey_Klondike), null, 0);
This gave me a fully-styled TextView to query for anything I want. For example:
float textSize = sample.getTextSize();
With Kotlin, if you include the androidx.core:core-ktx library in your app/library...
implementation("androidx.core:core-ktx:1.6.0") // Note the -ktx
...you can have either of the following (no need for you to recycle the TypedArray):
// Your desired attribute IDs
val attributes = intArrayOf(R.attr.myAttr1, R.attr.myAttr2, android.R.attr.text)
context.withStyledAttributes(R.style.MyStyle, attributes) {
val intExample = getInt(R.styleable.MyIntAttrName, 0)
val floatExample = getFloat(R.styleable.MyFloatAttrName, 0f)
val enumExample = R.styleable.MyEnumAttrName, MyEnum.ENUM_1 // See Note 1 below
// Similarly, getColor(), getBoolean(), etc.
}
context.withStyledAttributes(R.style.MyStyle, R.styleable.MyStyleable) {
// Like above
}
// attributeSet is provided to you like in the constructor of a custom view
context.withStyledAttributes(attributeSet, R.styleable.MyStyleable) {
// Like above
}
Note 1 (thanks to this answer)
For getting an enum value you can define this extension function:
internal inline fun <reified T : Enum<T>> TypedArray.getEnum(index: Int, default: T) =
getInt(index, -1).let { if (it >= 0) enumValues<T>()[it] else default }
Note 2
The difference between -ktx dependencies like androidx.core:core and androidx.core:core-ktx is that the -ktx variant includes useful extension functions for Kotlin.
Otherwise, they are the same.
Also, thanks to the answer by Ole.
If accepted solution not working for try to rename attr.xml to attrs.xml (worked for me)

I can't get styleable array of attrs

I am declaring a styleable view with attrs, and I created the file myview_attrs.xml this simple way:
<resources>
<declare-styleable name="TestStyleable">
<attr name="testAttr" format="integer"/>
</declare-styleable>
</resources>
Then, I want to load this attr in my code:
void loadAttributes(AttributeSet attrs) {
TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.);
}
After R.styleable. , with ctrl-SPACE, these are the options I am given:
That is obviously wrong. Indeed, I can't select R.styleable.TestStyleable, because it's not proposed in the menu, but only R.styleable.TestStyleable_testAttr, that does not make sense.
Of course, if I choose the second one it gives me error, because a int[] is expected and I put an int. On the other side, if I manually write R.styleable.TestStyleable fails with "Cannot resolve symbol".
What am I missing here??
Update: this ended to be an Android Studio bug.
As #Yurii Tsap stated, this is an Android Studio bug, and it finally got fixed by making
File -> Invalidate Caches / Restart

Understanding the xmlns attribute in an Android layout

I have only recently had to set the xmlns attribute in an Android layout file. Initially when I was adding a third-party control, certain attributes in the control's XML didn't have a prefix to identify the namespace. When I ran my app, the control was displayed but those attributes that didn't have the namespace prefix were ignored. Only after adding the xmlns to the top of my file and adding the prefix to the attributes did those attributes get recognized at run time. Here is what the corrected code looks like:
xmlns:fab="http://schemas.android.com/apk/res-auto"
<com.getbase.floatingactionbutton.FloatingActionButton
android:id="#+id/ivFAB"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
fab:fab_icon="#drawable/ic_fab_star"
fab:fab_colorNormal="#color/pink_500"
fab:fab_colorPressed="#color/pink_100"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_marginRight="15dp"
android:layout_marginBottom="15dp"
android:visibility="visible"
/>
The xmlns prefix her is 'fab'. What I don't understand is that without the namespace and prefix, the app compiles without any errors. Why doesn't Android Studio complain that it cannot find fab_icon? Why does it just ignore these attributes? I have seen a number of posts throughout stackoverflow on different topics where someone has indicated to leave out the prefix and then the code worked. So I'm at a loss to understand what's going on. In some problems (like mine) having the prefix is required but in others it isn't? Is this an issue with different versions of Android Studio or the SDK versions?
Yes. Even you can define your own custom layout attributes.
Step 1: Create a subclass of a view.
class PieChart extends View {
public PieChart(Context context, AttributeSet attrs) {
super(context, attrs);
}
}
Step 2: Define Custom Attributes with <declare-styleable> in res/values/attrs.xml .
<resources>
<declare-styleable name="PieChart">
<attr name="showText" format="boolean" />
<attr name="labelPosition" format="enum">
<enum name="left" value="0"/>
<enum name="right" value="1"/>
</attr>
</declare-styleable>
</resources>
Step 3: Using those attributes inside your layout xml.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:custom="http://schemas.android.com/apk/res/com.example.customviews">
<com.example.customviews.charting.PieChart
custom:showText="true"
custom:labelPosition="left" />
</LinearLayout>
Step 4: Applying custom attributes to your view.
public PieChart(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.getTheme().obtainStyledAttributes(
attrs,
R.styleable.PieChart,
0, 0);
try {
mShowText = a.getBoolean(R.styleable.PieChart_showText, false);
mTextPos = a.getInteger(R.styleable.PieChart_labelPosition, 0);
} finally {
a.recycle();
}
}
Step 5: Adding properties and events
Attributes are a powerful way of controlling the behavior and appearance of views, but they can only be read when the view is initialized. To provide dynamic behavior, expose a property getter and setter pair for each custom attribute. The following snippet shows how PieChart exposes a property called showText
public boolean isShowText() {
return mShowText;
}
public void setShowText(boolean showText) {
mShowText = showText;
invalidate();
requestLayout();
}
For more information and details, read this link.

Android Custom Control namespace issue

I've been working on a Custom Control for Android and although I tried to do what's suggested here there seems to be something I'm doing wrong.
Here's my code to see if anyone can spot the problem:
MyComponent.java
public MyComponent(Context context, AttributeSet attrs)
{
super(context);
TypedArray arr = context.obtainStyledAttributes(attrs, R.styleable.MyComponent);
CharSequence myId = arr.getString(R.styleable.MyComponent_identifier);
if (myId != null)
{
this.setIdentifier(myId.toString());
}
Integer cds = arr.getInteger(R.styleable.MyComponent_cd_number, 0);
if(cds != null)
{
this.setCds(cds);
}
arr.recycle();
}
attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="MyComponent">
<attr name="cd_number" format="integer" />
<attr name="identifier" format="string" />
</declare-styleable>
</resources>
main.xml
<TableLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:bgl="http://schemas.android.com/apk/res/my.test.package.components"
android:id="#+id/table"
android:layout_width="match_parent"
android:layout_height="match_parent">
...
<my.test.package.MyComponent
android:id="#+id/hand"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_span="2"
bgl:cd_number="4"
bgl:identifier="plr"/>
...
</TableLayout>
When I put this I get the following errors:
error: No resource identifier found for attribute 'cd_number' in package 'my.test.package'
error: No resource identifier found for attribute 'identifier' in package 'my.test.package'
If I change my namespace to something like:
xmlns:bgl="http://schemas.mywhatever.com/apk/res/my.test.package"
...the errors go way and the thing runs but myId is null and cds is 0 (the default value!) back on the MyComponent.java constructor.
I'd say it's some very basic mistake but I not being able to spot it and since there's not much documentation on this I decided to ask here.
Thanks in advance!
Ok. I got it solved!
On the original post I had:
xmlns:bgl="http://schemas.android.com/apk/res/my.test.package
...but in my source I had:
xmlns:bgl="http://schemas.android.com/apk/res/my.test.package.components
...because I thought one should put the URI to the components package.
THIS IS WRONG!
On the xmlns it should be the application name as is declared on the Manifest!
When I removed the "components" part of the xmlns it "matched" the application name in the Manifest and the errors went away and when I ran the thing in debug I could actually see the values I was passing to the parameters in the XML!
Hope this helps someone else! :-)
UPDATE
Later on I had the need to move the control into a library and faced the problem again. It seems that when you put the component in a library and use it on a client app you must declare the xmlns as below:
xmlns:myns="http://schemas.android.com/apk/res-auto"
If you do so (and have the library declared as an Android dependency) Eclipse (or is it Android?) will search the dependencies for the appropriate attribute bindings.
I had a problem similar to this, turned out it was calling a different constructor
Try with the constructor that takes in the defStyle parameter
public MyComponent(Context context, AttributeSet attrs, int defStyle)

How to add a custom button state

For instance, the default button has the following dependencies between its states and background images:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_window_focused="false" android:state_enabled="true"
android:drawable="#drawable/btn_default_normal" />
<item android:state_window_focused="false" android:state_enabled="false"
android:drawable="#drawable/btn_default_normal_disable" />
<item android:state_pressed="true"
android:drawable="#drawable/btn_default_pressed" />
<item android:state_focused="true" android:state_enabled="true"
android:drawable="#drawable/btn_default_selected" />
<item android:state_enabled="true"
android:drawable="#drawable/btn_default_normal" />
<item android:state_focused="true"
android:drawable="#drawable/btn_default_normal_disable_focused" />
<item
android:drawable="#drawable/btn_default_normal_disable" />
</selector>
How can I define my own custom state (smth like android:state_custom), so then I could use it to dynamically change my button visual appearance?
The solution indicated by #(Ted Hopp) works, but needs a little correction: in the selector, the item states need an "app:" prefix, otherwise the inflater won't recognise the namespace correctly, and will fail silently; at least this is what happens to me.
Allow me to report here the whole solution, with some more details:
First, create file "res/values/attrs.xml":
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="food">
<attr name="state_fried" format="boolean" />
<attr name="state_baked" format="boolean" />
</declare-styleable>
</resources>
Then define your custom class. For instance, it may be a class "FoodButton", derived from class "Button". You will have to implement a constructor; implement this one, which seems to be the one used by the inflater:
public FoodButton(Context context, AttributeSet attrs) {
super(context, attrs);
}
On top of the derived class:
private static final int[] STATE_FRIED = {R.attr.state_fried};
private static final int[] STATE_BAKED = {R.attr.state_baked};
Also, your state variables:
private boolean mIsFried = false;
private boolean mIsBaked = false;
And a couple of setters:
public void setFried(boolean isFried) {mIsFried = isFried;}
public void setBaked(boolean isBaked) {mIsBaked = isBaked;}
Then override function "onCreateDrawableState":
#Override
protected int[] onCreateDrawableState(int extraSpace) {
final int[] drawableState = super.onCreateDrawableState(extraSpace + 2);
if (mIsFried) {
mergeDrawableStates(drawableState, STATE_FRIED);
}
if (mIsBaked) {
mergeDrawableStates(drawableState, STATE_BAKED);
}
return drawableState;
}
Finally, the most delicate piece of this puzzle; the selector defining the StateListDrawable that you will use as a background for your widget. This is file "res/drawable/food_button.xml":
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res/com.mydomain.mypackage">
<item
app:state_baked="true"
app:state_fried="false"
android:drawable="#drawable/item_baked" />
<item
app:state_baked="false"
app:state_fried="true"
android:drawable="#drawable/item_fried" />
<item
app:state_baked="true"
app:state_fried="true"
android:drawable="#drawable/item_overcooked" />
<item
app:state_baked="false"
app:state_fried="false"
android:drawable="#drawable/item_raw" />
</selector>
Notice the "app:" prefix, whereas with standard android states you would have used prefix "android:". The XML namespace is crucial for a correct interpretation by the inflater and depends on the type of project in which you are adding attributes. If it is an application, replace com.mydomain.mypackage with the actual package name of your application (application name excluded). If it is a library you must use "http://schemas.android.com/apk/res-auto" (and be using Tools R17 or later) or you will get runtime errors.
A couple of notes:
It seems you don't need to call the "refreshDrawableState" function, at least the solution works well as is, in my case
In order to use your custom class in a layout xml file, you will have to specify the fully qualified name (e.g. com.mydomain.mypackage.FoodButton)
You can as weel mix-up standard states (e.g. android:pressed, android:enabled, android:selected) with custom states, in order to represent more complicated state combinations
This thread shows how to add custom states to buttons and the like. (If you can't see the new Google groups in your browser, there's a copy of the thread here.)
Please do not forget to call refreshDrawableState within UI thread:
mHandler.post(new Runnable() {
#Override
public void run() {
refreshDrawableState();
}
});
It took lot of my time to figure out why my button is not changing its state even though everything looks right.

Categories

Resources