웹/모바일 안드로이드 기초

2.1 레이아웃 개요

View, ViewGroup, Layout

안드로이드 UI 는 기본적으로 View 객체의 계층 구조로 구성됩니다. 화면에 보이는 텍스트, 버튼, 입력, 선택, 이미지 등 모든 구성 요소들이 View 클래스의 서브 클래스로 만들어진 객체로 볼 수 있습니다. 예를 들어 Button 클래스는 View 클래스를 상속받아 구현된 클래스 입니다.

View 의 다른 표현으로 위젯(Widget)이 있으며 이는 클래스 관점이 아니라 화면을 구성하는 UI 요소(element) 관점에서 부르는 용어 입니다.

기본적으로 위치 지정을 위한 좌표와 가로, 세로 크기를 가지는 2차원 구조의 객체로 화면 밀도(dpi)에 독립적인 DP(Density Independent Pixel)를 단위로 합니다.

안드로이드 라이브러리에는 수백개의 사전 정의된 View 서브 클래스들을 제공 하고 있으며 새로운 버전이 나올때 마다 추가되거나 확장되고 있습니다. 어떤 View 요소들은 시간이 지남에 따라 사용빈도가 떨어지거나 새로운 형태로 대체되는 경우도 있어 가능한 최신 경향의 View 클래스들을 사용하는것이 좋습니다.

일반적으로 많이 사용되는 View 클래스들은 다음과 같습니다.

ViewGroup (뷰 그룹)

View 화면 요소들은 계층 구조로 구성되어 있으며 최상위 계층에는 전체 화면에 대한 레이아웃이 포함되어 있습니다. ConstraintLayout, LinearLayout 등 레이아웃 클래스들이 그런 역할을 담당하며 레이아웃 클래스들은 ViewGroup 클래스의 서브 클래스가 됩니다. 즉, 안드로이드 화면은 컨테이너 역할을 하는 ViewGroup과 구성요소인 View 의 집합이 됩니다.

따라서 안드로이드 앱에서 화면을 구성한다는 것은 적절한 레이아웃 클래스들을 사용해 화면의 배치 방식을 결정하고 세부 구성요소들을 배치하는 형태로 이루어집니다. 이 과정에서 ViewGroup 안에 또다른 ViewGroup 에 들어갈 수 있으므로 포함된 ViewGroup 은 다른 레이아웃 구조로 부분 화면 구성이 가능합니다.

[그림: View 계층 구조]

루트(root, 최상위 계층 뷰)는 항상 ViewGroup이며 레이아웃으로 정의 됩니다. 처음 안드로이드 프로젝트를 생성하게 되면 기본적으로 ConstraintLayout으로 구성된 activity_main.xml 리소스를 생성하게 됩니다.

<?xml version="1.0" encoding="utf-8"?>
<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">
    ...

Layout (레이아웃)

현재 안드로이드 스튜디오를 통해 생성하는 프로젝트의 기본 레이아웃은 ConstraintLayout 입니다. 여러 레이아웃 중에서 원하는 레이아웃을 사용할 수 있지만 가장 좋은 방법은 구글 공식 가이드나 Best practice 등을 따르는 것이 좋습니다.

초기 안드로이드 학습과정에서는 LinearLayout 이 편할수 있으나 가급적 ConstraintLayout 사용을 권장 합니다. 물론 모든 ViewGroup에 ConstraintLayout 을 사용하는것이 최선은 아니기 때문에 화면 구성 형태에 따라 적절한 세부 레이아웃을 선택해 사용하면 됩니다.

대표적인 레이아웃은 다음과 같습니다.

[그림: 레이아웃 유형]

LinearLayout

ConstraintLayout

TableLayout, GridLayout

FrameLayout

RelativeLayout

기본 레이아웃 변경

기본 생성된 ConstraintLayout을 LinearLayout등 다른 레이아웃으로 변경할 수 있습니다. 이때 xml파일을 직접 수정하지 말고 레이아웃 에디터의 Component Tree에서 루트로 생성된 ConstraintLayout 선택 -> 오른쪽 마우스 클릭 -> Convert View 를 통해 변경이 가능합니다.



단위 및 View 속성

View와 텍스트에 적절한 단위를 사용해야 하며 px, pt, in, ,mm 등 절대 단위 형식은 가급적 사용하지 않는것이 좋습니다.

안드로이드 단위

DP(Density-independent Pixels)

밀도 독립적 픽셀 단위로 주로 View의 크기 지정에 사용됩니다.

밀도 독립적 픽셀이란 160dpi 화면의 물리적 픽셀 하나를 말하며 사용 중인 화면의 실제 밀도에 따라, 시스템이 실행중 dp 단위의 모든 확대/축소를 자동으로 처리합니다.dp단위를 일반 픽셀로 변환하는 공식은 다음과 같습니다.

px = dp * (dpi / 160)

예를들어 layout_width="100dp"로 설정된 View를 예로 들면 중간 밀도 화면에서는 100 픽셀 너비로 측정되고 고밀도 화면에서는 시스템이 최대 150 픽셀로 확대하므로 화면 밀도가 다르더라도 거의 동일한 비율로 보여지게 됩니다.

다른 밀도의 화면에 UI 가 적절히 표시되도록 하려면, 애플리케이션 UI를 사용할 때 항상 dp 단위를 사용해야 합니다.

SP(Scale-independent Pixels)

sp 는 보통 텍스트의 크기를 지정할때 사용하는 단위로 안드로이드 시스템의 글자 크기 설정에 따라 달라진다. 사용자가 지정한 시스템 글자 크기에 따라 앱에 사용된 글자 크기도 함께 변하게 된다. 만일 텍스트 크기를 dp 로 설정 한다면 고정 크기가 유지 된다.

주요 View 속성

모든 View 객체들은 여러 속성을 통해 화면 배치나 디자인 요소를 정의할 수 있습니다. xml 파일에 보면 설정된 속성에 대해서는 android: 접두어가 붙은 속성들을 볼 수 있으며 레이아웃 편집기에서는 전체 속성 목록을 확인할 수 있습니다.

대표적인 속성은 다음과 같습니다.

id

화면을 구성하는 모든 View 들은 고유의 id 를 가져야 합니다. 이유는 특정 View 에서 발생하는 이벤트를 감지하고 View 의 텍스트 내용이나 혹은 속성들을 변경하기 위해서는 해당 View 객체를 프로그램에서 참조할 수 있어야 하기 때문입니다.

UI 위젯을 처음 배치하게 되면 id 값은 비어있게 되고 편집기에서 직접 id 값을 입력하면 되고 xml 에는 다음과 같이 등록 됩니다. 이전 강좌에서 설명한 것 처럼 @+id/id 이름 형식 입니다.

android:id="@+id/show_count"

가능한 직관적으로 작성하며 이름은 언더바 _(snake case)로 단어를 연결하는 것이 좋습니다.

text/Size/Style/Alignment

출력되는 텍스트와 관련된 속성들로 text, textSize, textStyle 등이 있습니다. 이전 강좌에서 배운것 처럼 문자열 속성값들은 직접 문자열 값을 사용하기 보다는 문자열 리소스(strings.xml)로 등록하고 사용하는 것이 좋습니다.

gravity

View 텍스트 정렬을 위한 속성으로 기본값은 left, top 이며 top, bottom, left, right 및 left|bottm 과 같이 조합해서 사용이 가능합니다.

layout_width, layout_height

View의 크기를 지정하기 위한 속성으로 특정 크기를 지정하거나 match_parent 혹은 wrap_content를 사용할 수 있습니다.

layout_margin, layout_padding

View 요소의 마진과 패딩을 설정하기 위한 속성 입니다. 마진은 View 주변 여백을 말하고 패딩은 내부 여백을 말합니다. layout_marginTop 과 같이 상하좌우 등 세부 속성 항목도 있어 세부적으로 조정이 가능합니다.

tools

View 에 실제 속성들이 고정된 값이 아니라 프로그램이 실행되면서 사용자 입력이나 파일, 데이터베이스 등으로 부터 가지고오는 값으로 채워져야 하는 경우 안드로이드 스튜디오의 레이아웃 편집기에서 실행결과를 미리 확인하기 어려울때 사용할 수 있는 속성들 입니다. tools: 접두어를 사용하는 속성들로 예를들어 TextView 의경우 tools:text = "Hello World" 와 같이 적용하면 레이아웃 편집기 상에서 텍스트가 보여 실행결과를 예측할 수 있습니다.

tools 속성들은 안드로이드 스튜디오 안에서만 확인되는 속성들로 실제 프로그램을 실행하는 경우에는 보이지 않게 됩니다.

onClick

클릭 이벤트를 처리하기 위한 속성 입니다. 기본적으로 안드로이드 앱에서 버튼등의 이벤트 처리는 Activity 클래스에서 View 객체의 setOnClickListener()를 이용해 이벤터 처리를 위한 리스너 객체를 등록하고 이벤트 발생시 콜백(Callback) 메서드가 동작하는 형태로 구현 합니다.

onClick 속성은 레이아웃 xml 파일에서 해당 View 객체의 이벤트 발생시 호출할 메서드를 부여하는 방식으로 비교적 간단하고 사용하기 편하다는 장점이 있습니다.

이벤트 구현과 관련된 보다 세부적인 사항은 2.2 이벤트 핸들링에서 다루게 됩니다.

activity_main.xml

android:onClick="showMsg"

MainActivity.java

public void showMsg(View view) {
  String msg = "Hello World!";
  Toast toast = Toast.makeText(this, msg, duration);
  toast.show();
}


프로그램 코드로 UI 구성

Activity 에서 xml 에 설정된 View 객체 참조

레이아웃 xml에서 모든 UI 설정을 마쳤다고 해도 이벤트 발생시 View 객체의 속성값을 읽어 오거나 변경하는 경우등 위젯과의 상호작용을 위해서는 프로그램 코드내에서 View 객체들에 대한 참조가 필요 합니다.

이 경우 findViewById()를 이용해 View 객체를 xml에서 찾아 바인딩해 주어야 합니다. 그런에 위젯의 수가 많고 상호작용이 다양하게 발생하는 경우라면 이 과정은 매우 기계적이고 번거로운 작업이 됩니다.

이러한 문제 해결을 위해 ButterKnife라고 하는 애너테이션 기반의 바인딩 솔루션이 나왔으며 Kotlin의 경우 Kotlin extensions 를 사용해 보다 쉽게 바인딩하는 것이 가능합니다.

안드로이드 스튜디오 플러그인중 prettify의 경우에는 자동으로 findViewById() 코드를 생성해 주기도 합니다.

findViewById()

여러 방법들이 있지만 가장 기본이 되는 것은 findViewById()이므로 먼저 사용법을 알아 봅니다.

TextView textView = findViewById(R.id.msg_text);
textView.setText("Hello World");

ButterKnife

버터나이프는 안드로이드 공식 도구는 아니며 별도의 라이브러리를 사용한 방법입니다. 애너테이션 기반으로 손쉽게 View 객체들을 바인딩 하는 방법으로 보다 자세한 사항은 https://jakewharton.github.io/butterknife

@BindView(R.id.msg_text) TextView textView;
...
ButterKnife.bind(this);

View binding

안드로이드 스튜디오(3.6 이상)에서 공식적으로 사용 가능한 방법으로 findViewById()로 인한 번거로움을 줄여주는 방법입니다. 다만 몇 가지 설정 이후 사용할 수 있습니다. 안드로이드 스튜디오 1.5이상에서 지원되기 시작한 Data binding도 비슷한 기능을 제공 했지만 기본 목적에서 차이가 있으며 최신 버전의 안드로이드 스튜디오를 사용한다면 View 참조에 최적화된 View binding을 사용하는 것이 좋습니다.

data binding 지원 추가

[build.gradle]

android {
    ...
    viewBinding {
        enabled = true
    }
}

액티비티 코드 작성

[MainActivity.java]

ActivityMainBinding binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
binding.msgTxt.setText("My Hello World");
안드로이드 스튜디오 4.0에서 부터는 build.gradle 에서 view binding 설정이 다음과 같이 buildFeatures 로 이동하게 됩니다.
android { 
    buildFeatures { 
        viewBinding = true 
        } 
}

레이아웃 및 View 코드 구현

xml 을 사용하지 않고 프로그램 코드로도 UI 구성이 가능합니다. 경우에 따라서는 동적인 UI 구성이나 반복되는 위젯(예를들어 버튼이나 이미지의 반복)의 처리를 위해 프로그램 코드에서 UI를 처리하기도 합니다.

그러나 특별한 경우가 아니라면 프로그램 코드로 UI를 구성하는 것은 권장되지 않습니다.

코드를 사용한다는 것을 제외하면 구조적인 부분은 xml을 사용하는것과 거의 동일합니다. 기본적으로 루트 레이아웃을 정의하고 View 를 생성해 배치하는 방식 입니다.

레이아웃 생성

Activity 클래스에서 원하는 레이아웃 객체를 생성하고 필요한 속성들을 지정 합니다

LinearLayout mylayout = new LinearLayout(this);
mylayout.setOrientation(LinearLayout.VERTICAL);

View 생성

원하는 View 클래스의 인스턴스를 생성하고 필요한 속성을 부여 합니다.

TextView mytext = new TextView(this);
mytext.setText("Hello World");

레이아웃에 View 배치

layout.addView(mytext);
setContentView(mylayout);

실습 및 참고자료

실습: 레이아웃 편집기를 이용한 ConstraintLayout 사용

안드로이드 스튜디오의 레이아웃 편집기를 사용해 ConstraintLayout의 사용법을 익히고 자유롭게 View 위젯들을 구성해 원하는 화면을 만들어 봅니다.

본 예제는 다음과 같은 내용들을 학습하기 위한 내용으로 구성 됩니다.

프로젝트 준비

실행 결과

설계의도와 동일한 결과가 나오는지 확인하고 가로/세로 회전을 통해 각각의 레이아웃이 서로 다르게 출력되는지 확인 합니다.

따라하기 및 코드 설명

유튜브 동영상 강좌를 참고해 주세요


참고자료