Skip to content
This repository was archived by the owner on Nov 21, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions MaterialThemeBuilder/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
## Build a Material Theme
Build a Material Theme lets you create your own Material theme by customizing values for color, typography, and shape. See how these values appear when applied to Material Components and discover how to implement your custom theme in your own projects. Build a Material Theme is also available for the web as a remixable project on [Glitch](https://glitch.com/~material-theme-builder).

## Overview
Material Components for Android supports Material Theming by exposing top level theme attributes for color, typography and shape. Customizing these attributes will apply your custom theme throughout your entire app.

This project shows how you can organize and use your theme and style resources to take advantage of the robust support for theming in Material Components for Android.

## Change values for typography, shape, and color
By default, apps built with Material Components inherit our baseline theme values. To begin customizing, override properties in `color.xml`, `type.xml` and `shape.xml`. Each file includes detailed comments that illustrate how each subsystem can be customized.

### type.xml
To change your theme’s typography, we recommend using [Google Fonts](https://fonts.google.com/) and choosing a font family that best reflects your style. Set TextApperances to use your custom font and additional type properties to apply a custom type scale globally. [Learn how to add fonts in Android Studio](https://developer.android.com/guide/topics/ui/look-and-feel/downloadable-fonts)

### shape.xml
To systematically apply shape throughout your app, it helps to understand that components are grouped by size into categories of small, medium and large. The shape of each component size group can be themed by customizing its ShapeApperance style. We recommend using our [shape customization tool](https://material.io/design/shape/about-shape.html#shape-customization-tool) to help you pick your corner family and size values.

### color.xml
To change your theme's color scheme, replace the existing HEX color values with your custom HEX values. This project has both light and dark themes, toggle between them within the app to see your changes. Use our [color palette generator](https://material.io/design/color/the-color-system.html#tools-for-picking-colors) to help come up with pairings and check your color contrast.

## Get Started
Clone the material-components-android-examples repository

```
git clone https://github.com/material-components/material-components-android-examples.git
```

In Android Studio - Choose ‘Open an existing Android Studio Project’ and select ‘material-components-android-examples/MaterialThemeBuilder’

Sync, build and run the project. The project, by default, will be configured with the baseline Material theme.

Under the ‘res’ folder, open `color.xml`, `type.xml` and `shape.xml`. Each file has detailed comments describing the Material subsystem it controls. Try modifying each subsystem, re-running the app and seeing how changes are propagated throughout the app.

Once you build your Material theme, move the theme resources (`color.xml`, `type.xml`, `shape.xml`, `styles.xml`, `themes.xml` and `night/themes.xml`) over to your app to start using your Material theme in your own projects.
44 changes: 44 additions & 0 deletions MaterialThemeBuilder/app/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'

android {
compileSdkVersion 29
defaultConfig {
applicationId "io.material.materialthemebuilder"
minSdkVersion 23
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility = 1.8
targetCompatibility = 1.8
}
kotlinOptions {
jvmTarget = "1.8"
}
}

dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])

// Kotlin
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"

// AndroidX
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta4'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'androidx.recyclerview:recyclerview:1.2.0-alpha01'
implementation 'androidx.core:core-ktx:1.1.0'

//MDC
implementation 'com.google.android.material:material:1.2.0-alpha03'
}
21 changes: 21 additions & 0 deletions MaterialThemeBuilder/app/proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable

# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
44 changes: 44 additions & 0 deletions MaterialThemeBuilder/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2019 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="io.material.materialthemebuilder">

<application
android:name=".App"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.MyApp"
tools:ignore="GoogleAppIndexingWarning">
<activity android:name=".ui.MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>

<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<meta-data
android:name="preloaded_fonts"
android:resource="@array/preloaded_fonts" />
</application>

</manifest>
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.material.materialthemebuilder

import android.app.Application
import android.content.Context
import io.material.materialthemebuilder.data.PreferenceRepository

class App : Application() {

lateinit var preferenceRepository: PreferenceRepository

override fun onCreate() {
super.onCreate()
preferenceRepository = PreferenceRepository(
getSharedPreferences(DEFAULT_PREFERENCES, Context.MODE_PRIVATE)
)
}

companion object {
const val DEFAULT_PREFERENCES = "default_preferences"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
* Copyright 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.material.materialthemebuilder.data

import android.content.SharedPreferences
import androidx.appcompat.app.AppCompatDelegate
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData

/**
* A simple data repository for in-app settings.
*/
class PreferenceRepository(private val sharedPreferences: SharedPreferences) {

val nightMode: Int
get() = sharedPreferences.getInt(PREFERENCE_NIGHT_MODE, PREFERENCE_NIGHT_MODE_DEF_VAL)

private val _nightModeLive: MutableLiveData<Int> = MutableLiveData()
val nightModeLive: LiveData<Int>
get() = _nightModeLive

var isDarkTheme: Boolean = false
get() = nightMode == AppCompatDelegate.MODE_NIGHT_YES
set(value) {
sharedPreferences.edit().putInt(PREFERENCE_NIGHT_MODE, if (value) {
AppCompatDelegate.MODE_NIGHT_YES
} else {
AppCompatDelegate.MODE_NIGHT_NO
}).apply()
field = value
}

private val _isDarkThemeLive: MutableLiveData<Boolean> = MutableLiveData()
val isDarkThemeLive: LiveData<Boolean>
get() = _isDarkThemeLive

private val preferenceChangedListener =
SharedPreferences.OnSharedPreferenceChangeListener { _, key ->
when (key) {
PREFERENCE_NIGHT_MODE -> {
_nightModeLive.value = nightMode
_isDarkThemeLive.value = isDarkTheme
}
}
}

init {
// Init preference LiveData objects.
_nightModeLive.value = nightMode
_isDarkThemeLive.value = isDarkTheme

sharedPreferences.registerOnSharedPreferenceChangeListener(preferenceChangedListener)
}

companion object {
private const val PREFERENCE_NIGHT_MODE = "preference_night_mode"
private const val PREFERENCE_NIGHT_MODE_DEF_VAL = AppCompatDelegate.MODE_NIGHT_NO
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* Copyright 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.material.materialthemebuilder.ui

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.lifecycle.Observer
import com.google.android.material.tabs.TabLayout
import androidx.viewpager.widget.ViewPager
import io.material.materialthemebuilder.R
import io.material.materialthemebuilder.App
import io.material.materialthemebuilder.ui.instruction.InstructionsFragment
import io.material.materialthemebuilder.ui.themesummary.ThemeSummaryFragment
import io.material.materialthemebuilder.ui.component.ComponentFragment

/**
* Single activity which contains the [MainViewPagerAdapter] that shows the [InstructionsFragment],
* [ThemeSummaryFragment] and [ComponentFragment].
*/
class MainActivity : AppCompatActivity() {

private lateinit var viewPager: ViewPager
private lateinit var tabLayout: TabLayout

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
viewPager = findViewById(R.id.view_pager)
tabLayout = findViewById(R.id.tab_layout)

tabLayout.setupWithViewPager(viewPager)
val adapter = MainViewPagerAdapter(this, supportFragmentManager)
viewPager.adapter = adapter

(application as App).preferenceRepository
.nightModeLive.observe(this, Observer { nightMode ->
nightMode?.let { delegate.localNightMode = it }
}
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* Copyright 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.material.materialthemebuilder.ui

import android.content.Context
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentStatePagerAdapter
import io.material.materialthemebuilder.R
import io.material.materialthemebuilder.ui.component.ComponentFragment
import io.material.materialthemebuilder.ui.instruction.InstructionsFragment
import io.material.materialthemebuilder.ui.themesummary.ThemeSummaryFragment

/**
* View pager to show all tabbed destinations - Instructions, Theme Summary and Components.
*/
class MainViewPagerAdapter(
private val context: Context,
fragmentManager: FragmentManager
) : FragmentStatePagerAdapter(fragmentManager) {

enum class MainFragments(val titleRes: Int) {
INSTRUCTIONS(R.string.tab_title_instructions),
THEME_SUMMARY(R.string.tab_title_theme_summary),
COMPONENTS(R.string.tab_title_components)
}

override fun getCount(): Int = MainFragments.values().size

private fun getItemType(position: Int): MainFragments {
return MainFragments.values()[position]
}

override fun getPageTitle(position: Int): CharSequence? {
return context.getString(getItemType(position).titleRes)
}

override fun getItem(position: Int): Fragment {
return when (getItemType(position)) {
MainFragments.INSTRUCTIONS -> InstructionsFragment()
MainFragments.THEME_SUMMARY -> ThemeSummaryFragment()
MainFragments.COMPONENTS -> ComponentFragment()
}
}
}
Loading