Custom View. attrs array - android

I have a custom view with 4 rectangles in it. I want to make this view configurable from xml.
Is it possible to make it like:
custom:rectColor="red|cyan|blue|green" //each color correspond rectangle
with
<attr name="rectColor" format="color">
<flag name="red" value="2" />
<flag name="cyan" value="5" />
<flag name="blue" value="7" />
<flag name="green" value="9" />
</attr>
Or it must be:
<attr name="firstRectColor" format="color" />
<attr name="secondRectColor" format="color" />
<attr name="thirdRectColor" format="color" />
<attr name="fourthRectColor" format="color" />
and each view should contains:
custom:firstRectColor="red"
custom:secondRectColor="red"
custom:thirdRectColor="red"
custom:fourthRectColor="red"
And if it possible. How can I get set of color values from attributes.
Thanks

Read this documentation.. it shows you how to declare custom attributes and access them in your layout file..
http://developer.android.com/training/custom-views/create-view.html

Related

Share custom view attribute between views but with different type (int vs float)

I've written a number picker that supports Integer and Float - therefore I've written a generic base class and define two derived typed class like NumberPickerInt and NumberPickerFloat. Those custom views do share a set of common xml settings but also a set of settings, that should be named the same but have different types, like following example shows:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<attr name="mnp_prefix" format="string|reference" />
<attr name="mnp_suffix" format="string|reference" />
<attr name="mnp_editTextStyle" format="reference" />
<attr name="mnp_buttonWidth" format="dimension" />
<attr name="mnp_iconUp" format="reference" />
<attr name="mnp_iconDown" format="reference" />
<attr name="mnp_iconUpLarge" format="reference" />
<attr name="mnp_iconDownLarge" format="reference" />
<attr name="mnp_longPressRepeatClicks" format="boolean" />
<attr name="mnp_style" format="enum">
<enum name="input" value="0" />
<enum name="scroll" value="1" />
</attr>
<declare-styleable name="MaterialNumberPickerInteger">
<attr name="mnp_valueInt" format="integer" />
<attr name="mnp_stepSizeInt" format="integer" />
<attr name="mnp_stepSizeLargeInt" format="integer" />
<attr name="mnp_minInt" format="integer" />
<attr name="mnp_maxInt" format="integer" />
<attr name="mnp_prefix" />
<attr name="mnp_suffix" />
<attr name="mnp_editTextStyle" />
<attr name="mnp_buttonWidth" />
<attr name="mnp_iconUp" />
<attr name="mnp_iconDown" />
<attr name="mnp_iconUpLarge" />
<attr name="mnp_iconDownLarge" />
<attr name="mnp_longPressRepeatClicks" />
<attr name="mnp_orientation" />
<attr name="mnp_style" />
<attr name="android:background" />
<attr name="android:orientation" />
</declare-styleable>
<declare-styleable name="MaterialNumberPickerFloat">
<attr name="mnp_valueFloat" format="float" />
<attr name="mnp_stepSizeFloat" format="float" />
<attr name="mnp_stepSizeLargeFloat" format="float" />
<attr name="mnp_minFloat" format="float" />
<attr name="mnp_maxFloat" format="float" />
<attr name="mnp_prefix" />
<attr name="mnp_suffix" />
<attr name="mnp_editTextStyle" />
<attr name="mnp_buttonWidth" />
<attr name="mnp_iconUp" />
<attr name="mnp_iconDown" />
<attr name="mnp_iconUpLarge" />
<attr name="mnp_iconDownLarge" />
<attr name="mnp_longPressRepeatClicks" />
<attr name="mnp_orientation" />
<attr name="mnp_style" />
<attr name="mnp_commas" format="integer" />
<attr name="android:background" />
<attr name="android:orientation" />
</declare-styleable>
<attr name="pickerStyle" format="reference" />
</resources>
Question
As you can see, I've defined shared attributes like mnp_prefix and mnp_suffix which is easy because they are strings for both views. As you can image, a number picker has a value, so I defined a mnp_valueInt for the integer number picker and mnp_valueFloat for the float number picker - what I would prefer though is to define mnp_float in both views but with different formats - inter or float, depending on the picker type. BUT if I do this, I get compilation errors because mnp_value is defined multiple times. Is there a more beautiful solution than mine for this problem?

android declare-styleable attr format like an alias

in some attrs.xml
<declare-styleable name="ListItemDescNavigation">
<attr name="descGravity" format="I want refer format from android:gravity"/>
</declare-styleable>
I don't want decleare the android:gravity agiain. Like this:
<declare-styleable name="ListItemDescNavigation">
<attr name="descGravity">
<!-- value from andriod:gravity --->
<flag name="top" value="0x30" />
<!-- Push object to the bottom of its container, not changing its size. -->
<flag name="bottom" value="0x50" />
<-- ... have some more like abvoe -->
</attr>
</declare-styleable>
How can I declare descGravity like an alias of android:gravity...

What's the point of declare-styleable?

Suppose I am making some new views with styleable attributes. I declare them thusly (this is how the documentation says to do it:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="TriangleView">
<attr name="direction">
<enum name="NE" value="0" />
<enum name="NW" value="1" />
<enum name="SW" value="2" />
<enum name="SE" value="3" />
</attr>
</declare-styleable>
<declare-styleable name="BannerView">
<attr name="direction">
<enum name="NE" value="0" />
<enum name="NW" value="1" />
<enum name="SW" value="2" />
<enum name="SE" value="3" />
</attr>
<attr name="thickness" format="dimension" />
</declare-styleable>
</resources>
However, this won't work because all attributes are apparently in the same namespace, and I get the error Error: Attribute "direction" has already been defined.
So apparently I have to move the apparently duplicated attributes outside the <declare-styleable> like this:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<attr name="direction">
<enum name="NE" value="0" />
<enum name="NW" value="1" />
<enum name="SW" value="2" />
<enum name="SE" value="3" />
</attr>
<declare-styleable name="BannerView">
<attr name="thickness" format="dimension" />
</declare-styleable>
</resources>
But this poses two questions:
If this works, what exactly is the point of <declare-styleable>?
What if I want the attribute to behave differently in different views? For example if BannerView's direction can only be up or down.
What exactly is the point of <declare-styleable>?
<declare-stylable> tags let you declare attributes for your custom views that you can then set for those views in xml. There are really 3 parts to using the attribute:
Declare an <attr> inside of a <declare-stylable> tag.
Define a custom namespace in your xml layout pointing to your app package name (ex. app). Use the custom attribute in your layout (ex. app:direction="NW").
In your custom view, override the constructors with an AttributeSet parameter, get a TypedArray and read the custom attributes, if any, from it and then within the constructor tell the view how to use those attributes appropriately.
What if I want the attribute to behave differently in different views?
For example if BannerView's direction can only be up or down.
Try something like this:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<attr name="direction">
<enum name="NE" value="0" />
<enum name="NW" value="1" />
<enum name="SW" value="2" />
<enum name="SE" value="3" />
</attr>
<declare-styleable name="TriangleView">
<attr name="direction" />
</declare-styleable>
<declare-styleable name="BannerView">
<attr name="direction" />
<attr name="thickness" format="dimension" />
</declare-styleable>
</resources>
When you build your xml layout for TriangleView or BannerView, you can use the app:direction="NW" example for both. In the constructors with AttributeSet in TriangleView or BannerView, the attributes will have the same format as the original, but what you do with that value is dependent on your implementation of the constructors in each respective view (can be the same or different for both).
If you want attributes to be defined differenly (ie. different "format" or "enum") for different views, then you have to create different attributes with different names.

Correct format for android:gravity in attrs.xml?

I have the following situation:
in styles.xml:
<style name="fooStyle">
<item name="android:padding">?fooView.padding</item>
<item name="android:background">?fooView.background</item>
<item name="android:gravity">?fooView.gravity</item>
</style>
in attrs.xml:
<attr name="fooView.padding" format="dimension" />
<attr name="fooView.background" format="color|reference" />
<attr name="fooView.gravity" format="????"/>
in themes.xml:
<style name="fooViewTheme" parent="android:Theme">
<item name="fooView.padding" >2dip</item>
<item name="fooView.background" >#AA000000</item>
<item name="fooView.gravity">right|bottom</item>
</style>
The problem is that I cannot figure out what the format for the fooView.gravity should be. I've already tried with string, enum and flag but none seem to work: I always get a java.lang.NumberFormatException: unable to parse 'right|bottom' as integer as soon as the view that uses this theme gets loaded.
All answers are appreciated.
Those are the gravity values used by Android. You can use that in your attrs.xml:
<resources>
<declare-styleable name="MyCustomView">
<attr name="gravity">
<flag name="bottom" value="80" />
<flag name="center" value="17" />
<flag name="center_horizontal" value="1" />
<flag name="center_vertical" value="16" />
<flag name="clip_horizontal" value="8" />
<flag name="clip_vertical" value="128" />
<flag name="end" value="8388613" />
<flag name="fill" value="119" />
<flag name="fill_horizontal" value="7" />
<flag name="fill_vertical" value="112" />
<flag name="left" value="3" />
<flag name="right" value="5" />
<flag name="start" value="8388611" />
<flag name="top" value="48" />
</attr>
</declare-styleable>
</resources>
In your layout XML you can use it this way:
<MyCustomView
custom:gravity="center|bottom"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
And in Java code you can read the value using this:
int gravity = a.getInt(R.styleable.MyCustomView_gravity, Gravity.NO_GRAVITY);
and directly set it to a sub view, if that makes sense for you:
someSubView.setGravity(gravity);
You can look up those gravity values in the source of android.view.Gravity or here
It's a flag attribute. You need to define it in your attributes xml like this:
<attr name="gravity">
<flag name="right" value="0x01" />
<flag name="bottom" value="0x02" />
<flag name="left" value="0x04" />
<!-- etc. -->
</attr>
...and then access the values using bit masks, like this:
boolean right = (array.getInt(R.styleable.fooView_gravity, 0) & 0x01) == 0x01;
boolean bottom = (array.getInt(R.styleable.fooView_gravity, 0) & 0x02) == 0x02;
boolean left = (array.getInt(R.styleable.fooView_gravity, 0) & 0x04) == 0x04;
// etc.
...and of course, these values can be used from XML as well:
<style name="fooViewTheme" parent="android:Theme">
<item name="fooView.gravity">right|bottom</item>
</style>
As you only need a small selection of attributes, I'd suggest using the type "enum".
http://developer.android.com/training/custom-views/create-view.html
<attr name="labelPosition" format="enum">
<enum name="left" value="0"/>
<enum name="right" value="1"/>
</attr>
Referencing:
custom:labelPosition="left"
In Code:
mTextPos = a.getInteger(R.styleable.PieChart_labelPosition, 0);
Seems to be integer? Doc.
Would make sense since it can be 'ored'
Edit: sorry, probably misread the problem... It's clearly saying in the error that it's int, but bottom can't be converted to int :/
Edit2: Here are the int values if you need to work-around. Sorry for no real solution.

Format attribute value "android:drawable" not valid

I'm trying to create custom attributes to my button but I dont know which format I must use to images in attributes declaration...
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="TCButton">
<attr name="Text" format="string"/>
<attr name="BackgroundImage" format="android:drawable" />
</declare-styleable>
</resources>
Error is in the format="android:drawable"...
You can use format="integer", the resource id of the drawable, and AttributeSet.getDrawable(...).
Here is an example.
Declare the attribute as integer in res/values/attrs.xml:
<resources>
<declare-styleable name="MyLayout">
<attr name="icon" format="integer" />
</declare-styleable>
</resources>
Set the attribute to a drawable id in your layout:
<se.jog.MyLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
myapp:icon="#drawable/myImage"
/>
Get the drawable from the attribute in your custom widget component class:
ImageView myIcon;
//...
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MyLayout);
Drawable drawable = a.getDrawable(R.styleable.MyLayout_icon);
if (drawable != null)
myIcon.setBackgroundDrawable(drawable);
To see all options possible check the android src here
I think it will be better to use it as a simple reference:
<declare-styleable name="TCButton">
<attr name="customText" format="string"/>
<attr name="backgroundImage" format="reference" />
</declare-styleable>
And set it in your xml like this:
<your.package.name.TCButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
custom:customText="Some custom text"
custom:backgroundImage="#drawable/myImage"
/>
And in your class set the attributes like this:
public TCButton(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.MembershipItemView, 0, 0);
String customText;
Drawable backgroundImage;
try {
customText = a.getString(R.styleable.TCButton_customText);
backgroundImage = a.getDrawable(R.styleable.TCButton_backgroundImage);
} finally {
a.recycle();
}
if(!TextUtils.isEmpty(customText)) {
((TextView)findViewById(R.id.yourTextView)).setText(customText);
}
if(null != backgroundImage) {
((ImageView)findViewById(R.id.yourImageView)).setBackgroundDrawable(backgroundImage);
}
}
PS:
Don't forget to add this line for the root element of the layout you are using your custom view in
xmlns:custom="http://schemas.android.com/apk/res-auto"
If you don't set this, you won't be able to access your custom attributes.
From AOSP code, I found how google engineers declare ImageView#src attr.
<declare-styleable name="ImageView">
<attr name="src" format="reference|color" />
<attr name="scaleType">
<enum name="matrix" value="0" />
<enum name="fitXY" value="1" />
<enum name="fitStart" value="2" />
<enum name="fitCenter" value="3" />
<enum name="fitEnd" value="4" />
<enum name="center" value="5" />
<enum name="centerCrop" value="6" />
<enum name="centerInside" value="7" />
</attr>
<attr name="adjustViewBounds" format="boolean" />
<attr name="maxWidth" format="dimension" />
<attr name="maxHeight" format="dimension" />
<attr name="tint" format="color" />
<attr name="baselineAlignBottom" format="boolean" />
<attr name="cropToPadding" format="boolean" />
<attr name="baseline" format="dimension" />
<attr name="drawableAlpha" format="integer" />
<attr name="tintMode" />
</declare-styleable>
Above code is a sample and it can cover most case in our development.

Categories

Resources