Defining custom attrs - android

I need to implement my own attributes like in com.android.R.attr
Found nothing in official documentation so I need information about how to define these attrs and how to use them from my code.

Currently the best documentation is the source. You can take a look at it here (attrs.xml).
You can define attributes in the top <resources> element or inside of a <declare-styleable> element. If I'm going to use an attr in more than one place I put it in the root element. Note, all attributes share the same global namespace. That means that even if you create a new attribute inside of a <declare-styleable> element it can be used outside of it and you cannot create another attribute with the same name of a different type.
An <attr> element has two xml attributes name and format. name lets you call it something and this is how you end up referring to it in code, e.g., R.attr.my_attribute. The format attribute can have different values depending on the 'type' of attribute you want.
reference - if it references another resource id (e.g, "#color/my_color", "#layout/my_layout")
color
boolean
dimension
float
integer
string
fraction
enum - normally implicitly defined
flag - normally implicitly defined
You can set the format to multiple types by using |, e.g., format="reference|color".
enum attributes can be defined as follows:
<attr name="my_enum_attr">
<enum name="value1" value="1" />
<enum name="value2" value="2" />
</attr>
flag attributes are similar except the values need to be defined so they can be bit ored together:
<attr name="my_flag_attr">
<flag name="fuzzy" value="0x01" />
<flag name="cold" value="0x02" />
</attr>
In addition to attributes there is the <declare-styleable> element. This allows you to define attributes a custom view can use. You do this by specifying an <attr> element, if it was previously defined you do not specify the format. If you wish to reuse an android attr, for example, android:gravity, then you can do that in the name, as follows.
An example of a custom view <declare-styleable>:
<declare-styleable name="MyCustomView">
<attr name="my_custom_attribute" />
<attr name="android:gravity" />
</declare-styleable>
When defining your custom attributes in XML on your custom view you need to do a few things. First you must declare a namespace to find your attributes. You do this on the root layout element. Normally there is only xmlns:android="http://schemas.android.com/apk/res/android". You must now also add xmlns:whatever="http://schemas.android.com/apk/res-auto".
Example:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:whatever="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<org.example.mypackage.MyCustomView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center"
whatever:my_custom_attribute="Hello, world!" />
</LinearLayout>
Finally, to access that custom attribute you normally do so in the constructor of your custom view as follows.
public MyCustomView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MyCustomView, defStyle, 0);
String str = a.getString(R.styleable.MyCustomView_my_custom_attribute);
//do something with str
a.recycle();
}
The end. :)

Qberticus's answer is good, but one useful detail is missing. If you are implementing these in a library replace:
xmlns:whatever="http://schemas.android.com/apk/res/org.example.mypackage"
with:
xmlns:whatever="http://schemas.android.com/apk/res-auto"
Otherwise the application that uses the library will have runtime errors.

The answer above covers everything in great detail, apart from a couple of things.
First, if there are no styles, then the (Context context, AttributeSet attrs) method signature will be used to instantiate the preference. In this case just use context.obtainStyledAttributes(attrs, R.styleable.MyCustomView) to get the TypedArray.
Secondly it does not cover how to deal with plaurals resources (quantity strings). These cannot be dealt with using TypedArray. Here is a code snippet from my SeekBarPreference that sets the summary of the preference formatting its value according to the value of the preference. If the xml for the preference sets android:summary to a text string or a string resouce the value of the preference is formatted into the string (it should have %d in it, to pick up the value). If android:summary is set to a plaurals resource, then that is used to format the result.
// Use your own name space if not using an android resource.
final static private String ANDROID_NS =
"http://schemas.android.com/apk/res/android";
private int pluralResource;
private Resources resources;
private String summary;
public SeekBarPreference(Context context, AttributeSet attrs) {
// ...
TypedArray attributes = context.obtainStyledAttributes(
attrs, R.styleable.SeekBarPreference);
pluralResource = attrs.getAttributeResourceValue(ANDROID_NS, "summary", 0);
if (pluralResource != 0) {
if (! resources.getResourceTypeName(pluralResource).equals("plurals")) {
pluralResource = 0;
}
}
if (pluralResource == 0) {
summary = attributes.getString(
R.styleable.SeekBarPreference_android_summary);
}
attributes.recycle();
}
#Override
public CharSequence getSummary() {
int value = getPersistedInt(defaultValue);
if (pluralResource != 0) {
return resources.getQuantityString(pluralResource, value, value);
}
return (summary == null) ? null : String.format(summary, value);
}
This is just given as an example, however, if you want are tempted to set the summary on the preference screen, then you need to call notifyChanged() in the preference's onDialogClosed method.

The traditional approach is full of boilerplate code and clumsy resource handling. That's why I made the Spyglass framework. To demonstrate how it works, here's an example showing how to make a custom view that displays a String title.
Step 1: Create a custom view class.
public class CustomView extends FrameLayout {
private TextView titleView;
public CustomView(Context context) {
super(context);
init(null, 0, 0);
}
public CustomView(Context context, AttributeSet attrs) {
super(context, attrs);
init(attrs, 0, 0);
}
public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(attrs, defStyleAttr, 0);
}
#RequiresApi(21)
public CustomView(
Context context,
AttributeSet attrs,
int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(attrs, defStyleAttr, defStyleRes);
}
public void setTitle(String title) {
titleView.setText(title);
}
private void init(AttributeSet attrs, int defStyleAttr, int defStyleRes) {
inflate(getContext(), R.layout.custom_view, this);
titleView = findViewById(R.id.title_view);
}
}
Step 2: Define a string attribute in the values/attrs.xml resource file:
<resources>
<declare-styleable name="CustomView">
<attr name="title" format="string"/>
</declare-styleable>
</resources>
Step 3: Apply the #StringHandler annotation to the setTitle method to tell the Spyglass framework to route the attribute value to this method when the view is inflated.
#HandlesString(attributeId = R.styleable.CustomView_title)
public void setTitle(String title) {
titleView.setText(title);
}
Now that your class has a Spyglass annotation, the Spyglass framework will detect it at compile-time and automatically generate the CustomView_SpyglassCompanion class.
Step 4: Use the generated class in the custom view's init method:
private void init(AttributeSet attrs, int defStyleAttr, int defStyleRes) {
inflate(getContext(), R.layout.custom_view, this);
titleView = findViewById(R.id.title_view);
CustomView_SpyglassCompanion
.builder()
.withTarget(this)
.withContext(getContext())
.withAttributeSet(attrs)
.withDefaultStyleAttribute(defStyleAttr)
.withDefaultStyleResource(defStyleRes)
.build()
.callTargetMethodsNow();
}
That's it. Now when you instantiate the class from XML, the Spyglass companion interprets the attributes and makes the required method call. For example, if we inflate the following layout then setTitle will be called with "Hello, World!" as the argument.
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:width="match_parent"
android:height="match_parent">
<com.example.CustomView
android:width="match_parent"
android:height="match_parent"
app:title="Hello, World!"/>
</FrameLayout>
The framework isn't limited to string resources has lots of different annotations for handling other resource types. It also has annotations for defining default values and for passing in placeholder values if your methods have multiple parameters.
Have a look at the Github repo for more information and examples.

if you omit the format attribute from the attr element, you can use it to reference a class from XML layouts.
example from attrs.xml.
Android Studio understands that the class is being referenced from XML
i.e.
Refactor > Rename works
Find Usages works
and so on...
don't specify a format attribute in .../src/main/res/values/attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="MyCustomView">
....
<attr name="give_me_a_class"/>
....
</declare-styleable>
</resources>
use it in some layout file .../src/main/res/layout/activity__main_menu.xml
<?xml version="1.0" encoding="utf-8"?>
<SomeLayout
xmlns:app="http://schemas.android.com/apk/res-auto">
<!-- make sure to use $ dollar signs for nested classes -->
<MyCustomView
app:give_me_a_class="class.type.name.Outer$Nested/>
<MyCustomView
app:give_me_a_class="class.type.name.AnotherClass/>
</SomeLayout>
parse the class in your view initialization code .../src/main/java/.../MyCustomView.kt
class MyCustomView(
context:Context,
attrs:AttributeSet)
:View(context,attrs)
{
// parse XML attributes
....
private val giveMeAClass:SomeCustomInterface
init
{
context.theme.obtainStyledAttributes(attrs,R.styleable.ColorPreference,0,0).apply()
{
try
{
// very important to use the class loader from the passed-in context
giveMeAClass = context::class.java.classLoader!!
.loadClass(getString(R.styleable.MyCustomView_give_me_a_class))
.newInstance() // instantiate using 0-args constructor
.let {it as SomeCustomInterface}
}
finally
{
recycle()
}
}
}

HERE is the official documentation for creating custom attributes and Views

Related

How to set view attribute from custom attr on a custom Preference class?

I have created a class CustomPreference, which inherits from the androidx Preference class, for the purpose of setting a custom layout made of my own components. And I defined a styleable attr on that custom Preference class. But now I'm trying to understand how to pass along the value from that custom styleable attr on to the attribute on my underlying SpecialCustomComponent.
CustomPreference.java
public class CustomPreference extends Preference {
private Context context;
private AttributeSet attrs;
public CustomPreference(Context context, AttributeSet attrs,
int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
this.context = context;
this.attrs = attrs;
setLayoutResource(R.layout.preference_widget_custom);
}
// other required constructor overloads omitted
#Override
public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
// calling this method here results in an error because
// a.getResourceId(R.styleable.PreferenceWidgetCustom_preferenceTitle, 0)
// returns no resource Id at this point
setPreferenceTitle(holder);
}
#Override
protected void onAttachedToHierarchy(PreferenceManager preferenceManager) {
super.onAttachedToHierarchy(preferenceManager);
this.preferenceManager = preferenceManager;
// alternatively, calling this method here would result in a
// different error, since I don't have access to the viewHolder
// here. To make this work, I tried preserving "viewHolder" in a
// field inside onBindViewHolder (which I don't think I'm
// supposed to do anyways), but the viewHolder reference was
// null, so it didn't work.
setPreferenceTitle();
}
// This is my incorrect attempt at setting "myText" to the value of "preferenceTitle"
private void setPreferenceTitle(PreferenceViewHolder holder) {
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.PreferenceWidgetSwitchCustom, 0, 0);
try {
int preferenceTitleId = a
.getResourceId(R.styleable.PreferenceWidgetCustom_preferenceTitle, 0);
((SpecialCustomComponent) holder.itemView).setText(a.getResources().getString(preferenceTitleId));
}
finally {
a.recycle();
}
}
}
I declared a custom attribute for my CustomPreference in attrs.xml that I am hoping to use to set the underlying "myText" attribute of my custom component in the layout.
attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="PreferenceWidgetCustom">
<attr name="preferenceTitle" format="string" />
</declare-styleable>
</resources>
The layout for each CustomPreference is defined here in preference_widget_custom.xml. Notice the attribute "myText", which is what I would like to set to the value of "preferenceTitle" defined above.
preference_widget_custom.xml
<?xml version="1.0" encoding="utf-8"?>
<SpecialCustomComponent
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:myText="this is the field I would like to be set by 'preferenceTitle'" />
I am using these preferences in a PreferenceScreen layout like this:
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:title="Settings">
<CustomPreference
android:key="pref_one"
app:preferenceTitle="Preference #1"/>
<CustomPreference
android:key="pref_two"
app:preferenceTitle="Preference #2"/>
</PreferenceScreen>
It's generally wrong to provide a string resource as a styleable and that's why you're vastly overcomplicating a simple localization job. Any resource can be accessed with this pattern: [<package_name>.]R.<resource_type>.<resource_name>.
So this would be res/values/strings.xml:
<resources>
<string name="title_preference_01">Title 01</string>
<string name="summary_preference_01">Summary 01</string>
<string name="title_preference_02">Title 02</string>
<string name="summary_preference_02">Summary 02</string>
<resources>
Which then can be accessed throughput the application:
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:title="Settings">
<CustomPreference
android:title="#string/title_preference_01"
android:summary="#string/summary_preference_01"
android:key="pref_one" />
<CustomPreference
android:title="#string/title_preference_02"
android:summary="#string/summary_preference_02"
android:key="pref_two" />
</PreferenceScreen>
Localize your app explains this all in high detail.
Besides, even if it were actual stylable values and not strings commonly used for localization, you'd still need to apply the PreferenceWidgetCustom style to SpecialCustomComponent in order to access it's values.
I solved my problem. As Martin pointed out a styleable is not really intended for strings anyways. As it turns out, using the built-in android:title attribute instead of a custom styleable made things really easy. All I have to do is call this.getTitle() to access its value from my CustomPreference class.
public class CustomPreference extends Preference {
public CustomPreference(Context context, AttributeSet attrs,
int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
setLayoutResource(R.layout.preference_widget_custom);
}
// other required constructor overloads omitted
#Override
public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
setPreferenceTitle();
}
private void setPreferenceTitle() {
((SpecialCustomComponent) holder.itemView).setText(this.getTitle());
}
}

Getting IAttributeSet From View Inflation Process

I'm working on a project that relies on the user adding a custom attribute to their Android layout elements. Just like MvvmCross' app:MvxBind. There are no custom view classes as the idea is that the user can use the normal Android views.
The problem is that in order to get the value of this tag I need to get the IAttributeSet that is used during the view inflation process and I can't find a method of doing so that suits my needs.
I have a working example using LayoutInflater.IFactory however, this requires me to set my own LayoutInflater/factory which, if the user is using a library such as MvvmCross, causes problems as only one factory can be set at once.
I'm looking for a way that I can get the IAttributeSet object whenever a view is inflated to check for my attribute that doesn't interfere with the standard LayoutInflater or LayoutInflater's from other libraries. Or if there is any way to get my attribute after the view has been inflated.
Thanks in advance!
Edit
I want to be able to get the value of MyAttribute from a view without subclassing views or creating custom views. This is easily accomplished with LayoutInflater.IFactory but this method interferes with libraries such as MvvmCross.
<TextView
android:layout_width="wrap_content"
android:layout_width="wrap_content"
app:MyAttribute="My attribute value" />
I'm not sure if I understand what you mean. Please refer to the following:
you could create a custom view and define its attribute in attrs.xml and when it is created from an XML layout, all of the attributes in the XML tag are read from the resource bundle and passed into the view’s constructor as an IAttributeSet
for example,here is a custom view named IconView
public class IconView : View
{
}
define some attributes in attrs.xml(Resources/values/attrs.xml)
<declare-styleable name="IconView">
<attr name="bg_color" format="color" />
<attr name="src" format="integer" />
<attr name="showIconLabel" format="boolean" />
<attr name="iconLabelTextColor" format="color" />
<attr name="iconLabelText" format="string" />
</declare-styleable>
then we could process the attributes in the view constructor when it is created (here define an Initialize method):
public IconView(Context context) : base(context)
{
Initialize(context);
}
public IconView(Context context, IAttributeSet attrs) : base(context, attrs)
{
Initialize(context, attrs);
}
private void Initialize(Context context, IAttributeSet attrs = null)
{
if (attrs != null)
{
// Contains the values set for the styleable attributes you declared in your attrs.xml
var array = context.ObtainStyledAttributes(attrs, Resource.Styleable.IconView, 0, 0);
iconBackgroundColor = array.GetColor(Resource.Styleable.IconView_bg_color, Color.Gray);
iconLabelTextColor = array.GetColor(Resource.Styleable.IconView_iconLabelTextColor, Color.ParseColor("#D9000000"));
_labelText = array.GetString(Resource.Styleable.IconView_iconLabelText);
_showIconLabel = array.GetBoolean(Resource.Styleable.IconView_showIconLabel, false);
var iconResId = array.GetResourceId(Resource.Styleable.IconView_src, 0);
if (iconResId != 0) // If the user actually set a drawable
_icon = AppCompatDrawableManager.Get().GetDrawable(context, iconResId);
// If the users sets text for the icon without setting the showIconLabel attr to true
// set it to true for the user anyways
if (_labelText != null)
_showIconLabel = true;
// Very important to recycle the array after use
array.Recycle();
}
...
}
You can refer to it for more details make custom view
I finally managed to figure this out.
Non-MvvmCross Solution
I stumbled across an article called "Layout Inflater: Friend or Foe?". I think this is the link but at the time of posting this answer it isn't working.
The author did an amazing talk on LayoutInflater and how he changed the Android LayoutInflater process so that he could intercept it for his library Calligraphy. The resulting solution is called ViewPump and it's written in Kotlin.
I have written the ViewPump library in Xamarin for use with non-MvvmCross projects: https://github.com/lewisbennett/viewpump.
MvvmCross Solution
MvvmCross uses a solution based on InflationX' ViewPump to do its binding and we can access it by first creating the below classes:
public class BindingBuilder : MvxAndroidBindingBuilder
{
protected override IMvxAndroidViewBinderFactory CreateAndroidViewBinderFactory()
{
return new ViewBinderFactory();
}
}
public class ViewBinderFactory : IMvxAndroidViewBinderFactory
{
public IMvxAndroidViewBinder Create(object source)
{
return new ViewBinder(source);
}
}
public class ViewBinder : MvxAndroidViewBinder
{
public override void BindView(View view, Context context, IAttributeSet attrs)
{
base.BindView(view, context, attrs);
// Do your intercepting here.
}
public ViewBinder(object source)
: base(source)
{
}
}
Then in your MvxAndroidSetup or MvxAppCompatSetup class add the following:
protected override MvxBindingBuilder CreateBindingBuilder()
{
return new BindingBuilder();
}
Done! I hope this helps someone :)

How to pass custom attributes to nested xml

I have structure like that:
preferences.xml:
...
<com.example.MyCustomPreference
...
myCustomMessage="#string/abc"
android:inputType="..."
... />
...
preference_my_custom.xml:
<LinearLayout ...>
<com.example.MyCustomView
...
app:myCustomMessage="?????"
... />
</LinearLayout>
view_my_custom.xml:
<GridView ...>
...EditTexts, TextViews, etc.
</GridView>
I would like to pass myCustomMessage's value (I omitted other attributes for simplification) from MyCustomPreference to MyCustomView using XML. MyCustomView reads custom attributes, so I would like to avoid reading attributes in MyCustomPreference programmatically, getting TextViews from MyCustomView and setting them values. However, I really don't know what to type in place of "?????".
How can i do this using XML? Is this possible?
You have to do it programmatically (unless you use data binding). For example, in your MyCustomPreference you catch de attribute myCustomMessage:
String myCustomMessage = null;
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.MyCustomPreference, 0, 0);
try {
myCustomMessage = a.getString(R.styleable.MyCustomPreference_myCustomMessage);
} finally {
a.recycle();
}
Here you got the String value of your attribute. Then, I supose you have inflated your MyCustomView inside your MyCustomPreference. As an example:
View.inflate(getContext(), R.layout.preference_my_custom, this);
MyCustomView myCustomView = (MyCustomView) findViewById(R.id.you_custom_view_id);
So, here you can set programmatically your myCustomMessage in your MyCustomView.
myCustomView.setMyCustomMessage(myCustomMessage);
You should create this method to set correctly your text, and if necessary propagate this text to other child views of your MyCustomView.
Now, changing your String resId in your preferences.xml the interface should update as expected.
P.S: Since I don't know all your resource ids, please adapt them to your project.
Create an attribute file for your customeView:
Add in attrs.xml
<declare-styleable name="CustomView">
<attr name="width" format="dimension" />
</declare-styleable>
Used in your customView init:
public CustomView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CustomView, defStyle, 0);
mWidth = a.getDimensionPixelSize(R.styleable.CustomView_width,0);
a.recycle();
}

Read base class xml attribute from custom widget constructor?

I have a class derived from a standard library widget, how can I read one of the base class' xml attributes in the constructor? For example, how would I get the value of "android:layout_height" in the following?:
class MyTextView extends TextView {
public MyTextView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
int layoutHeightParamFromXmlAttributes = ?;
}
}
I'm interested in reading other "android:x" attributes in this way, this is just an example.
Thanks
You can try something like this:
In your widget constructor.
If you get custom attribute then try this.
if (attrs != null) {
TypedArray attributeArray = context.obtainStyledAttributes(attrs,R.styleable.CustomTextView);
//for font
String fontName = attributeArray.getString(R.styleable.CustomTextView_font_name);
}
And if you get default property like height and width then try this:
int width=attributeArray.getLayoutDimension(0,ViewGroup.LayoutParams.WRAP_CONTENT);
int height = attributeArray.getLayoutDimension(1,ViewGroup.LayoutParams.WRAP_CONTENT);//index is based on your xml file
Define style in attrs.xml file
<declare-styleable name="CustomTextView">
<attr name="font_name" format="string" />
</declare-styleable>
In your layout file add this:
<com.package.CustomTextView
android:id="#+id/customFTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:font_name="OpenSans-Regular.ttf"
android:gravity="center"/>
Hope this explanation helps you. :)

How do I use obtainStyledAttributes(int []) with internal Themes of Android

So I have looked around and found out that android.R.styleable is no longer part of the SDK even though it is still documented here.
That wouldn't really be an issue if it was clearly documented what the alternative is. For example the AOSP Calendar App is still using the android.R.styleable
// Get the dim amount from the theme
TypedArray a = obtainStyledAttributes(com.android.internal.R.styleable.Theme);
lp.dimAmount = a.getFloat(android.R.styleable.Theme_backgroundDimAmount, 0.5f);
a.recycle();
So how would one get the backgroundDimAmount without getting the int[] from android.R.styleable.Theme?
What do I have to stick into obtainStyledAttributes(int []) in order to make it work with the SDK?
The CustomView API demo shows how to retrieve styled attributes. The code for the view is here:
https://github.com/android/platform_development/blob/master/samples/ApiDemos/src/com/example/android/apis/view/LabelView.java
The styleable array used to retrieve the text, color, and size is defined in the <declare-styleable> section here:
https://github.com/android/platform_development/blob/master/samples/ApiDemos/res/values/attrs.xml#L24
You can use <declare-styleable> to define any list of attributes that you want to retrieve as a group, containing both your own and ones defined by the platform.
As far as these things being in the documentation, there is a lot of java doc around the styleable arrays that makes them useful to have in the documentation, so they have been left there. However as the arrays change, such as new attributes being added, the values of the constants can change, so the platform ones can not be in the SDK (and please do not use any tricks to try to access them). There should be no need to use the platform ones anyway, because they are each there just for the implementation of parts of the framework, and it is trivial to create your own as shown here.
In the example, they left out the reference to the Context 'c':
public ImageAdapter(Context c) {
TypedArray a = c.obtainStyledAttributes(R.styleable.GalleryPrototype);
mGalleryItemBackground = a.getResourceId(
R.styleable.GalleryPrototype_android_galleryItemBackground, 0);
a.recycle();
return mGalleryItemBackground;
}
Changing obtainStyledAttributes to c.obtainStyledAttributes should work
Example of pulling out standard attribute (background) in a custom view which has its own default style. In this example the custom view PasswordGrid extends GridLayout. I specified a style for PasswordGrid which sets a background image using the standard android attribute android:background.
public class PasswordGrid extends GridLayout {
public PasswordGrid(Context context) {
super(context);
init(context, null, 0);
}
public PasswordGrid(Context context, AttributeSet attrs) {
super(context, attrs, R.attr.passwordGridStyle);
init(context, attrs, 0);
}
public PasswordGrid(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context, attrs, defStyle);
}
private void init(Context context, AttributeSet attrs, int defStyle) {
if (!isInEditMode()) {
TypedArray stdAttrs = context.obtainStyledAttributes(attrs,
new int[] { android.R.attr.background }, // attribute[s] to access
defStyle,
R.style.PasswordGridStyle); // Style to access
// or use any style available in the android.R.style file, such as
// android.R.style.Theme_Holo_Light
if (stdAttrs != null) {
Drawable bgDrawable = stdAttrs.getDrawable(0);
if (bgDrawable != null)
this.setBackground(bgDrawable);
stdAttrs.recycle();
}
}
}
Here is part of my styles.xml file:
<declare-styleable name="passwordGrid">
<attr name="drawOn" format="color|reference" />
<attr name="drawOff" format="color|reference" />
<attr name="pathWidth" format="integer" />
<attr name="pathAlpha" format="integer" />
<attr name="pathColor" format="color" />
</declare-styleable>
<style name="PasswordGridStyle" parent="#android:style/Widget.GridView" >
<!-- Style custom attributes. -->
<item name="drawOff">#drawable/ic_more</item>
<item name="drawOn">#drawable/ic_menu_cut</item>
<item name="pathWidth">31</item>
<item name="pathAlpha">129</item>
<item name="pathColor">#color/green</item>
<!-- Style standard attributes -->
<item name="android:background">#drawable/pattern_bg</item>
</style>
This appears to be a bug in the SDK. I have filed an issue on it, which you may wish to star so as to receive updates on it.
As a worksaround, you can use reflection to access the field:
Class clazz=Class.forName("android.R$styleable");
int i=clazz.getField("Theme_backgroundDimAmount").getInt(clazz);

Categories

Resources