Building a Design System implementation for Android — Part 2

Howie Zuo
3 min readMar 31, 2020

Part 1

Part 2 (You are here)

In the last article, I talked about how to build a foundation of a Design System implementation, in this article I will demonstrate how to build a “real” screen use Design System.

I really recommend you go through Part 1 first if you haven’t read it.

Ok, sit tight, let’s get started

Design

I made a simple Instagram similar screen based on the styles from the same Design System we are using in Part 1.

Screen

Components

Screens are built with separated components as below.

  • TopNavigationBar
  • Card
  • Card/Header
  • Photo
  • Card/Footer
  • BottomNavigationBar
  • Avatar
  • IconButton

In the same way I built Button in Part 1, I will make every component into a custom view and give it style.

I will skip another code example here, please check https://github.com/howiezuo/design-system-android/tree/master/component/src/main/java/io/github/howiezuo/designsystem/component

Card is the component combined with other components.

/component/src/main/res/layout/component_card.xml

<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" tools:parentTag="io.github.howiezuo.designsystem.component.Card">

<
io.github.howiezuo.designsystem.component.card.Header
android:id="@+id/header"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent" />

<
ImageView
android:id="@+id/photo"
android:layout_width="match_parent"
android:layout_height="0dp"
android:scaleType="centerCrop"
app:layout_constraintDimensionRatio="h,1:1"
app:layout_constraintTop_toBottomOf="@id/header"
tools:ignore="ContentDescription" />

<
io.github.howiezuo.designsystem.component.card.Footer
android:id="@+id/footer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@id/photo" />
</
merge>

/component/src/main/java/io/github/howiezuo/designsystem/component/Card.kt

class Card @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : ConstraintLayout(context, attrs, defStyleAttr) {

private val header: Header by lazy {
findViewById<Header>(R.id.header)
}

private val photo: ImageView by lazy {
findViewById<ImageView>(R.id.photo)
}

init {
LayoutInflater.from(getContext()).inflate(R.layout.component_card, this, true)
}

fun setAvatar(@DrawableRes resId: Int) {
header.setAvatar(resId)
}

fun setName(text: CharSequence) {
header.setName(text)
}

fun setPhoto(@DrawableRes resId: Int) {
photo.setImageResource(resId)
}
}

Screen

Now it’s time to build the screen.

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:backgroundTint="?attr/dsColorBackground"
android:orientation="vertical">

<
io.github.howiezuo.designsystem.component.TopNavigationBar
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:dsTitle="Instagram"
app:menu="@menu/top_navigation_bar_menu"
app:navigationIcon="?attr/dsIconCamera" />

<
androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_photos"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1.0"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:listitem="@layout/row_card" />

<
io.github.howiezuo.designsystem.component.BottomNavigationBar
android:id="@+id/bottom_navigation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:menu="@menu/bottom_navigation_bar_menu" />

</
LinearLayout>

Since the photo list is a RecyclerView, there is another layout file needed for each row.

<io.github.howiezuo.designsystem.component.Card xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/card"
android:layout_width="match_parent"
android:layout_height="wrap_content" />

Run the app.

Screenshot

Looks nice, right?

Theme

Remember I was talking about always using attrsto reference styles (colors, typography, spacing, etc.) through theme instead just link them directly from @dimen/ or @color/ .

Let me show you the magic there.

Here is a new theme called Base.Theme.DesignSystem.Another with three different color values to the original one.

<style name="Base.Theme.DesignSystem.Another" parent="Base.Theme.DesignSystem">
<item name="dsColorBackground">@color/ds_text</item>
<
item name="dsColorText">@color/ds_text_reverse</item>
<
item name="dsColorTextReverse">@color/ds_text</item>
</
style>

Now switching the application theme to the new one.

Screenshot

Not bad, huh?

One more bonus

How to change the theme dynamically?

Just show you the code.

/app/src/main/java/io/github/howiezuo/designsystem/ThemeManager.kt

object ThemeManager {
fun setThemeOverlay(activity: Activity) {
val theme = R.style.AppTheme

activity.setTheme(theme)
}
}

Set the theme before calling super.onCreate .

class MainActivity : AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) {
ThemeManager.setThemeOverlay(this)

super.onCreate(savedInstanceState)

setContentView(R.layout.activity_main)
}

...
}

Last

Thank you for reading, I’m not good at writing, but I hope this article is helpful for you to implement a Design System.

Please check the source code (at the end) for more details, and feel free to leave a comment if there is anything unclear, I will try to improve my article.

Reference

Source code:

--

--