안드로이드 테두리 그림자 - andeuloideu teduli geulimja

오랜만에 안드로이드 앱 업데이트를 진행하려고 Android Studio를 켰다.

AdMob 계정에 다시 활기가 찾아오고

곧 광고 게재 제한도 풀릴 것 같아서

어플 개발과 출시한 어플 업데이트를 진행 하려고 한다.

우선 가장 먼저 진행할 사안은,

"의무경찰 성적표"

의무경찰 성적표 - 의경 전역일 계산기 앱, 내 군생활은 몇등급일까? - Google Play 앱

현재 방범순찰대 복무중인 의경이 직접 개발하여 실제 필요한 정보만 모았습니다. 내 군생활은 몇등급인지, 지방청 인력풀 지원 여부까지 한눈에 확인하세요 :) 깔끔한 디자인으로 용량을 최소�

play.google.com

안드로이드 테두리 그림자 - andeuloideu teduli geulimja

어플 버그수정 및 디자인 개선.

수정 목록

- 화면이 큰 기기에서 인트로 로고가 가운데 정렬이 되지 않는 문제 -> layout:gravity="center" 값을 줌으로서 해결

- 결과창에 그림자효과 넣기 -> 이번 포스팅의 주제


안드로이드 스튜디오에서, View 객체 (Linearlayout, Relativelayout, ImageView, TextView,,, etc) 에

그림자 효과를 넣고싶을 때는

android:elevation="Xdp"

를 이용해서 매우 쉽게 구현할 수 있다.

단, elevation은 Android API 21 이상에서 작동한다.

(Android 5.0 Lolipop 이상)

안드로이드 테두리 그림자 - andeuloideu teduli geulimja
디자인 변경 전

난 여기서 저 하늘색 테두리로 둘러싸인 부분과, 상단에 '의무경찰 전역일 계산기' 라고 적힌 박스에

그림자 효과를 주려고 한다.

매우 간단하다.

각 뷰에 해당하는 코드에

android:elevation="10dp"

라는 코드를 넣어주면 바로 적용된다.

10dp 라는 부분이 그림자의 두께를 결정하는 것이다.

안드로이드 테두리 그림자 - andeuloideu teduli geulimja

여기서 주의할점은, background 값이 있어야 한다.

background를 기준으로 그림자를 형성하기 때문에,

color 값을 잡아주거나, 따로 xml을 만들어 background 값을 잡아주지 않으면 작동하지 않는다.

안드로이드 테두리 그림자 - andeuloideu teduli geulimja

또한 stroke 값도 잡아주면 안된다.

테두리가 있으면 그림자 효과를 무시하게 된다.

안드로이드 테두리 그림자 - andeuloideu teduli geulimja
elevation 값을 주고 난 다음 모습

이렇게 매우 깔끔하게 그림자 효과가 주어진 것을 볼 수 있다.

이렇게 간단하게 그림자 효과를 줄 수 있을 지 몰랐다.


이렇게, 간단한 코드 한줄 만으로

UI를 상당히 세련되게 만들어 줄 수 있다.

안드로이드에서 그림자 효과를 넣는 방법으로는 UI의 elevation 속성 값을 조정하는 것과 직접 그림자용 리소스 파일을 만드는 방법이 있다. 이번 포스트에서는 이 두가지의 사용 방법과 각각의 장단점을 소개해보려고 한다.

1. elevation 값 조정하기 

UI에 가장 쉽게 섀도우 효과를 입힐 수 있는 방법이다. 안드로이드 API21 부터 UI 뷰들에 elevation 이라는 속성값이 추가 됐는데 이 값을 넣으면 UI가 Z축으로 위로 튀어나와 그림자 효과를 줄 수 있게 된다. 

안드로이드 테두리 그림자 - andeuloideu teduli geulimja
elevation 값을 조정해 그림자 효과를 준 경우.

코드는 다음과 같다.

<androidx.constraintlayout.widget.ConstraintLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <FrameLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent">

        <ImageView
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:layout_margin="50dp"
            android:elevation="20dp"
            android:background="@color/colorPrimary" />
    </FrameLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

elevation 값을 조정해서 효과를 줄 때는 주의해야할 점이 두가지 있는데 첫번째는 elevation 값에 넣어준 수치 만큼 View 주변에 여백을 충분하게 주어야 한다는 것이다. elevation으로 만든 그림자는 View의 width/height 영역 밖에서 발생하기 때문에 이 부분의 여백을 주지 않으면 그림자 효과가 발생하지 않는다. 

안드로이드 테두리 그림자 - andeuloideu teduli geulimja
미리보기 화면을 통해 확인해보면 FrameLayout 내부의 ImageView 주변에 여백이 있는 것을 확인 할 수 있다.

두번째로는 background값이 투명하면 안된다. 불투명한 값으로 셋팅을 해줘야한다. 왜 불투명한 background 값을 셋팅해줘야하는지는 아직 잘 모르겠다; 하지만 투명한 값으로 세팅하면 그림자 효과가 나타나지 않는다.

이 방법은 편하긴 하지만 API21 버전부터 사용할 수 있고 하단부에만 그림자 효과를 줄 수 있다는 단점이 있다. 상하좌우 모두 그림자 효과를 주어야 할 때는 사용 할 수 가 없다. 이런 경우에는 직접 리소스 파일로 그림자 효과를 만들어야 한다.

2. 그림자용 리소스 파일 만들기

선이나 사각형을 코드로 만들 때 사용했던 XML 파일을 이용해서 그림자 효과를 줄 수 있다. 설명에 앞서서 아래 예시 코드와 이 코드를 입힌 UI 결과물을 먼저 보자. 코드가 길지만 반복 구문이 많으니 대강 훓어보는 것을 추천한다

shadow_test.xml

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >

    <!-- Drop Shadow Stack -->
    <item>
        <shape>
            <padding
                android:bottom="2.5dp"
                android:left="2.5dp"
                android:right="2.5dp"
                android:top="2.5dp" />

            <solid android:color="#00CCCCCC" />
        </shape>
    </item>
    <item>
        <shape>
            <padding
                android:bottom="2.5dp"
                android:left="2.5dp"
                android:right="2.5dp"
                android:top="2.5dp" />

            <solid android:color="#06CCCCCC" />
        </shape>
    </item>
    <item>
        <shape>
            <padding
                android:bottom="2.5dp"
                android:left="2.5dp"
                android:right="2.5dp"
                android:top="2.5dp" />

            <solid android:color="#09CCCCCC" />
        </shape>
    </item>
    <item>
        <shape>
            <padding
                android:bottom="2.5dp"
                android:left="2.5dp"
                android:right="2.5dp"
                android:top="2.5dp" />

            <solid android:color="#0BCCCCCC" />
        </shape>
    </item>
    <item>
        <shape>
            <padding
                android:bottom="2.5dp"
                android:left="2.5dp"
                android:right="2.5dp"
                android:top="2.5dp" />

            <solid android:color="#0DCCCCCC" />
        </shape>
    </item>
    <item>
        <shape>
            <padding
                android:bottom="2.5dp"
                android:left="2.5dp"
                android:right="2.5dp"
                android:top="2.5dp" />

            <solid android:color="#10CCCCCC" />
        </shape>
    </item>
    <item>
        <shape>
            <padding
                android:bottom="2.5dp"
                android:left="2.5dp"
                android:right="2.5dp"
                android:top="2.5dp" />

            <solid android:color="#12CCCCCC" />
        </shape>
    </item>
    <item>
        <shape>
            <padding
                android:bottom="2.5dp"
                android:left="2.5dp"
                android:right="2.5dp"
                android:top="2.5dp" />

            <solid android:color="#15CCCCCC" />
        </shape>
    </item>
    <item>
        <shape>
            <padding
                android:bottom="2.5dp"
                android:left="2.5dp"
                android:right="2.5dp"
                android:top="2.5dp" />

            <solid android:color="#17CCCCCC" />
        </shape>
    </item>
    <item>
        <shape>
            <padding
                android:bottom="2.5dp"
                android:left="2.5dp"
                android:right="2.5dp"
                android:top="2.5dp" />

            <solid android:color="#1ACCCCCC" />
        </shape>
    </item>

    <!-- Background -->
    <item>
        <shape>
            <solid android:color="@android:color/white" />
        </shape>
    </item>

</layer-list>

ImageView에 위에서 만든 리소스를 background로 넣었다.

<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <androidx.appcompat.widget.LinearLayoutCompat
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:ignore="RtlSymmetry">

        <ImageView
            android:layout_width="150dp"
            android:layout_height="150dp"
            android:layout_gravity="center"
            android:background="@drawable/shadow_test"/>
    </androidx.appcompat.widget.LinearLayoutCompat>
</androidx.constraintlayout.widget.ConstraintLayout>

이 코드를 이용해 그림자 효과를 입혀본 결과는 다음과 같다.

안드로이드 테두리 그림자 - andeuloideu teduli geulimja
예제 코드를 사용해 그림자 효과를 입힌 결과

사진을 자세히 보면 흰 사각형 바깥쪽에 촘촘히 작은 선들이 있는 것을 볼 수가 있다. 이건 위 예제 코드에서 2.5dp 기준으로 각각 색깔이 다른 사각형을 넣었기 때문이다. 여러개의 작은 장면들을 조합해서 연속된 애니메이션 효과로 보이게 한 것 처럼 이 그라데이션 효과도 작은 사각형들을 합해서 그림자처럼 보이게 만든 효과다. 

이 방법은 약간의 노가다가 필요하긴 하지만 개발자가 그림자 효과를 자유자재로 커스텀이 가능하다는 장점이 있다. 어떤 부분에 좀더 강조를 세게 주고 싶다거나 좌측 상단, 우측 하단, 상화좌우 전체에 그림자 효과를 선택해서 줄 수 있다. 

elevation을 이용한 방법과 차이가 있다면 이 방법은 그림자 영역이 뷰의 영역에 포함되어 있다는 것이다. 아래 그림을 보면 왼쪽 그림의 보라색 사각형이 elevation을 이용해서 그림자 효과를 준 경우고 하얀색 사각형이 리소스를 이용해서 그림자 효과를 준 경우인데, 미리보기 상으로는 하얀색 사각형이 더 작아보이지만 두 ImageView의 가로 세로 너비 값은 오른쪽 그림에서도 알 수 있듯이 동일하다. 리소스를 사용하면 그림자 영역을 View 내부에서 사용하기 때문에 원래 생각했던 ImageView의 크기와 약간 차이가 발생 할 수 있다. 상황에 따라서 단점이 될 수도 있고 장점이 될 수 도 있는 기능이라 섣불리 판단 할 수는 없을 것 같다. 단 차이점은 유의해서 알고가는 것이 좋을 것 같다.

안드로이드 테두리 그림자 - andeuloideu teduli geulimja