[Android][Kotlin] ChatGPT를 활용한 Customize an Image in compose (꿀팁)

D.O·2023년 10월 22일
0

Image Customize 필요

Mineme 프로젝트를 구현하며
Figam에서 Story의 Calendar 섹션을 살펴보면, 디자이너가 특별히 일그러진 원 형태로 디자인한 Calendar Item을 발견하였다.

이 디자인 선택은 예전 회의 때 의도적으로 결정된 사항이다.
개인적으로 굉장히 간단히 이 부분을 구현했다고 생각하는데 이 꿀팁을 공유하고자 글을 작성한다.


in Figma

in Project

Custom Shape

초기에 커스텀 디자인을 어떻게 구현해야 할지에 대한 고민이 컸다.

특히나, XML을 사용하는 과정에서도 Custom View를 생성해본 경험이 없었기 때문에, Compose 환경에서 이를 어떻게 구현하고 적용해야 할지에 대한 고민이 더욱 깊었다.

공식 문서에서 Customize an image 부분을 참고하며 공부하였다

Jetpack Compose에서 이미지나 다른 UI 요소를 특정 도형에 맞게 자르기 위해서는 clip이라는 내장 수정자를 사용한다.

예를 들어 이미지를 원형으로 잘라내고 싶다면 Modifier.clip(CircleShape)을 사용하면 된다.

만약 특별한 도형을 원한다면, Shape를 확장하여 Custom Shap를 생성할 수 있다. 이때 Path를 활용해 도형을 정의할 수 있다고 한다.

먼저 Jetpack Compose에서 기본으로 제공하는 도형 중 하나인 CircleShape를 찾아 좀 분석해봐야겠다고 생각했다.

CircleShape는 실제로 RoundedCornerShape 클래스를 통해 생성됩니다. 그리고 RoundedCornerShape는 궁극적으로 Shape 인터페이스를 상속받아 구현되어 있다.

그럼 공식문서에 써져있는대로 Shape를 확장하여 Custom Shape를 만들 수 있다는 것을 확인했고 이제 어떻게 그릴지 RoundedCornerShape를 좀 더 분석해보자

먼저 Shape 인터페이스는 createOutline 함수를 가지는데

이 createOutline은 도형의 외곽선(Outline)을 생성하는 함수이다.
여기서 중요한 것은 이 함수의 반환값인 Outline 객체이다.

OutLine

Outline주석에 따르면 그래픽 영역의 경계를 정의하기 위한 간단한 도형을 정의한다고 한다.

그냥 말 그대로 도형의 외곽선을 나타내는 것이다.

Outline은 sealed class 로 이루어져있다. 따라서 서브 클래스는 해당 파일 내에서만 정의 할 수 있다.

그전에 Outline은 bounds : Rect 라는 추상 속성을 가지는데

이 추상 속성은 Outline 클래스나 그 하위 클래스에서 아웃라인의 경계를 나타내는 직사각형 영역을 반환하는 속성 Outline 클래스를 상속받는 모든 하위 클래스는 이 bounds 속성에 대한 구체적인 구현을 제공해야 한다

즉 좀 헷갈릴 수 있는데

Outline의 bounds 속성은 해당 UI 요소가 차지하는 외부 경계 영역을 나타낸다. 원이나 커스텀 형태의 UI 요소라 할지라도, 이 bounds는 그 요소를 완벽하게 감싸는 최소한의 직사각형 영역을 반환한다.

예를 들어, 원의 경우 이 직사각형은 원을 완전히 포함하는 가장 작은 크기의 정사각형이 된다.

커스텀 형태 UI의 경우 그 경로를 완벽하게 감싸는 가장 작은 크기의 직사각형 상자가 bounds가 되는 것이다.

개발자 옵션의 "레이아웃 경계 보기"를 활성화하면 이를 더욱 명확하게 확인할 수 있다

원이나 다른 형태의 UI 요소라도 그 영역은 항상 직사각형으로 표현된다. 결론적으로, bounds는 해당 Outline이 차지하는 최소 영역을 의미하는 것이다

이제 Outline Selade Class에 정의된 서브 클래스에는 Rectangle, Rounded, Generic 3가지가 있다.

  • Outline.Rectangle: 사각형 외곽선
  • Outline.Rounded: 둥근 모서리가 있는 사각형 외곽선
  • Outline.Generic: Path를 사용하여 정의된 일반적인 외곽선

현재 내가 필요한 것은 울퉁불퉁 모양 즉, Custom OutLine이므로 Generic에 대해서 알아보겠다.

Generic은 주어진 경로(Path)를 기반으로 한 영역을 나타낸다.

해당 클래스는 Path 타입의 인자를 받아 인스턴스 변수로 저장한다.

그리고 해당 변수를 Path에서 제공하는 getBounds() 메서드를 사용하여 해당 경로의 경계 직사각형을 반환한다.

Path

Path는 일련의 점, 선, 곡선으로 이루어진 그래픽 형태를 정의하는 방법인데 이를 통해 간단한 선부터 복잡한 도형까지 다양한 그래픽을 표현 가능하다.

이 Path의 기본 속성으로는 여러가지가 있는데 주요 속성으로는

  • moveTo: Path의 시작 위치를 설정
  • lineTo: 현재 위치에서 특정 위치까지 선을 그린다.
  • curveTo: (Bezier Curves): 베지어 곡선을 사용하여 곡선을 그립니다.
  • arcTo: 호 또는 원의 일부를 그립니다.
  • close: Path의 시작점과 끝점을 연결하여 닫힌 도형을 형성합니다.
  • cubicTo : 네 개의 점을 사용하여 큐빅 베지어 곡선을 그리기 위해 사용

즉 최종적으로 정리하자면

  1. Compose에서 Image의 형태를 커스텀하기 위해 clip 수정자를 사용
  2. 원하는 형태를 구현하기 위해서는 clip에 제공할 Custom Shape 클래스를 정의
  3. 복잡한 형태를 구현할 경우, ShapecreateOutline 함수 내에서 Outline.Generic을 사용
  4. Outline.Generic 구현 시, Path를 사용하여 원하는 디자인을 그림
  5. 최종적으로 만든 Shapeclip 수정자에 적용하여 Image의 형태를 커스텀하게 잘라냄

근데 내가 이 Path의 주요 속성을 공부해서 하나하나 그려가며 확인하기에는 너무 러닝 커브가 크다고 판단했다.

혹시나 하고 Figma에서 이미지를 벡터로 추출해서 Android Studio에서 이미지를 확인해보니

이해하기 어려운 Path정보가 이미 있는 것을 확인했다

이때부터 ChatGPT를 활용한 방법이다

이 Path정보를 가지는 XML 벡터 그래픽 데이터 정보를 ChatGpt 제공하며 Custom Shape를 그려달라고 부탁했다.

내가 GPT에게 입력한 프롬프트 아래와 같다.

프롬프트

내가 제공한 XML 벡터 그래픽 데이터를 기반으로 @Composable Custom Shape를 만들어줘

데이터는 아래와 같다.

<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="48dp"
    android:height="45dp"
    android:viewportWidth="48"
    android:viewportHeight="45">
  <path
      android:pathData="M36.403,42.528C26.23,46.909 4.996,47.558 0.996,25.196C-0.004,12.021 8.033,-4.319 32.447,2.028C51.245,6.915 51.118,36.191 36.403,42.528Z"
      android:fillColor="#D9D9D9"/>
</vector>

결과

내 코드에 맞게 이름 및 스타일을 조금 수정한 뒤

해당 Shape를 clip에 넘겨주었다.

실제 기기에서 확인해보니 원했던 대로 잘 나오는 것을 볼 수 있다.
업로드중..

마무리

Image를 Customize해서 표시하는 방법에 대해서 알아보았습니다.

저는 이 Path의 속성을 공부하며 직접 그리는 방법 대신 이미 백터 데이터가 있는 상황에서 ChatGPT를 통해 간단히 구현 하는 방법을 택했습니다.

Figma에서 이미지의 백터 데이터를 추출하여 ChatGpt에게 제공하며 질문을 하시면 됩니다.

이러한 방법은 효율적이지만 ChagGPT의 응답을 테스트해보는 단계를 꼭 포함하는 것이 중요하다

profile
Android Developer

0개의 댓글