Skip to content

Quick Start

Nathanael Silverman edited this page Oct 24, 2018 · 5 revisions

Applying an XML-Defined Style

myView.style(R.style.MyStyle)
Click to see the example in Java.
Paris.style(myView).apply(R.style.MyStyle);

Where myView is an arbitrary view instance, MyStyle an XML-defined style, and style an extension function provided by Paris. Many but not all attributes are supported, for more see Supported View Types and Attributes.

Combining 2 or More Styles

myView.style {
    add(R.style.StyleA)
    add(R.style.StyleB)
    …
}
Click to see the example in Java.
Paris.styleBuilder(myView)
        .add(R.style.StyleA)
        .add(R.style.StyleB)
        …
        .apply();

In cases where there's some overlap the attribute value from the last style added prevails. For more see Combining Styles.

Defining Styles Programmatically

textView.style {
    // Using an actual value.
    textColor(Color.GREEN)
    // Or a resource.
    textSizeRes(R.dimen.my_text_size_small)
}
Click to see the example in Java.
Paris.styleBuilder(textView)
        // Using an actual value.
        .textColor(Color.GREEN)
        // Or a resource.
        .textSizeRes(R.dimen.my_text_size_small)
        .apply();

Can be combined with style resources as well:

textView.style {
    // Adds all the attributes defined in the MyGreenTextView style.
    add(R.style.MyGreenTextView)
    textSizeRes(R.dimen.my_text_size_small)
}
Click to see the example in Java.
Paris.styleBuilder(textView)
        // Adds all the attributes defined in the MyGreenTextView style.
        .add(R.style.MyGreenTextView)
        .textSizeRes(R.dimen.my_text_size_small)
        .apply();

For more see Defining Styles Programmatically.

Custom View Attributes

Attributes are declared as follows:

<declare-styleable name="MyView">
    <attr name="title" format="string" />
    <attr name="image" format="reference" />
    <attr name="imageSize" format="dimension" />
</declare-styleable>

The custom view is annotated with @Styleable and @Attr:

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

The @Attr-annotated methods will be called by Paris when the view is inflated with an AttributeSet or when a style is applied.

For more see Custom View Attributes.

Styling Subviews

Attributes are declared as followed for the 2 subviews we'd like to be able to style:

<declare-styleable name="MyHeader">
    <attr name="titleStyle" format="reference" />
    <attr name="subtitleStyle" format="reference" />
    ...
</declare-styleable>

The subview fields are annotated with @StyleableChild:

@Styleable("MyHeader")
class MyHeader(…) : ViewGroup(…) {

    @StyleableChild(R.styleable.MyHeader_titleStyle)
    internal val title: TextView …
    
    @StyleableChild(R.styleable.MyHeader_subtitleStyle)
    internal val subtitle: TextViewinit {
        style(attrs)
    }
}
Click to see the example in Java.
@Styleable("MyHeader")
public class MyHeader extends ViewGroup {

    @StyleableChild(R.styleable.MyHeader_titleStyle)
    TextView title;
    
    @StyleableChild(R.styleable.MyHeader_subtitleStyle)
    TextView subtitle;
    
    …
    // Make sure to call Paris.style(this).apply(attrs) during initialization.
}

The title and subtitle styles can now be part of MyHeader styles:

<MyHeader
    ...
    app:titleStyle="@style/Title2"
    app:subtitleStyle="@style/Regular" />
myHeader.style {
    // Defined in XML.
    titleStyle(R.style.Title2)
    // Defined programmatically.
    subtitleStyle {
        textColorRes(R.color.text_color_regular)
        textSizeRes(R.dimen.text_size_regular)
    }
}
Click to see the example in Java.
Paris.styleBuilder(myHeader)
        // Defined in XML.
        .titleStyle(R.style.Title2)
        // Defined programmatically.
        .subtitleStyle((builder) -> builder
                .textColorRes(R.color.text_color_regular)
                .textSizeRes(R.dimen.text_size_regular))
        .apply();

Attention: Extension functions like titleStyle and subtitleStyle are generated during compilation by the Paris annotation processor. When new @StyleableChild annotations are added, the project must be (re)compiled once for the related functions to become available.

For more see Styling Subviews.

Linking Styles to Views

@Styleable
class MyView(…) : View(…) {

    companion object {
        // For styles defined in XML.
        @Style
        val RED_STYLE = R.style.MyView_Red

        // For styles defined programmatically.
        @Style
        val GREEN_STYLE = myViewStyle {
            background(R.color.green)
        }
    }
}
Click to see the example in Java.
@Styleable
public class MyView extends View {

    // For styles defined in XML.
    @Style
    static final int RED_STYLE = R.style.MyView_Red;

    // For styles defined programmatically.
    @Style
    static void greenStyle(MyViewStyleApplier.StyleBuilder builder) {
        builder.background(R.color.green);
    }
}

Helper methods are generated for each linked style:

myView.style { addRed() } // Equivalent to style(R.style.MyView_Red)
myView.style { addGreen() } // Equivalent to add(MyView.GREEN_STYLE)

myView.style {
    addRed() // Equivalent to add(R.style.MyView_Red)
    addGreen() // Equivalent to add(MyView.GREEN_STYLE)
    …
}
Click to see the example in Java.
Paris.style(myView).applyRed(); // Equivalent to apply(R.style.MyView_Red)
Paris.style(myView).applyGreen(); // No equivalent.

Paris.styleBuilder(myView)
        .addRed() // Equivalent to add(R.style.MyView_Red)
        .addGreen() // No equivalent.
        …
        .apply();

Attention: Extension functions like addRed and addGreen are generated during compilation by the Paris annotation processor. When new @Style annotations are added, the project must be (re)compiled once for the related functions to become available.

For more see Linking Styles to Custom Views.