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.
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.
Looks nice, right?
Theme
Remember I was talking about always using attrs
to 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.
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: