Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Android DarkTheme 코드랩

Android DarkTheme 코드랩

Sungyong An

July 19, 2019
Tweet

More Decks by Sungyong An

Other Decks in Programming

Transcript

  1. • Android Q
 Settings > Display > Dark Theme
 


    ( 'যف਍ ప݃' QuickSettingب ୶оؽ ) • Android P
 Settings > System > Developer options > Night mode
 
 ( ࢖ࢿ ױ݈਷ 'ঠр ݽ٘' QuickSetting ઁҕ ) OS Setting New
 in Q
  2. New
 in Q • জਸ ׮௼ ప݃۽ ੗ز ߸ജೞח ӝמ

    • API 29 ( ) ীࢲ݅ ࢎਊоמ ForceDark
  3. • ݽٚ জী force-darkܳ ੸ਊೞח ѐߊ੗ ݽ٘ • Android Q

    Settings > System > Developer options > Override force-dark Override ForceDark • ೞ૑݅ Q Beta4ࠗఠ ز੘ೞ૑ ঋח׮. $ F O R C E D A R K
  4. • জ ప݃੄ forceDarkAllowed ࣘࢿਸ true۽ ࢸ੿ೞݶ ഝࢿചػ׮.
 
 


    
 • ױ, Light ప݃݅ ForceDarkо ੸ਊؼ ࣻ ੓׮. Enable ForceDark F O R C E D A R K <style name="AppTheme" parent="@style/Theme.MaterialComponents.Light> <item name="android:forceDarkAllowed">true</item> </style> <style name="Theme.AppCompat.Light" parent="..."> <item name="isLightTheme">true</item> </style>
  5. • ࠗ࠙੸ਵ۽ ForceDarkܳ ࠺ഝࢿചೡ ࣻب ੓׮. <!-- For Layout XML

    --> <View android:forceDarkAllowed="false" /> // For Kotlin view.isForceDarkAllowed = false // For Java view.setForceDarkAllowed(false) Disable ForceDark (Partially) F O R C E D A R K
  6. CodeLab • Android Studio 3.4 ੉࢚ • API 21 ੉࢚

    Emulator / ױ݈ӝ (STEP7਷ Q ೙ਃ) • Kotlin • DataBinding ળ࠺ $ git clone https://github.com/fornewid/Android-DarkTheme-CodeLab
  7. CodeLab • ঠр/઱р ݽ٘о ߸҃غ૑ ঋח ޙઁ • AppCompatDelegate API

    ഐ୹ द,
 AppCompatActivityо ੤द੘غ૑ ঋח׮. Step 1. CodeLab
  8. • Android 4.0 (API14) ੉࢚ ૑ਗ • AppCompat 1.1.0ীࢲ ز੘

    ѐࢶ • Night Modeо ߸҃غݶ AppCompatActivityо ੗زਵ۽ ੤द੘ػ׮. AppCompat: Night Mode
  9. • MODE_NIGHT_YES
 ೦࢚ যنѱ ಴द • MODE_NIGHT_NO
 ೦࢚ ߋѱ ಴द

    • MODE_NIGHT_FOLLOW_SYSTEM
 दझమ ࢸ੿ী ٮۄ ߸҃ • MODE_NIGHT_AUTO_BATTERY
 ੺੹ݽ٘ীࢲ যنѱ ಴द Modes N I G H T M O D E App
 Compat 1.1.0 API21 ↑
  10. • ঠрݽ٘ܳ ࢸ੿ೞח ߑߨ੉ 2о૑ ੓׮. Set night mode N

    I G H T M O D E // AppCompatDelegate.java public abstract void setLocalNightMode(@NightMode int mode); public static void setDefaultNightMode(@NightMode int mode) { ... } • ଵҊ: ч੉ in-memoryী ੷੢غ޲۽, ߹ب۽ чਸ ੷੢/ࠂҳ ೧ঠ ೠ׮.
  11. • Configuration ੿ࠁ۽ ঠрݽ٘ ੸ਊ ৈࠗܳ ഛੋೡ ࣻ ੓׮. Get

    night mode N I G H T M O D E fun isDarkTheme(config: Configuration): Boolean { return config.uiMode and // andח &৬ زੌ Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES }
  12. • Activityܳ ੤द੘ೞ૑ ঋҊ, ࣻزਵ۽ DarkThemeܳ ੸ਊೡ ࣻب ੓׮. Manual

    Night Mode N I G H T M O D E <activity android:name=".MainActivity" android:configChanges="uiMode" /> class MainActivity : AppCompatActivity() { override fun onConfigurationChanged(newConfig: Configuration) { super.onConfigurationChanged(newConfig) val isDarkTheme: Boolean = isDarkTheme(newConfig) ... UI সؘ੉౟ ... } }
  13. CodeLab • start/build.gradle S T E P 1 Use AppCompat

    1.1.0 (1/1) //TODO: STEP1 - Use AppCompat 1.1.0 implementation 'androidx.appcompat:appcompat:1.1.0-rc01'
  14. CodeLab • DarkTheme.kt (Pre-defined) S T E P 1 Done!

    fun apply(enabled: Boolean = false) { val nightMode = if (enabled) { AppCompatDelegate.MODE_NIGHT_YES } else { AppCompatDelegate.MODE_NIGHT_NO } AppCompatDelegate.setDefaultNightMode(nightMode) }
  15. • L OS ੉੹ীח ӝࠄ ప݃о Dark Theme৓׮. Dark Theme

    Dark Theme: <style name="Theme.DeviceDefault" /> Light Theme: <style name="Theme.DeviceDefault.Light" />
  16. • AppCompatীࢲب Dark Themeܳ ઁҕೠ׮. Dark Theme Dark Theme: <style

    name="Theme.AppCompat"/> Light Theme: <style name="Theme.AppCompat.Light"/>
  17. • Night Modeীࢲח -night resource qualifierо ഝࢿചػ׮. • '-night' ಫ؊ী

    ܻࣗझܳ ୶о೧ঠ ೠ׮. • ৘) values-night, raw-night ١١ '-night' Resource Qualifier Link: https://developer.android.com/guide/topics/resources/providing-resources#AlternativeResources <!-- values/colors.xml --> <color name=“colorCustom">#000000</color> <!-- values-night/colors.xml --> <color name=“colorCustom">#ffffff</color>
  18. • ⚠ Resource Qualifier ਋ࢶࣽਤܳ ઱੄೧ঠ ೠ׮. '-night' Resource Qualifier

    res/drawable-hdpi/ic_example.png res/drawable-night-hdpi/ic_example.png res/drawable-land/ic_example.png res/drawable-land-night/ic_example.png
  19. CodeLab • res/values-night/styles.xml S T E P 2 Declare '-night'

    resources (1/3) <!-- TODO: STEP2 - Declare '-night' resources --> <style name="AppTheme" parent="Theme.AppCompat.NoActionBar"> ... </style> <style name="AppTheme.Splash"> ... </style> <style name="AppTheme.Github" parent="Theme.AppCompat" />
  20. CodeLab • res/values-night/colors.xml S T E P 2 Declare '-night'

    resources (2/3) <!-- TODO: STEP2 - Declare '-night' resources --> <color name="colorPrimary">#121212</color> <color name="colorPrimaryDark">#000000</color> <color name="colorAccent">#00c300</color> <color name="colorBackground">#202020</color> ... <color name="colorOnError">#202020</color>
  21. CodeLab • Splash ۽Ҋ ੉ܴ ߸҃ S T E P

    2 Declare '-night' resources (3/3) Before: res/drawable-night/ic_logo_soup_night.png After: res/drawable-night/ic_logo_soup.png
  22. • খࢲ ࠄ Modeܳ ੉ਊೞৈ Drawableী ࢝ઑܳ ੑ൧ ࣻ ੓׮.

    (ӝࠄ: SRC_IN) • ColorStateList۽ Stateful Tintب оמೞ׮. • API 21 ࠗఠ ୶оغ঻૑݅, Compat APIо ઁҕػ׮. • DrawableCompat • VectorDrawableCompat Tint
  23. • Drawable XMLীࢲ android:tint ࣘࢿਸ ࢎਊೡ ࣻ ੓׮. Tinting Drawables

    <!-- res/drawable/shape.xml --> <shape android:tint="@color/btn_background"> <solid android:color="@color/white" /> </shape> <!-- res/drawable/vector.xml --> <vector android:tint="@color/btn_background"> <path android:fillColor="@color/white" /> </vector>
  24. • Viewب Tint APIо ઁҕػ׮. Tinting Views <ImageView android:tint="@color/btn_icon" />

    <View android:backgroundTint="@color/btn_background" /> imageView.setImageTintList(ColorStateList) view.setBackgroundTintList(ColorStateList) ImageViewCompat.setImageTintList(view, ColorStateList) ViewCompat.setBackgroundTintList(view, ColorStateList)
  25. CodeLab • res/drawable/ic_round_favorite_off.xml S T E P 3 Tinting icons

    (1/2) <!-- TODO: STEP3 - Tinting icons --> <vector android:width="24dp" android:height="24dp" android:tint="@color/colorIcon">
  26. CodeLab • res/drawable/ic_round_error_outline.xml S T E P 3 Tinting icons

    (2/2) <!-- TODO: STEP3 - Tinting icons --> <vector android:width="24dp" android:height="24dp" android:tint="@color/colorOnError">
  27. • Animation ࢸ੿ೡ ٸ, زੌ ܻࣗझ IDח সؘ੉౟غ૑ ঋח ੉गо

    ੓׮. Lottie Issue // LottieComposition.java private static String rawResCacheKey(@RawRes int resId) { return "rawRes_" + resId; } Link: https://github.com/airbnb/lottie-android/issues/1305
  28. CodeLab • res/layout/home_card_moop.xml S T E P 4 Set 'Lottie'

    dynamically (1/2) <!--TODO: STEP4 - Set 'Lottie' animation dynamically --> <import type="soup.codelab.darktheme.R" /> <com.airbnb.lottie.LottieAnimationView android:id="@+id/favoriteButton" ... app:lottie_rawResDay="@{R.raw.ic_lottie_favorite_day}" app:lottie_rawResNight="@{R.raw.ic_lottie_favorite_night}" />
  29. CodeLab • LottieBindingAdapter.kt S T E P 4 Set 'Lottie'

    dynamically (2/2) @BindingAdapter("lottie_rawResDay", "lottie_rawResNight") fun setAnimationAsync(view: LottieAnimationView, @RawRes dayRawRes: Int, @RawRes nightRawRes: Int) { if (DarkTheme.isEnabled(view.context)) { view.setAnimation(nightRawRes) } else { view.setAnimation(dayRawRes) } }
  30. CodeLab Step 5. <!-- res/values/colors.xml --> <color name="colorPrimary" >#FF00c300</color> <color

    name="colorPrimaryDark" >#FF00b300</color> <color name="colorAccent" >#FF00c300</color> <color name="colorBackground" >#FFffffff</color> <color name="colorText" >#FF000000</color> <color name="colorIcon" >#FF000000</color> <color name="colorSurface" >#FFeeeeee</color> <color name="colorTagBackground">#1A000000</color> <color name="colorTagText" >#FF121212</color> <color name="colorDivider" >#1F000000</color> <color name="colorBorder" >#33000000</color> <color name="colorName" >#dd000000</color> <color name="colorError" >#FFee4b4b</color> <color name="colorOnError" >#FFffffff</color> <!-- res/values/colors.xml --> <color name="colorPrimary" >#FF00c300</color> <color name="colorPrimaryDark" >#FF00b300</color> <color name="colorAccent" >#FF00c300</color> <color name="colorBackground" >#FFffffff</color> <color name="colorText" >#FF000000</color> <color name="colorIcon" >#FF000000</color> <color name="colorSurface" >#FFeeeeee</color> <color name="colorTagBackground">#1A000000</color> <color name="colorTagText" >#FF121212</color> <color name="colorDivider" >#1F000000</color> <color name="colorBorder" >#33000000</color> <color name="colorName" >#dd000000</color> <color name="colorError" >#FFee4b4b</color> <color name="colorOnError" >#FFffffff</color> • ҙܻೞח Colorо ݆ই૑ח ޙઁ • Color Attributeܳ ੉ਊೞৈ
 Ӓܛ ױਤ۽ ҙܻೡ ࣻ ੓׮.
  31. • android:alpha attribute੉ ನೣػ ColorStateList ܻࣗझܳ ੍ਸ ٸ,
 API 23

    ੉ೞ ߡ੹ীࢲח AppCompat APIܳ ࢎਊ೧ঠ ೤פ׮. ColorStateList AppCompatResources.getColorStateList(context, R.color.divider)
  32. CodeLab • res/values/attrs.xml S T E P 5 Use color

    attribute (1/5) <!-- TODO: STEP5 - Use color attribute --> <attr name="colorOnBackground" format="color" />
  33. CodeLab • res/values/styles.xml S T E P 5 Use color

    attribute (2/5) <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar"> ... <!-- TODO: STEP5 - Use color attribute --> <item name="colorOnBackground">@android:color/black</item> </style>
  34. CodeLab • res/values-night/styles.xml S T E P 5 Use color

    attribute (3/5) <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar"> ... <!-- TODO: STEP5 - Use color attribute --> <item name="colorOnBackground">@android:color/white</item> <style>
  35. CodeLab • res/color/divider.xml S T E P 5 Use color

    attribute (4/5) <selector> <!-- TODO: STEP5 - Use color attribute --> <item android:alpha="0.1" android:color="?colorOnBackground" /> </selector>
  36. CodeLab • res/layout/home_card_soup.xml • res/layout/home_card_moop.xml S T E P 5

    Use color attribute (5/5) <!-- TODO: STEP5 - Use color attribute --> <ImageView android:id="@+id/divider" ... android:background="#ffffff" app:backgroundTint="@color/divider" />
  37. • AppCompatীࢲ DayNight themeܳ ઁҕೠ׮. DayNight Theme values/theme.xml <style name="Theme.AppCompat.DayNight"

    parent="Theme.AppCompat.Light" /> values-night/theme.xml <style name="Theme.AppCompat.DayNight" parent="Theme.AppCompat" />
  38. • AppCompatীࢶ ThemeOverlayب DayNight themeܳ ઁҕೠ׮. DayNight Theme values/theme.xml <style

    name="ThemeOverlay.AppCompat.DayNight" parent="ThemeOverlay.AppCompat.Light"/> values-night/theme.xml <style name="ThemeOverlay.AppCompat.DayNight" parent="ThemeOverlay.AppCompat.Dark"/>
  39. • MDCীب DayNight themeܳ ઁҕೠ׮. DayNight Theme values/theme.xml <style name="Theme.MaterialComponents.DayNight"

    parent="Theme.MaterialComponents.Light" /> values-night/theme.xml <style name="Theme.MaterialComponents.DayNight" parent="Theme.MaterialComponents" />
  40. • ೞ૑݅ MDCী ThemeOverlayח DayNight themeܳ ૑ਗೞ૑ ঋח׮. DayNight Theme

    values/theme.xml <style name="ThemeOverlay.MaterialComponents.DayNight" parent="ThemeOverlay.MaterialComponents.Light"/> values-night/theme.xml <style name="ThemeOverlay.MaterialComponents.DayNight" parent="ThemeOverlay.MaterialComponents"/>
  41. CodeLab • res/layout/home_activity.xml S T E P 6 Use DayNight

    theme (1/1) <!--TODO: STEP6 - Use DayNight theme --> <androidx.appcompat.widget.Toolbar android:id="@+id/toolbar" ... app:popupTheme="@style/ThemeOverlay.AppCompat.DayNight" />
  42. "Developing Themes with Style" #3. Layer your themes for re-use

    Link: https://j.mp/themes-styles?slide=176
  43. CodeLab • start/build.gradle S T E P 7. O P

    T I O N A L Layer your themes (1/4) //TODO: STEP7 - Layer your themes android { compileSdkVersion 29 buildToolsVersion "29.0.0" defaultConfig { applicationId "soup.codelab.darktheme" minSdkVersion 21 targetSdkVersion 29 ...
  44. CodeLab • res/values/themes.xml S T E P 7. O P

    T I O N A L Layer your themes (2/4) <!-- TODO: STEP7 - Layer your themes --> <style name="Platform.Theme.Github" parent="Theme.AppCompat.DayNight" /> <style name="Base.Theme.Github" parent="Platform.Theme.Github" />
  45. CodeLab • res/values-v29/themes.xml S T E P 7. O P

    T I O N A L Layer your themes (3/4) <!-- TODO: STEP7 - Layer your themes --> <style name="Platform.Theme.Github" parent="Theme.AppCompat.DayNight"> <item name="android:isLightTheme">true</item> <item name="android:forceDarkAllowed">true</item> </style>
  46. CodeLab • res/values/styles.xml • res/values-night/styles.xml S T E P 7.

    O P T I O N A L Layer your themes (4/4) <!-- TODO: STEP7 - Layer your themes --> <style name="AppTheme.Github" parent="Base.Theme.Github" />
  47. CodeLab • ׮௼ప݃ীࢲ BottomSheet दੋࢿ ޙઁ • Elevationਵ۽ ੋೠ Ӓܿ੗о

    ੜ উࠁੋ׮.
 
 Black Bg + Black Shadow = Black Step 8. Optional
  48. • Higher elevation, lighter surface • ߋӝח ߈ైݺೠ ൟ࢝ overlay۽

    ઑ੺ػ׮. • Elevation (0dp ~ 24dp) • Overlay Transparency (0% ~ 16%) Elevation Surface Elevation Overlay
  49. • MDC Componentsח ইې ࣘࢿী ٮۄ Elevation Overlaysо ੸ਊػ׮.
 


    
 
 • MaterialShapeDrawable, ElevationOverlayProviderܳ ੉ਊೞݶ
 Custom Viewب Elevation Overlaysܳ ࣚऔѱ ੸ਊೡ ࣻ ੓׮. • ਗ஖ ঋח ҃਋, elevationOverlaysEnabled ࣘࢿਸ false۽ ߸҃ೞݶ ػ׮. Elevation Overlays E L E V A T I O N <style name="Theme.MaterialComponents" parent="..."> <item name="elevationOverlaysEnabled">true</item> <item name="elevationOverlaysColor">?attr/colorOnSurface</item> </style>
  50. • Elevationী ٮۄ Overlay ࢚࢝җ ߓ҃࢝ਸ ੸੺൤ ࢴযળ׮. ElevationOverlayProvider E

    L E V A T I O N MDC 1.1.0 @ColorInt // layered color, if elevationOverlaysEnabled == true public int layerOverlayIfNeeded(@ColorInt int bgColor, float elevation) @ColorInt // layered color, always public int layerOverlay(@ColorInt int bgColor, float elevation) @ColorInt // layered color, of attr/colorSurface public int getSurfaceColorWithOverlayIfNeeded(float elevation) C
  51. • MDCীࢲ ߓ҃ী ࢎਊغח Drawable۽,
 ղࠗ੸ਵ۽ ElevationOverlayProviderܳ ࢎਊೠ׮.
 
 


    
 • ز੸ਵ۽ Elevation ߸҃੉ оמೞ׮. MaterialShapeDrawable E L E V A T I O N private val bgDrawable = MaterialShapeDrawable().apply { initializeElevationOverlay(context) fillColor = context.getColorAttr(R.attr.colorSurface) } bgDrawable.setElevation(float elevation) C
  52. • MDC Guideline: WCAG’s AA ળࣻܳ ӂ੢ • ੘਷ ఫझ౟

    - ୭ࣗ 4.5:1 • ௾ ఫझ౟ - ୭ࣗ 3.0:1 • Text, BG р ؀࠺о ୭ࣗ 15.8:1 ח غযঠ
 24dp elevationীࢲ body textо AAܳ ݅઒ • 24dp = ୭؀ elevation (MDC) Accessibility E L E V A T I O N #121212 8% Primary Dark Primary #1F1B24 Link: https://www.w3.org/TR/UNDERSTANDING-WCAG20/visual-audio-contrast-contrast.html
  53. CodeLab • start/build.gradle S T E P 8. O P

    T I O N A L Get Lighter Surface (1/4) //TODO: STEP8 - Get Lighter Surface implementation 'com.google.android.material:material:1.1.0-alpha08'
  54. CodeLab • res/values/styles.xml S T E P 8. O P

    T I O N A L Get Lighter Surface (2/4) <!-- TODO: STEP8 - Get Lighter Surface --> <style name="AppTheme.BottomSheet" parent="Theme.MaterialComponents.DayNight"> <item name="colorPrimary">@color/colorPrimary</item> <item name="colorPrimaryDark">@color/colorPrimaryDark</item> <item name="colorAccent">@color/colorAccent</item> <item name="colorSurface">@color/colorBackground</item> </style>
  55. CodeLab • MaterialViewBindingAdapter.kt S T E P 8. O P

    T I O N A L Get Lighter Surface (3/4) /* TODO: STEP8 - Get Lighter Surface */ @BindingAdapter("elevationOverlaysEnabled") fun setElevationOverlaysEnabled(view: View, enabled: Boolean) { view.background = if (enabled) { MaterialShapeDrawable.createWithElevationOverlay( view.context, view.elevation) } else { ... } }
  56. CodeLab • res/layout/home_bottom_sheet.xml S T E P 8. O P

    T I O N A L Get Lighter Surface (4/4) <!-- TODO: STEP8 - Get Lighter Surface --> <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="match_parent" android:elevation="8dp" android:theme="@style/AppTheme.BottomSheet" app:elevationOverlaysEnabled="@{true}">
  57. • Notification਷ ੗ز ੸ਊػ׮. • Custom Notification਷ ইې styleਸ ࢎਊೡ

    Ѫ • TextAppearance.Compat.Notification • ױ, ࢖ࢿ P OSח ੸ਊ ؀࢚੉ ইפ׮. Notification