Skip to content

Building and Applying Styles

Nathanael Silverman edited this page Dec 16, 2019 · 4 revisions

Paris lets you apply styles programmatically to Android views, for example:

textView.style(R.style.Title1)
Click to see the example in Java.
Paris.style(textView).apply(R.style.Title1);

Where textView is an arbitrary TextView instance and Title1 is defined as such:

<style name="Title1">
    <item name="android:paddingTop">20dp</item>
    <item name="android:textColor">#000000</item>
    <item name="android:textSize">30sp</item>
</style>

textView's top padding, text color and text size will be changed according to Title1's attribute values. Simple as that.

Supported View Types and Attributes

For a list of currently supported attributes see Supported View Types and Attributes.

Unsupported attributes are ignored when applying styles. With that said, adding support for new Android attributes is easy and is a great way to start contributing to Paris!

To support custom views attributes see Custom Views.

To support third-party view types see View Proxies.

Themes

Themes can define default attribute values:

<style name="AppTheme">
    <item name="android:textColor">@color/my_default_text_color</item>
</style>

In the context of an Activity using this particular theme, any style applied to a TextView (or descendant) through Paris will apply the theme's text color unless it is overridden by the style.

Warning: Many provided themes define the android:textAppearance attribute. For example, Theme.AppCompat and its variations set the text appearance to @style/TextAppearance.Material by default. This might lead to unexpected results because this default text appearance will be applied to your styled text views unless they override it or its attributes. Such behavior can be disabled at the theme level:

<style name="AppTheme" parent="Theme.AppCompat…">
    <item name="android:textAppearance">@null</item>
</style>

Combining Styles

Using style builders we can combine multiple styles and apply them as one:

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

Where MyGreenTextView and MySmallTextView are defined as follows:

<style name="MyGreenTextView">
    <item name="android:textColor">#00FF00</item>
    <item name="android:textSize">18sp</item>
</style>

<style name="MySmallTextView">
    <item name="android:textSize">14sp</item>
</style>

In cases where there's some overlap (here both styles define a textSize) the attribute value from the last style added prevails, in this case MySmallTextView's 14sp.

There is no limit to the number of styles that can be combined together.

Defining Styles Programmatically

XML can be a bit of a drag. Style builders enable us to define 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();

They 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();

The same rules apply: the small text size takes precedence over the one defined in the MyGreenTextView style because it was specified last.

Style Objects

It's also possible to save styles to apply them later:

val myStyle: Style = textViewStyle {
    add(R.style.MyGreenTextView)
    textSizeRes(R.dimen.my_text_size_small)
}

// Later:
anyTextView.style(myStyle)
Click to see the example in Java.
Style myStyle = Paris.styleBuilder(textView)
        .add(R.style.MyGreenTextView)
        .textSizeRes(R.dimen.my_text_size_small)
        .build();

// Later:
Paris.style(anyTextView).apply(myStyle);