-
Notifications
You must be signed in to change notification settings - Fork 91
Custom View Attributes
In addition to supporting the application of custom attributes, Paris helps get rid of the boilerplate associated with adding custom attributes to your views in the first place.
Custom attributes are declared in XML as you normally would:
<declare-styleable name="MyView">
<attr name="title" format="string" />
<attr name="image" format="reference" />
<attr name="imageSize" format="dimension" />
</declare-styleable>
Next we'll use two annotations to give our custom view information about its attributes.
-
@Styleable
is a class annotation that references the name of our styleable declaration -
@Attr
is a method annotation that references a particular styleable attribute
When Paris is used to apply either an AttributeSet
or a style resource to our custom view, the annotated methods will automatically be called with the corresponding attribute value.
Here's an example:
// The value here corresponds to the name chosen in declare-styleable.
@Styleable("MyView")
class MyView(…) : ViewGroup(…) {
init {
// This call enables the custom attributes when used in XML layouts. It
// extracts styling information from AttributeSet like it would a StyleRes.
style(attrs)
}
@Attr(R.styleable.MyView_title)
fun setTitle(title: String) {
// Automatically called with the title value (if any) when an AttributeSet
// or StyleRes is applied to the MyView instance.
}
@Attr(R.styleable.MyView_image)
fun setImage(image: Drawable?) {
// Automatically called with the image value (if any) when an AttributeSet
// or StyleRes is applied to the MyView instance.
}
@Attr(R.styleable.MyView_imageSize)
fun setImageSize(@Px imageSize: Int) {
// Automatically called with the imageSize value (if any) when an
// AttributeSet or StyleRes is applied to the MyView instance.
}
}
Click to see the example in Java.
// The value here corresponds to the name chosen in declare-styleable.
@Styleable("MyView")
public class MyView extends ViewGroup {
public MyView(Context context) {
super(context);
}
public MyView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public MyView(Context context, AttributeSet attrs, int defStyle) {
this(context, attrs, defStyle);
// This call enables the custom attributes when used in XML layouts. It
// extracts styling information from AttributeSet like it would a StyleRes.
Paris.style(this).apply(attrs);
}
@Attr(R.styleable.MyView_title)
public void setTitle(String title) {
// Automatically called with the title value (if any) when an AttributeSet
// or StyleRes is applied to the MyView instance.
}
@Attr(R.styleable.MyView_image)
public void setImage(Drawable image) {
// Automatically called with the image value (if any) when an AttributeSet
// or StyleRes is applied to the MyView instance.
}
@Attr(R.styleable.MyView_imageSize)
public void setImageSize(@Px int imageSize) {
// Automatically called with the imageSize value (if any) when an
// AttributeSet or StyleRes is applied to the MyView instance.
}
}
In Kotlin, the annotated functions must be public (default) or internal. In Java, the annotated methods must be public or package-private.
That's it!
We can now use these custom attributes in an XML layout:
<MyView
…
app:title="@string/hello"
app:image="@drawable/beach"
app:imageSize="@dimen/huge" />
Or in an XML style:
<style name="Beach">
<item name="title">@string/hello</item>
<item name="image">@drawable/beach</item>
<item name="imageSize">@dimen/huge</item>
<item name="android:textColor">#FFFF00</item>
</style>
Which can then be applied in XML:
<MyView
style="@style/Beach"
… />
Or programmatically:
myView.style(R.style.Beach)
Click to see the example in Java.
Paris.style(myView).apply(R.style.Beach);
We also have access to these new attributes via the style builders:
myView.style {
title("Hello") // Using an actual value.
image(R.drawable.beach) // Or a resource.
…
}
Click to see the example in Java.
Paris.styleBuilder(myView)
.title("Hello") // Using an actual value.
.image(R.drawable.beach) // Or a resource.
…
.apply();
Attention: Extension functions like title
and image
are generated during compilation by the Paris annotation processor. When new @Attr
annotations are added, the project must be (re)compiled once for the related functions to become available.
Paris converts the attribute value based on the type of the method parameter, as well as the use of @Fraction
and support annotations like @ColorInt
, @ColorRes
, @DimenRes
, all the other @…Res
, and @Px
.
The following conversion table indicates which method parameter types can be used depending on the styleable attribute format (as declared in <attr … format="…" />
).
Attribute Format | Parameter Type |
---|---|
boolean | boolean |
color | @ColorInt int |
dimension | @LayoutDimension int |
dimension | @Px int |
enum | int |
flag | int |
float | float |
fraction | @Fraction(base = ?, pbase = ?) float |
integer | int |
reference | @AnyRes* int |
reference | CharSequence[] |
reference | ColorStateList |
reference | Drawable |
reference | Typeface |
string | CharSequence |
string | String |
string | Typeface |
* Any of the @…Res annotations can be used
Paris doesn't know how to parse attribute values properly unless the method parameters are properly annotated. For example, an attribute declared with format="dimension"
used on a method whose parameter is not annotated with @Px
will be parsed as a normal integer, resulting in an error.