How to read a TextAppearance attribute programmatically [duplicate] - android

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)

Related

Making buttons mixed case on Android from Xamarin Forms

I am new to Xamrin. I am attempting to build a Xamarin Forms app (as little native as possible) for running on both Android and IOS.
I am running in the Android Emulator and by buttons all have uppercase text even though I'm specifying text in mixed case.
I have found a number of pages that say to add the following to styles.xml:
<item name="android:textAllCaps">false</item>
and another that referenced:
<item name="textAllCaps">false</item>
It looks like, though, that there's been some changes according to the docs in styles.xml, so I added it and it now looks like this:
<?xml version="1.0" encoding="utf-8" ?>
<resources>
<style name="MainTheme" parent="MainTheme.Base">
<!-- As of Xamarin.Forms 4.6 the theme has moved into the Forms binary -->
<!-- If you want to override anything you can do that here. -->
<!-- Underneath are a couple of entries to get you started. -->
<!-- Set theme colors from https://aka.ms/material-colors -->
<!-- colorPrimary is used for the default action bar background -->
<!--<item name="colorPrimary">#2196F3</item>-->
<!-- colorPrimaryDark is used for the status bar -->
<!--<item name="colorPrimaryDark">#1976D2</item>-->
<!-- colorAccent is used as the default value for colorControlActivated
which is used to tint widgets -->
<!--<item name="colorAccent">#FF4081</item>-->
<item name="textAllCaps">false</item>
<item name="android:textAllCaps">false</item>
</style>
</resources>
But when I run the emulator, it shows all button text in all uppercase.
I don't know what else to try.
You can create the Custom Renderer for button and set the SetAllCaps to false and try.
Here is the Code:
public class CustomButtonRenderer : ButtonRenderer
{
public CustomButtonRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Button> e)
{
base.OnElementChanged(e);
if (e.OldElement != null)
{
// Cleanup
}
if (e.NewElement != null)
{
var button = (Button)this.Control;
button.SetAllCaps(false);
}
}
}
I made a test and found the issue is related to the version of Xamarin.Forms.
If you are using Xamarin.Forms 4.6 , the solution you mentioned before does the trick , we just need to add <item name="android:textAllCaps">false</item> into style.xml .
If you're using the latest version(4.8) , the solution above does not work , we need to create custom renderer for the Button , pls copy the code into your android project.
[assembly: ExportRenderer(typeof(Xamarin.Forms.Button), typeof(MyRenderer))]
namespace YourNameSpace.Droid
{
class MyRenderer : ButtonRenderer
{
public MyRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.Button> e)
{
base.OnElementChanged(e);
if (e.OldElement != null)
{
// Cleanup
}
if (e.NewElement != null)
{
Control.SetAllCaps(false);
}
}
}
}

Android: Change color of Switch that is added dynamically

Android beginner here, so please bear with me...
I'm using a drawer where the menu items are added dynamically.Currently, this is what my code looks like:
val menu = nav_view.menu
menu.clear()
val selectedCatalogIsEmpty = selectedCatalogs.isEmpty()
for (catalog in catalogs){
val menuItem = menu.add(R.id.catalog_items, Menu.FIRST + catalog.catalogId, Menu.NONE, catalog.catalogName)
val switch = Switch(applicationContext)
menuItem.actionView = switch
if(selectedCatalogIsEmpty ||
selectedCatalogs.contains(catalog.catalogId) ) {
menuItem.isChecked = true
switch.isChecked = true
if(selectedCatalogIsEmpty){
selectedCatalogs.add(catalog.catalogId)
}
}
switch.setOnCheckedChangeListener { _, isChecked -> menuItem.isChecked = isChecked }
}
val menuItemSettings = menu.add(R.id.settings, Menu.NONE+ 5000, Menu.NONE, "Settings" )
Now, what i'd like to do is change the color of the thum when in the selected state. In order to achieve that, I've added the following to the styles.xml file:
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">#color/colorPrimary</item>
<item name="colorPrimaryDark">#color/colorPrimaryDark</item>
<item name="colorAccent">#color/colorPrimary</item>
<item name="colorControlNormal">#color/colorWhite</item>
<item name="colorControlActivated">#color/colorPrimary</item>
</style>
Unfortunately, I'm still getting the wrong color during runtime. Instead of the blue. I'm getting a greeny thumb:
It's clear that I've completely missed the point...I've run a couple of searches and people suggest using the SwitchCompat instead of the Switch. I've tried doing that, but I must also be missing something because I've ended up seing the text in really small caps (instead of the thumb I get with the Switch view).
Thanks.
Regards,
Luis
Ok, so after more than 3 hours, I've finally found my bug: I was using the applicationContext to initialize the Switch and application's theme isn't initialized: it's only used to apply a default theme for the remaining activities. So, updating the Switch instantiation to something like this solves the problem:
val switch = Switch(this#MainActivity) //kotlin ref to my activity

Change TimePicker text color

I've been trying to change the textcolor of my timepicker. But I can't find where the parent style is located. I've tried both
<style name="MyTimePicker" parent="#android:style/Widget.Holo.TimePicker">
<item name="android:textColor">#color/text</item>
</style>
and
<style name="MyTimePicker" parent="#android:style/Widget.TimePicker">
<item name="android:textColor">#color/text</item>
</style>
My minSdkVersion is 15. My targetSdkVersion is 20. I have rebuilded and cleaned my project.
I think I've been through every similar question on SO and none of them really have provided a solution for me. The only answer that might work is using some sort of library, but I'm not a big fan of that solution. Is the path to the parent something different from what I'm using, because I'm pretty sure I should be able to access it somehow?
Edit
This is how the theme is applied;
<TimePicker
style="#style/MyTimePicker"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/timePicker"
android:layout_below="#+id/textView"
android:layout_centerHorizontal="true" />
On a note this is the error I receive (forgot to place it before):
Error:Error retrieving parent for item: No resource found that matches the given name '#android:style/Widget.Holo.TimePicker'.
Edit 2
A couple of the questions I've viewed to try to solve this:
How can I override TimePicker to change text color - I think this question gets as close to an answer, but I'm not entirely sure what I need to do? Do I need to import the android TimePicker style into my project?
https://stackoverflow.com/questions/24973586/set-textcolor-for-timepicker-in-customized-app-theme - No answer is given.
How can i change the textcolor of my timepicker and datepicker? - Tried the 0 votes answer, but it didn't work.
How to change the default color of DatePicker and TimePicker dialog in Android? - Again can't find the TimePicker in a similar way.
Android - How do I change the textColor in a TimePicker? - Again, can't find the actual TimePicker parent.
These are probably the best questions/answers to my problem, but none of them help me. It would be nice to get a definitive answer on this.
I have combined Paul Burke's Answer and Simon's Answer to succesfully edit the text colour of the TimePicker.
Here's how it is accomplished:
TimePicker time_picker; //Instantiated in onCreate()
Resources system;
private void set_timepicker_text_colour(){
system = Resources.getSystem();
int hour_numberpicker_id = system.getIdentifier("hour", "id", "android");
int minute_numberpicker_id = system.getIdentifier("minute", "id", "android");
int ampm_numberpicker_id = system.getIdentifier("amPm", "id", "android");
NumberPicker hour_numberpicker = (NumberPicker) time_picker.findViewById(hour_numberpicker_id);
NumberPicker minute_numberpicker = (NumberPicker) time_picker.findViewById(minute_numberpicker_id);
NumberPicker ampm_numberpicker = (NumberPicker) time_picker.findViewById(ampm_numberpicker_id);
set_numberpicker_text_colour(hour_numberpicker);
set_numberpicker_text_colour(minute_numberpicker);
set_numberpicker_text_colour(ampm_numberpicker);
}
private void set_numberpicker_text_colour(NumberPicker number_picker){
final int count = number_picker.getChildCount();
final int color = getResources().getColor(R.color.text);
for(int i = 0; i < count; i++){
View child = number_picker.getChildAt(i);
try{
Field wheelpaint_field = number_picker.getClass().getDeclaredField("mSelectorWheelPaint");
wheelpaint_field.setAccessible(true);
((Paint)wheelpaint_field.get(number_picker)).setColor(color);
((EditText)child).setTextColor(color);
number_picker.invalidate();
}
catch(NoSuchFieldException e){
Log.w("setNumberPickerTextColor", e);
}
catch(IllegalAccessException e){
Log.w("setNumberPickerTextColor", e);
}
catch(IllegalArgumentException e){
Log.w("setNumberPickerTextColor", e);
}
}
}
Please note that this answer might be outdated by now. I ran into this a while ago with something that might have been buggy (see my question for more details). Otherwise you should probably follow Vikram's answer.
Not sure why you would need to dive into Java Reflection API for this. Its a simple styling matter. The attribute that you need to override is: textColorPrimary.
<style name="AppTheme" parent="#android:style/Theme.Holo.Light">
....
<item name="android:textColorPrimary">#ff0000</item>
</style>
If you're using the TimePicker inside a Dialog, override android:textColorPrimary in the dialog's theme.
That's about it.
A TimePicker is really just two NumberPickers. Looking into the Widget.NumberPicker style and layout, you'll find the it uses
#style/TextAppearance.Large.Inverse.NumberPickerInputText
Unfortunately, TextAppearance.Large.Inverse.NumberPickerInputText doesn't use one of the attributes that you can set in your theme. So you have two options:
Copy the necessary classes to make your own version of NumberPicker and TimePicker. (You might be able to extract something from libraries like HoloEverywhere)
Use hacks.
If you want to go the second route, you can do this:
private int mNumberPickerInputId = 0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Resources system = Resources.getSystem();
// This is the internal id of the EditText used in NumberPicker (hack)
mNumberPickerInputId =
system.getIdentifier("numberpicker_input", "id", "android");
// just used for full example, use your TimePicker
TimePicker timePicker = new TimePicker(this);
setContentView(timePicker);
final int hourSpinnerId =
system.getIdentifier("hour", "id", "android");
View hourSpinner = timePicker.findViewById(hourSpinnerId);
if (hourSpinner != null) {
setNumberPickerTextColor(hourSpinner, Color.BLUE);
}
final int minSpinnerId =
system.getIdentifier("minute", "id", "android");
View minSpinner = timePicker.findViewById(minSpinnerId);
if (minSpinner != null) {
setNumberPickerTextColor(minSpinner, Color.BLUE);
}
final int amPmSpinnerId =
system.getIdentifier("amPm", "id", "android");
View amPmSpinner = timePicker.findViewById(amPmSpinnerId);
if (amPmSpinner != null) {
setNumberPickerTextColor(amPmSpinner, Color.BLUE);
}
}
private void setNumberPickerTextColor(View spinner, int color) {
TextView input = (TextView) spinner.findViewById(mNumberPickerInputId);
input.setTextColor(color);
}
EDIT
Upon further investigation, this hack doesn't really work well. It won't allow you to change the color of the NumberPicker above/below values. The color also resets after the use interacts with it. It seems that your only option will be to create your own copies of the necessary classes (option #1 above).
Adding to Vikram answer , Set the theme to timePicker do not set it as style
in styles.xml
<style name="timePickerOrange">
<item name="android:textColorPrimary">#color/orange</item> <!-- color for digits -->
<item name="android:textColor">#color/orange</item> <!-- color for colon -->
<item name="android:colorControlNormal">#color/orange</item> <!-- color for (horizontal) delimeters -->
</style>
and for timePicker
<TimePicker
android:theme="#style/timePickerOrange"
android:id="#+id/timePicker_defaultTime"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fontFamily="#font/hero_regular"
android:timePickerMode="spinner"
app:fontFamily="#font/hero_regular" />
#Vikram is right
This is so simple.You can try this way
<style name="abirStyle" parent="#android:style/Theme.Holo.Light.Dialog.NoActionBar">
<item name="android:textColor">#color/colorPrimary</item>
<item name="android:background">#null</item>
<item name="android:textColorPrimary">#color/colorPrimary</item>
</style>
You can try to set android:textColorPrimaryInverse and android:textColorSecondaryInverse. That should do the job without using Reflection.
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:textColorPrimaryInverse">#android:color/black</item>
<item name="android:textColorSecondaryInverse">#color/colorLightGrey</item>
</style>
For those using the spinner, this is an easy way to change the colors and even provides the appropriate shadow effect of the previous and next numbers. Spinner requires a theme because most of the default xml attributes for colors only effect the clock mode.
styles.xml
<style name="timepicker">
<item name="android:textColorSecondary">#color/white </item>
<item name="android:textColorPrimary">#color/white </item>
</style>
xml layout file
<TimePicker
android:id="#+id/timePicker1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:theme="#style/timepicker"
android:layout_below="#id/line1"
android:layout_centerHorizontal="true"
android:padding="15dp"
android:timePickerMode="spinner" />

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

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

Read Newer Theme Attributes On Older Platform

I am trying to read attribute values from themes and styles which were designed for platforms that are newer than I am running my application on.
Please don't ask why. If you know anything about the libraries I write then you should already know that I like to push the capabilities of the platform :)
I am operating under the presumption that when Android styles are compiled the attribute constants are what is used for the keys and therefore should theoretically be able to be read on any platform somehow. This is what I have observed to be happening with layout XMLs in my other libraries with no trouble.
Here is a base test case which shows the problem. This should be compiled using Android 3.0+.
<resources>
<style name="Theme.BreakMe">
<item name="android:actionBarStyle">#style/Widget.BreakMe</item>
</style>
<style name="Widget.BreakMe" parent="android:Widget">
<item name="android:padding">20dp</item>
</style>
</resources>
The fact that this uses android:actionBarStyle specifically is irreleveant. All that should be understood is that its an attribute which was only available starting with Android 3.0.
Here are the way that I have tried to access these values thus far on platforms prior to Android 3.0.
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Break Me"
style="?android:attr/actionBarStyle"
/>
and
<declare-styleable name="Whatever">
<item name="datStyle" format="reference" />
</declare-styleable>
<style name="Theme.BreakMe.Take2">
<item name="datStyle">?android:attr/actionBarSize</item>
</style>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Break Me"
style="?attr/datStyle"
/>
and
TypedValue outValue = new TypedValue();
context.getTheme().resolveAttribute(android.R.attr.actionBarStyle, outValue, true);
and
int[] Theme = new int[] { android.R.attr.actionBarSize };
int Theme_actionBarSize = 0;
TypedArray a = context.obtainStyledAttributes(attrs, Theme);
int ref = a.getResourceId(Theme_actionBarSize, 0);
and
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ActionBar, android.R.attr.actionBarStyle, 0);
All of them result in this error in LogCat:
E/ResourceType(5618): Style contains key with bad entry: 0x010102ce
The 0x010102ce constant is the attribute value of android.R.attr.actionBarStyle which seems to indicate the platform is rejecting the attribute before I can even get a chance to access its value.
I am looking for any other way to read attributes like this from the Theme. I'm fairly sure that once I've obtained the style reference I won't have trouble reading its attributes.
Is there any possible way to do this?
I am operating under the presumption that when Android styles are compiled the attribute constants are what is used for the keys and therefore should theoretically be able to be read on any platform somehow.
Possibly, though that is not how I am interpreting the C++ source code that raises the error you are seeing. Check out ResTable::Theme::applyStyle() in frameworks/base/libs/utils/ResourceTypes.cpp.
My interpretation is that Android has what amounts to an in-memory table of packages->types->possible entries:
numEntries = curPI->types[t].numEntries;
Your entry index is higher than the highest known entry:
if (e >= numEntries) {
LOGE("Style contains key with bad entry: 0x%08x\n", attrRes);
bag++;
continue;
}
It is possible that they handle this different for android versus other packages -- android uses known values at firmware build time (and your generated entry index is higher, because it is from a newer platform), non-android ones assume anything's valid.
If my guesswork is correct, what you want to do will not work. That being said, my C++ days are seriously in my rear-view mirror, so I may be misinterpreting what I'm seeing.
Perhaps I'm missing the end goal here, but I put together the following example that was able to read out all the attributes without issue on any 2.x device. The example was compiled against a 3.0 targetSdk.
styles.xml (Declare the styles and themes)
<resources>
<style name="Theme.NewFeatures" parent="android:Theme">
<item name="android:actionBarStyle">#style/Widget.MyActionBar</item>
</style>
<style name="Widget.MyActionBar" parent="android:Widget">
<item name="android:padding">20dp</item>
</style>
</resources>
attrs.xml (Declare the attribute groups you wish to obtain at runtime)
<resources>
<declare-styleable name="ActionBarNewFeatures">
<attr name="android:actionBarStyle" />
</declare-styleable>
<declare-styleable name="MyWidgetNewFeatures">
<attr name="android:padding" />
</declare-styleable>
</resources>
AndroidManifest.xml (Apply the custom theme)
<application
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/Theme.NewFeatures" >
<activity
android:name=".SomeActivity"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
SomeActivity.java (Go digging for attributes)
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
TypedArray a = obtainStyledAttributes(R.styleable.ActionBarNewFeatures);
//Get the style ID for the widget
int resid = a.getResourceId(R.styleable.ActionBarNewFeatures_android_actionBarStyle, -1);
a.recycle();
a = obtainStyledAttributes(resid, R.styleable.MyWidgetNewFeatures);
int padding = a.getDimensionPixelSize(R.styleable.MyWidgetNewFeatures_android_padding, -1);
a.recycle();
TextView tv = new TextView(this);
tv.setText(String.format("Padding will be %d px", padding));
setContentView(tv);
}
As long as I compile the example against 3.0 so it can resolved all the attribute names; on every 2.X device/emulator I have this will correctly read into the theme and then into the widget style to get the scaled padding dimension I had set.
Hope I didn't miss something big.
Probably, You must define a few themes. For old devices use folder res/values-v11/themes.xml. See section "Using Holo while supporting Android 2.x" in the http://android-developers.blogspot.com/2012/01/holo-everywhere.html

Categories

Resources