Slide 1

Slide 1 text

NAVER WEBTOON | 안성용 놓치기쉬운 안드로이드UI디테일살펴보기

Slide 2

Slide 2 text

Java ?

Slide 3

Slide 3 text

Java/Kotlin/XML/C++ . 요즘에는대부분Kotlin으로개발한다. 오늘은이부분을중점으로살펴봅니다.

Slide 4

Slide 4 text

안드로이드에서 XML은 어디에 사용할까? - 애니메이션 - 이미지 - UI 레이아웃 - 메뉴 - 앱 탐색 그래프 - 리소스 (문자열, 크기, 색상 등) … 다양한 곳에 사용하고 있다! 어떻게 사용될까? 개발할때,조금더신경쓰면좋을내용을다뤄봅시다.

Slide 5

Slide 5 text

가끔버튼이잘안눌러지는부분이있다. 1

Slide 6

Slide 6 text

버튼이 잘 안 눌러지는 이유 주로 이미지 버튼에서 발생하는데, View 영역이 손가락으로 누를 수 있을만큼 적절하게 확보되지 않는 것이 원인이다.

Slide 7

Slide 7 text

Link: https://material.io/design/usability/accessibility.html#layout-and-typography Material Design에서는 최소 48 x 48 dp 만큼의 터치영역을 확보하는 것을 권장한다.

Slide 8

Slide 8 text

버튼이 잘 안 눌러지는 이유 Margin은 View 터치 영역으로 간주되지 않는다. 디자인 가이드를 맹목적으로 따르지 말고 사용성을 고려하여 터치영역을 적절히 확보하는 것이 좋다.

Slide 9

Slide 9 text

목표 놓치기쉬운 안드로이드UI디테일살펴보기

Slide 10

Slide 10 text

목표 놓치기쉬운 안드로이드UI관련 XML디테일살펴보기

Slide 11

Slide 11 text

클릭 효과가 이미지 아래에 그려져서 보이지 않는 경우가 있다. 2

Slide 12

Slide 12 text

버튼이 안 눌러지는 것처럼 보이는 이유 이럴 때는 foreground와 같은 속성을 사용하여 해결할 수 있다.

Slide 13

Slide 13 text

버튼이 안 눌러지는 것처럼 보이는 이유 안드로이드 앱은 OS 버전에 따라 API가 지원되지 않을 수 있다. 이 때는 적절한 대안을 만들면 된다.

Slide 14

Slide 14 text

버튼이 안 눌러지는 것처럼 보이는 이유 그런데 FrameLayout에서는 API 21에서도 foreground 속성이 잘 동작한다. 도대체 왜?

Slide 15

Slide 15 text

Link: http://androidxref.com/5.0.0_r2/.../android/widget/FrameLayout.java L 까지는 FrameLayout의 속성이었다.

Slide 16

Slide 16 text

Link: http://androidxref.com/6.0.0_r1/.../android/view/View.java M 부터는 View의 속성으로 변경되었다.

Slide 17

Slide 17 text

버튼이 안 눌러지는 것처럼 보이는 이유 사용하는 View(=FrameLayout)에 있는 속성만 아니라 상속하는 상위 View 속성도 사용할 수 있다는 것을 알 수 있다.

Slide 18

Slide 18 text

레이아웃 XML이 화면에 어떻게 그려지게 되는지 과정이 궁금하다면? 3

Slide 19

Slide 19 text

LayoutInflater override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) } @Override public void setContentView(int resId) { ... LayoutInflater.from(mContext).inflate(resId, ...); ... }

Slide 20

Slide 20 text

Link: http://androidxref.com/9.0.0_r3/.../android/view/LayoutInflater.java 단순히 레이아웃 XML을 파싱해서 View 구현체로 변환해주는 방식이다.

Slide 21

Slide 21 text

LayoutInflater 따라서 XML 코드는 실제 구현체로 변환해주는 단계가 반드시 필요하다.

Slide 22

Slide 22 text

LayoutInflater View는 선언된 순서대로 그려진다. 레이아웃이 겹쳐지는 경우에는 고민이 필요하다. (ex. RecyclerView + Button 등)

Slide 23

Slide 23 text

LayoutInflater Z축을 변경하는 방법을 사용할 수도 있다. 단, BG가 있으면 그림자 효과가 나타나니 주의할 것.

Slide 24

Slide 24 text

4 android:tint 속성을 이용하여, 상태에 따라 아이콘 색상을 다르게 보여줄 수 있다.

Slide 25

Slide 25 text

AppCompat 하지만 API 21 미만에서는 런타임에 오류가 발생한다. 위 속성에서 발생한 문제다.

Slide 26

Slide 26 text

Link: http://androidxref.com/5.0.0_r2/.../android/widget/ImageView.java L 부터는 ColorStateList를 허용한다.

Slide 27

Slide 27 text

Link: http://androidxref.com/4.4_r1/.../android/widget/ImageView.java KK 까지는 Color 만 허용한다.

Slide 28

Slide 28 text

AppCompat 그렇다면 어떻게 해결할 수 있을까?

Slide 29

Slide 29 text

AppCompat 플랫폼 속성 대신 커스텀 속성으로 변경하면 API 21 미만에서도 ColorStateList가 동작한다. 무슨 차이가 있는 걸까?

Slide 30

Slide 30 text

android:tint 는 ImageView의 속성이고, app:tint 는 AppCompatImageView의 속성이다.

Slide 31

Slide 31 text

Link: https://github.com/androidx/.../androidx/appcompat/widget/AppCompatImageHelper.java AppCompatImageView는 ColorStateList를 허용한다. 따라서 API level에 관계없이 잘 동작한다.

Slide 32

Slide 32 text

AppCompat 잠깐! ImageView가 어떻게 AppCompatImageView가 된걸까요? class MainActivity : AppCompatActivity() 비밀(?)은 상속에 숨어 있습니다.

Slide 33

Slide 33 text

AppCompatActivity를 상속하면 내부적으로 LayoutInflater에 Factory를 설정해준다.

Slide 34

Slide 34 text

LayoutInflater를 통해 레이아웃 XML을 View 구현체로 변환하려고 할 때, 우선적으로 Factory를 이용한다.

Slide 35

Slide 35 text

Link: http://androidxref.com/.../android/view/LayoutInflater.java (이 부분은 MDC에서 사용됩니다.) AppCompatViewInflater 내부에서 "ImageView" 태그는 AppCompatImageView로 생성해준다.

Slide 36

Slide 36 text

AppCompat과 MDC 이번에는 Button을 살펴봅시다.

Slide 37

Slide 37 text

AppCompat과 MDC 상속하는 테마에 따라 Button 모양이 바뀝니다?

Slide 38

Slide 38 text

MDC 테마에서 ViewInflater를 교체하여 "Button" 태그가 MaterialButton으로 생성된다.

Slide 39

Slide 39 text

AppCompat과 MDC 상속 테마에 따라 View 구현체가 달라진다. AS 4.0 까지는 기본 프로젝트를 생성하면 AppCompat을 상속하지만, AS 4.1 부터는 MDC를 상속하는 것으로 변경되었으니 주의할 것!

Slide 40

Slide 40 text

5 Style과 Theme에 대해서 조금 더 알아보자.

Slide 41

Slide 41 text

Style과 Theme <item name="colorAccent">#FF2C2C</item> <item name="colorAccent">#000000</item>

Slide 42

Slide 42 text

버튼 Style 내부 리소스에서 현재 Theme의 colorAccent 속성을 참조한다.

Slide 43

Slide 43 text

Style과 Theme ... 아무것도설정하지않은버튼의기본색상은뭘까? 즉, Theme는 자신을 포함하여 하위 View에도 영향을 준다.

Slide 44

Slide 44 text

현재 Theme의 buttonStyle 속성에서 배경 이미지를 참조한다.

Slide 45

Slide 45 text

Theme에서 기본 Style을 정의해두고 있고, Style은 단일 View에 적용된다는 것을 알 수 있다.

Slide 46

Slide 46 text

Style과 Theme 상황에 따라 화면 단위로 Theme를 적절히 설정해주고, 세세하게 변경이 필요하면 해당 View에 Style을 설정하는 것을 추천한다.

Slide 47

Slide 47 text

6 ... 혹시 배경이 흰색이 아니라서 root에 background를 설정한 적이 있다면?

Slide 48

Slide 48 text

WindowBackground 이 경우, 개발자 옵션으로 overdraw를 확인해보면 화면의 모든 영역이 불필요하게 한번씩 더 그려진다. background 속성을 사용하지 않고, 기본 배경색을 변경할 수는 없을까?

Slide 49

Slide 49 text

WindowBackground <item name="android:windowBackground">#ffffff</item> ... windowBackground 속성을 이용하면 이 부분을 개선할 수 있다. 어디에 그려지는지는 직접 찾아보면 재미있겠죠?

Slide 50

Slide 50 text

7 drawable 많이 쓰시나요?

Slide 51

Slide 51 text

생각보다 강력한 Drawable shape drawable에 Ripple 효과를 쉽게 추가할 수 있다.

Slide 52

Slide 52 text

생각보다 강력한 Drawable Ripple 효과의 영역을 다르게 설정할 수도 있다. (= mask)

Slide 53

Slide 53 text

생각보다 강력한 Drawable 조금 복잡한 조건의 배터리 UI를 만들어보자! A. 0 ~ 100% 단계가 1% 단위로 보인다. B. 1 ~ 20% 단계에서는 빨간색으로 보인다. C. 0 ~ 1% 단계에서는 경고 아이콘(❗)이 보인다. D. 충전중에는 0 ~ 20% 단계에서도 검정색으로 보이고, 충전 아이콘(⚡)이 함께 보인다. Java 또는 Kotlin 코드 없이도 이미지 5장만으로 구현할 수 있다면? empty low full alert charge

Slide 54

Slide 54 text

생각보다 강력한 Drawable ⚡ selector, layer-list, level-list, clip 등을 혼합하면 가능하다. 먼저 충전중 상태부터 구현해보자. 임시로 Activated 상태를 충전중이라 가정했다. 이미지를 겹쳐 보여줘야 할 때 사용한다. ProgressBar 처럼 이미지를 단계 별로 자를 때 사용한다.

Slide 55

Slide 55 text

생각보다 강력한 Drawable ❗ 다음으로 기본 상태를 살펴보자. level-list를 사용하여 각 level에 맞는 이미지 조합을 보여주자. 참고로 level은 0 ~ 10000 단계를 가진다.

Slide 56

Slide 56 text

생각보다 강력한 Drawable val level: Int = ... // 0 ~ 100 val batteryView: ImageView = ... batteryView.setImageLevel(level * 100) batteryView.isActivated = isCharging setActivated, setImageLevel API를 호출하면 구현 완료! Drawable 구현체가 궁금하다면?

Slide 57

Slide 57 text

재밌게도 "shape"는 GradientDrawable이 구현체다.

Slide 58

Slide 58 text

ImageView의 Level API는 Drawable의 Level API를 호출해주는 형태다.

Slide 59

Slide 59 text

생각보다 강력한 Drawable 이외에도 다양한 Drawable 구현체가 있으므로, 직접 살펴보는 것을 추천한다!

Slide 60

Slide 60 text

8 #F2F2F2 #000000 res/values/colors.xml 다크모드를 지원하는 방법은?

Slide 61

Slide 61 text

Resource Qualifier 색상 외에 이미지 등 다른 리소스도 -night qualifier를 사용할 수 있다. #202020 #FFFFFF res/values-night/colors.xml res/drawable/ic_drink.xml res/drawable-night/ic_drink.xml

Slide 62

Slide 62 text

자세한 내용은 공식 문서를 참고하자.

Slide 63

Slide 63 text

Resource Qualifier 이외에도 다양한 Qualifier를 이용하여, 안드로이드의 파편화된 기기 생태계에 적응할 수 있다. res/layout-land/activity_main.xml res/values-v23/styles.xml res/values-ko/string.xml res/values-sw600dp/dimens.xml

Slide 64

Slide 64 text

이런 목록형 UI를 구현할 때의 과정을 생각해본다면 레이아웃 변경사항을 확인하려고 매번 빌드하지는 않았나? 9

Slide 65

Slide 65 text

tools 이용하기 레이아웃 XML을 작성하면 왼쪽 화면처럼 보인다. 빌드하기 전에 미리 볼 수 있는 방법이 있다면?

Slide 66

Slide 66 text

tools 이용하기 tools를 이용하여, 목록의 개수/방향 등을 설정할 수 있다.

Slide 67

Slide 67 text

tools 이용하기 미리 정의된 tools 샘플 데이터를 이용하면 좀 더 실제처럼 보인다.

Slide 68

Slide 68 text

tools 이용하기 {project}/{module}/sampledata/labels 샘플 데이터를 직접 정의할 수도 있다. tools를 이용하여 빌드 횟수를 줄이고, 생산성을 높여보자. I am debugging. My foldable phone. NAVER TECHCON 2020 ⷎ

Slide 69

Slide 69 text

정리 margin, padding tools Attribute LayoutInflater AppCompat, MDC WindowBackground Drawable Qualifier XML 오늘은 안드로이드를 구성하는 XML 코드에 대해서 살펴봤습니다. 어떤가요. 안드로이드 앱 개발 재미있어 보이지 않나요? Style, Theme

Slide 70

Slide 70 text

감사합니다 Link: https://github.com/fornewid/TECHCON-2020-Sample