Android UI 스타일링과 테마 가이드
Android UI 스타일링과 테마 가이드
Android 앱의 일관된 디자인을 위한 스타일과 테마 적용 방법을 알아봅니다.
Theme
테마 적용 전략
Theme.AppCompat기반 테마 선택- 팝업, 액션바 등에는
ThemeOverlay.AppCompat사용 - 앱 디자인에 맞춰 주요 색상 속성 설정
주요 색상 속성
<style name="AppTheme" parent="Theme.MaterialComponents.DayNight.NoActionBar">
<item name="colorPrimary">@color/primary</item>
<item name="colorPrimaryDark">@color/primary_dark</item>
<item name="colorPrimaryVariant">@color/primary_variant</item>
<item name="colorSecondary">@color/secondary</item>
<item name="colorAccent">@color/accent</item>
<item name="android:colorBackground">@color/background</item>
</style>
Window 속성
<style name="AppTheme" parent="Theme.MaterialComponents.DayNight.NoActionBar">
<!-- 타이틀바 없음 -->
<item name="android:windowNoTitle">true</item>
<!-- 상태바 색상 -->
<item name="android:statusBarColor">@color/primary_dark</item>
<!-- 시스템 바 영역까지 확장 -->
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
</style>
테마 오버레이
뷰 레벨에서 테마 적용:
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/ThemeOverlay.MaterialComponents.Dark">
<!-- 하위 뷰들은 다크 테마 적용 -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Dark Theme Text" />
</LinearLayout>
코드에서 테마 적용
// ContextThemeWrapper 사용
val wrapper = ContextThemeWrapper(context, R.style.MyPopupTheme)
val popupMenu = PopupMenu(wrapper, anchorView)
// Dialog에 테마 적용
AlertDialog.Builder(context, R.style.Theme_MaterialComponents_Dialog)
.setTitle("Title")
.show()
// Inflater에 테마 적용
val themedContext = ContextThemeWrapper(context, R.style.MyTheme)
val inflater = LayoutInflater.from(themedContext)
val view = inflater.inflate(R.layout.my_layout, container, false)
Style
스타일 정의
<style name="MyTextStyle" parent="TextAppearance.MaterialComponents.Body1">
<item name="android:textColor">@color/text_primary</item>
<item name="android:textSize">16sp</item>
<item name="android:fontFamily">@font/roboto</item>
</style>
스타일 상속
<!-- 시스템 스타일 상속 (parent 명시) -->
<style name="GreenText" parent="@android:style/TextAppearance">
<item name="android:textColor">#00FF00</item>
</style>
<!-- 자신의 스타일 상속 (점 표기법) -->
<style name="CodeFont.Red">
<item name="android:textColor">#FF0000</item>
</style>
스타일 적용
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@style/MyTextStyle" />
위젯별 스타일
<!-- ProgressBar -->
<ProgressBar
style="@android:style/Widget.ProgressBar.Small" />
<!-- Button -->
<Button
style="@style/Widget.MaterialComponents.Button.OutlinedButton" />
Material Design 텍스트 스타일
<!-- Headline -->
<TextView
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline5" />
<!-- Subtitle -->
<TextView
android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle1" />
<!-- Body -->
<TextView
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1" />
<!-- Caption -->
<TextView
android:textAppearance="@style/TextAppearance.MaterialComponents.Caption" />
Button 스타일
<!-- Filled Button -->
<Button
style="@style/Widget.MaterialComponents.Button" />
<!-- Outlined Button -->
<Button
style="@style/Widget.MaterialComponents.Button.OutlinedButton" />
<!-- Text Button -->
<Button
style="@style/Widget.MaterialComponents.Button.TextButton" />
Shape Drawable
기본 Shape
<!-- res/drawable/rounded_background.xml -->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/background" />
<corners android:radius="8dp" />
<stroke
android:width="1dp"
android:color="@color/border" />
</shape>
원형 Shape
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="@color/accent" />
</shape>
Inset (마진 적용)
<inset xmlns:android="http://schemas.android.com/apk/res/android"
android:insetLeft="72dp">
<shape android:shape="rectangle">
<solid android:color="#1e000000" />
</shape>
</inset>
Ripple 효과
기본 Ripple
<!-- 기본 ripple -->
android:background="?android:attr/selectableItemBackground"
<!-- 원형 ripple (경계 없음) -->
android:background="?attr/selectableItemBackgroundBorderless"
커스텀 Ripple
<!-- res/drawable/ripple_rounded.xml -->
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="?android:attr/colorControlHighlight">
<item android:id="@android:id/mask">
<shape android:shape="rectangle">
<solid android:color="#000000" />
<corners android:radius="8dp" />
</shape>
</item>
<item>
<shape android:shape="rectangle">
<solid android:color="@color/button_background" />
<corners android:radius="8dp" />
</shape>
</item>
</ripple>
Foreground Ripple
FrameLayout에서 foreground로 ripple 적용:
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="true"
android:focusable="true"
android:foreground="?android:attr/selectableItemBackground">
<!-- 내용 -->
</FrameLayout>
Shadow (Elevation)
<CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:elevation="4dp"
android:translationZ="2dp" />
그림자가 안 보이면:
- 뷰에 배경색 설정
- 부모의
clipChildren="false"설정 clipToPadding="false"설정
Vector Drawable
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@color/icon"
android:pathData="M12,2L2,12h3v8h14v-8h3L12,2z" />
</vector>
Path 명령어
M/m: 이동 (Move)L/l: 직선 (Line to)H/h: 수평선 (Horizontal line)V/v: 수직선 (Vertical line)Z/z: 경로 닫기
대문자는 절대 좌표, 소문자는 상대 좌표입니다.
Color Selector
<!-- res/color/button_text_color.xml -->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true"
android:color="@color/text_pressed" />
<item android:state_enabled="false"
android:color="@color/text_disabled" />
<item android:color="@color/text_normal" />
</selector>
Font
커스텀 폰트 적용
<!-- res/font/roboto.xml -->
<font-family xmlns:android="http://schemas.android.com/apk/res/android">
<font
android:fontStyle="normal"
android:fontWeight="400"
android:font="@font/roboto_regular" />
<font
android:fontStyle="normal"
android:fontWeight="700"
android:font="@font/roboto_bold" />
</font-family>
<TextView
android:fontFamily="@font/roboto" />
결론
일관된 스타일과 테마는 앱의 품질을 높입니다. Material Design 가이드라인을 따르고, 공통 스타일을 정의하여 코드 중복을 줄이세요. Shape, Ripple, Vector Drawable을 활용하면 해상도에 독립적인 UI를 만들 수 있습니다.
Comments