본문 바로가기
앱 제작/SSAFY 서명 앱

#6 해상도 별 레이아웃 대응하기

by Kim Juhwan 2021. 12. 5.

1. 해상도 별 위치 대응하기
   1-1. 문제 제시
   1-2. ConstraintLayout을 사용하자

   1-3. dpi별로 레이아웃 나누기
   1-4. dpi별로 value값 설정하기
   1-5. GuideLine 사용하기
   1-6. ScalableLayout
2. 해상도 별 크기 대응하기
3. 곡선 처리

 

 

 


 

 

2021/12/04 ~ 2021/12/05 개발 내용

1. 해상도 별 위치 대응하기

1-1. 문제 제시

1dp 씩 조절해가며 위치를 맞췄다.

 

우선 임시로 대충 위치를 잡아보기 위해 텍스트 뷰를 이미지 위에 올려보았다.

스샷만 봤을 때는 문제가 없어 보이지만 이게 문제점이... 다른 스마트폰에서는 위치가 조금씩 바뀐다는 것이다.

해상도가 제각각이다보니 이걸 해결하기가 쉽지 않았다.

그래서 오늘 하루동안 고민했던 것들, 공부했던 것들 그리고 해결방법을 기록해보려 한다.

 

[목차 1-2]부터 [목차 1-4]는 고민했던 것들, 공부했던 것들

[목차 1-5]는 내가 사용한 해결방법이다.

 

1-2. ConstraintLayout을 사용하자

우선 가장 기본적으로 해야 하는 건 ConstraintLayout을 사용하는 것이다.

절대 위치가 아닌 상대 위치를 사용하는 특성상 다른 레이아웃을 사용하는 것보다 비교적 화면 잘림 같은 현상이 덜 하다는 것 같다.

어느 정도 오차가 허용되고 간단한 레이아웃이면 이 ConstraintLayout을 사용하는 것만으로도 해결이 되긴 하지만 

내가 지금 만드는 앱 특성상 정.확.한. 위치가 요구되기 때문에 이것만으로는 문제가 많았다.

서명을 엉뚱한 곳에 할 수는 없으니까...

 

1-3. dpi별로 레이아웃 나누기

원초적인 방법이다.

화면 크기별로 레이아웃을 각각 따로 만드는 방법이다.

 

 

다행히도 안드로이드에는 dpi라는 개념이 있다.

여기서 설명하기엔 너무 길어서 나중에 따로 포스팅해야겠다.

나는 이 블로그 글을 참고했다.

아무튼 이 dpi 덕분에 우리는 6가지 종류의 레이아웃을 만들어 대응하면 된다.

 

다만, 이 방법은 자주 쓰이지 않는 듯했다.

xml은 디렉터리가 지원되지 않다 보니 페이지 하나만 해상도 별 대응하려고 해도 벌써 똑같은 레이아웃이 6개가 생기는데... 이런 이유에서 안 쓰이는 게 아닐까 싶다. (관리가 불-편...)

 

1-4. dpi별로 value값 설정하기

dimens 값을 해상도 별로 다르게 줌

 

보통은 이 방법을 사용한다고 한다.

dimens 파일을 해상도별로 만들고 거기에 각각 해상도에 맞는 margin값을 준 다음

레이아웃에서 그 margin값을 사용하면 사용자의 스마트폰 해상도에 맞는 margin값을 알아서 사용한다.

이 방법에 대해서는 이 블로그에 자세히 나와있다.

 

페이지마다 6개씩 레이아웃을 만들지 않아도 되고 딱 저 dimens 파일만 6개 만들면 되는 거라 [목차 1-3] 방법보다 효율적이라고 할 수 있다.

다만 나는 그 적절한 margin값을 알아내기 위해 노가다 하는 작업이 하기 싫어서 다른 방법을 택했다.

(근데 결국 노가다 해야하는 건 비슷하긴 했음...)

 

1-5. GuildLine 사용하기

Guildline을 이용해 적절한 위치를 잡아주었다.

 

안드로이드 스튜디오에는 Guildline이라는 뷰가 있다.

포토샵을 다룰 줄 사람이면 익숙할 것이다.

임의의 가상선을 만들어 그 선을 기준으로 다른 뷰를 배치하는 방법이다.

오오 드디어 이제 해치웠나? 싶었는데

 

 

해상도를 바꾸니 Guideline의 위치가 변경되어버린다.

 

해상도가 많이 차이 나게 바꾸면 바꿀수록 본래의 위치에서 멀어져 버렸다.

Guildline을 사용하면 될 줄 알았는데... 

고민하다가 Guildline에 대해서 검색해보니 이 가이드라인의 위치를 정하는 방법이 2가지가 있었다.

 

<android.support.constraint.Guideline
    android:id="@+id/guideline"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    app:layout_constraintGuide_begin="100dp" />

하나는 이렇게 임의의 dp값만큼 벽에서 떨어지도록 배치하는 방법

내가 이 방법을 사용했었다.

 

<android.support.constraint.Guideline
    android:id="@+id/guideline"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    app:layout_constraintGuide_percent="0.755" />

그리고 두 번째 방법이 퍼센트 값을 주어 배치하는 방법

이 방법은 너비의 시작 부분이 0% 끝 부분이 100% 라고 했을 때

몇 퍼센트에 해당하는 위치에 배치할 건지 값을 주는 방법이다.

 

이렇게 하니 Guideline이 해상도를 바꿔도 원래 위치에서 벗어나지 않았다!

-> Guideline을 기준으로 뷰를 배치하니 뷰도 원래 위치에서 움직이지 않았다.

 

이것도 해보니 노가다긴 노가다더라...

 

코드가 무진장 길어진다는 단점이 있긴 하지만

나는 개인적으로 margin 값을 다르게 주는 방법보단 이게 편했고 나은 것 같았다.

 

5인치부터 7인치까지 테스트를 해봤는데 아주 살짝씩 오차범위가 있긴 하지만 그래도 봐줄 만한 정도였다.

7인치 이상은 태블릿 급이라 위치가 많이 바뀌었고 이건 따로 또 처리를 해줘야 할 듯싶다.

태블릿 지원은 나중에 생각하기로...

 

5인치 이하는... 요즘 5인치 이하인 스마트폰이 있나 싶긴 한데 (개발자가 그런 말을 하면 못쓰지 임마!)

이건 앱 출시를 해보고 교육생분들의 반응을 봐서 필요하다 싶으면 그때 대응할 예정이다.

 

1-6. ScalableLayout

검색을 하다가 해상도 대응을 좀 쉽게 할 수 있도록 도와주는 라이브러리가 있다는 글을 봤다.

공식 라이브러리가 아니면 최대한 사용을 피하자 주의여서 그냥 이런 게 있구나 정도만 알고 넘어갔다.

깃허브를 보니 마지막 업데이트도 4년 전이라 음...

뭔가 시기상 태블릿이 막 쏟아져 나오기 시작했을 때 일반 스마트폰에서 사용하던 앱을 태블릿에서도 사용할 수 있게 하려고 만든 라이브러리가 아닐까...?

쓸 것 같진 않지만 일단 기록!

 

2. 해상도 별 크기 대응하기

class ApplicationClass : Application() {
    companion object {
        var dpHeight = 0.0F
        var dpWidth = 0.0F
    }

    override fun onCreate() {
        super.onCreate()

        val windowManager = applicationContext.getSystemService(Context.WINDOW_SERVICE) as WindowManager
        val display = windowManager.defaultDisplay
        val outMetrics = DisplayMetrics()
        display.getMetrics(outMetrics)

        val density = resources.displayMetrics.density
        dpHeight = outMetrics.heightPixels / density
        dpWidth = outMetrics.widthPixels / density
    }
}

[목차 1]에서는 해상도 별 '위치'를 정하는 방법에 대해 알아봤다.

이번에는 해상도 별 '크기'를 정하는 방법이다.

사용자가 작성한 서명의 크기를 줄여야 하는데 위치에 비해서 비교적 오차범위를 넓게 잡아도 돼서

적당한 값으로 나눠줘도 될 것 같긴 했지만, 그래도 방법을 생각해봤다.

 

우선 사용자 스마트폰의 가로, 세로 픽셀을 구해주었다. (참고한 글)

 

    .
    .
    .
    
    fun setSign(sign: List<Point>) {
        val rate = dpHeight / 68
        val mapped = sign.map { it -> Point(it.x / rate, it.y / rate, it.isContinue) }
        list.addAll(mapped)
        paint.strokeWidth = 20 / rate
        invalidate()
    }
}

스마트폰의 가로 길이 : 스마트폰의 세로 길이 = 축소하고 싶은 크기의 가로 길이 : 축소하고 싶은 크기의 세로 길이

여기서 내가 원하는 가로 길이가 68dp라서 나눠가지고 비율을 구했다.

그리고 Collection API인 map을 통해 리스트에 들어있는 좌표들을 나눠주었다.

크기가 작아짐에 따라 선의 굵기도 얇아져야 하니 굵기도 비율로 나눠주었다.

 

이렇게 해서 해상도 별 크기 대응까지 구현 완료!

 

3. 곡선 처리

곡선 처리 전 (좌), 곡선 처리 후 (우)

 

자세히 보기 전에는 몰랐는데 그림을 그릴 때 자세히 보니까 좌측 선처럼 뭔가 뚝뚝 끊기는 구간이 보였다.

좌표와 좌표 사이에 선을 긋는 개념이다 보니 곡선 부분에서 저런 현상이 생기는 듯했다.

 

    init {
        paint = Paint()
        paint.strokeWidth = 14F
        paint.color = Color.BLACK

        // 곡선 처리를 위해 추가한 코드들
        paint.isAntiAlias = true; // enable anti aliasing
        paint.isDither = true; // enable dithering
        paint.style = Paint.Style.STROKE; // set to STOKE
        paint.strokeJoin = Paint.Join.ROUND; // set the join to round you want
        paint.strokeCap = Paint.Cap.ROUND;  // set the paint cap to round too
        paint.pathEffect = CornerPathEffect(14F); // set the path effect when they join.
    }

검색해보니 이런 방법이 있다고 해서 추가해보니 곡선이라고 하기엔 아직 뭔가 어색한 듯 보였지만

어차피 문서에 축소돼서 들어가면 전혀 눈에 띄지 않아서 그대로 사용하기로 했다.

 

 


💡 느낀 점

  • 해상도 대응이 이렇게 귀찮은 일일 줄 몰랐다. 지금 만드는 앱은 페이지 하나만 대응하면 돼서 할만했는데 나중에 회사 가면... (끔찍) 회사에서 사용하는 더 좋은 방법이 있는 걸까?
  • 제발 xml을 디렉터리로 묶을 수 있는 기능을 지원해줬으면 좋겠다. 기술적으로 문제가 되나? 왜 안 만들어주지??
  • 귀찮아서 항상 피했던 주제였는데 이번 기회에 알게 돼서 기쁘다!
  • 코테랑 CS공부해야 하는데... 개발이 더 재밌다 ㅠㅠ... 얼른 취업해서 그냥 개발에만 몰두하고 싶어라...

📘 참고한 자료


 

 

반응형

댓글