Android/UI

Android Layout

sh1mj1 2022. 8. 29. 22:56

아래 내용은 안드로이드 공식문서 및 개발자를 위한 레시피 tistory 을 참고하였습니다.

https://recipes4dev.tistory.com/66?category=658689!

https://recipes4dev.tistory.com/87?category=658689

https://developer.android.com/guide/topics/ui/declaring-layout

https://stackoverflow.com/questions/3482742/what-is-the-difference-between-gravity-and-layout-gravity-in-android

Layout

Layout 은 사전적 의미로 "배치" 라는 뜻이다.

레이아웃은 앱에서 사용자 인터페이스를 위한 구조(Activity 등)를 결정한다.

Layout 계층 구조 예시

  • 레이아웃의 모든 요소는 ViewViewGroup 객체의 계층 구조를 사용하여 빌드된다.
  • View 는 사용자가 보고 상호작용할 수 있는 것.ViewGroup은 View 및 기타 ViewGroup 객체의 레이아웃 구조를 정의하는 보이지 않는 컨테이너이다.

만약 우리가 ConstraintLayout 안에 LinearLayout 을 만들고 두 개의 Button 을 만 든 후 LinearLayout 안에 세 개의 TextView 을 만든 다면 위 그림과 똑같은 구조가 되는 것이다.

이 때 ConstraintLayoutLinearLayoutViewGroup 이고, 다른 ButtonTextViewView 로 분류된다.

레이아웃을 선언하는 방법

UI 요소를 XML로 선언. (선언적 방식)

  • Android는 위젯, 레이아웃과 같이 View 클래스와 서브클래스를 간단한 XML 문법을 통해 선언한다.
  • Android Studio의 Layout Editor를 사용해서 마우스로 드로그 앤 드롭으로도 빌드할 수 있지만 보통 키보드로 빌드한다.

런타임에 레이아웃 요소 인스턴스화 (프로그래밍 방식)

  • 직접 프로그래밍하여 ViewViewGroup 객체를 만들고 그 속성을 따로 선언, 조작할 수 있다.

위 두 가지를 모두 사용하여 앱의 UI를 빌드할 수 있다. 예를 들어 앱의 기본 레이아웃을 XML에서 선언한 다음, 런 타임에 레이아웃을 수정할 수도 있다!

 

XML

XML 쓰기

XML 어휘를 사용하여 UI Layout과 화면 요소를 중첩된 요소를 사용하는 방식으로 디자인.

루트 요소를 정의하고 더 많은 레이아웃 객체 또는 위젯을 하위 요소로 View 계층 구조를 빌드할 수 있다.

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:orientation="vertical" >
    <TextView android:id="@+id/text"
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:text="Hello, I am a TextView" />
    <Button android:id="@+id/button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Hello, I am a Button" />
</LinearLayout>

XML 리소스 로드

앱을 컴파일할 때 각 XML 레이아웃 파일이 View 리소스 안에 컴파일된다. Activity.onCreate() 콜백 구현해서 레이아웃 리소스를 로드해야 한다.

이 떄 setContentView()를 호출하고 R.layout.main\_layout 의 형태로 참조한다.

(Resource에 있는 layout 중 main\_layout을 참조한다는 의미. 이때 main_layout 자리에 해당 레아아웃의 이름이 들어간다.)

fun onCreate(savedInstanceState: Bundle) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.main_layout)
}

속성

모든 View, ViewGroup 객체는 고유한 XML 속성들을 지원한다.

(예_ ID, 레이아웃 매개변수, 위치정보, 크기, padding, margin 등)

ID

모든 View 객체는 뷰를 고유하게 식별할 수 있는 정수 ID와 연결될 수 있다.

XML 에서는 아래처럼 사용한다.

android:id="@+id/my_button"

(@) 는 XML Parser 가 ID 문자열의 나머지를 파싱하고 확장하여 ID 리소스로 식별해야 한다는 뜻,

(+) 는 Android(framework)에 이 리소스가 없기 때문에 반드시 생성하여 리소스에 추가해야 한다는 뜻이다.

🤔그렇다면 (+) 가 없는 경우도 있을까?
Android 프레임워크는 다른 ID 리소스도 많이 제공한다.
우리 앱 내에 있는 것이 아닌 안드로이드 프레임워크 내에 있는 리소스의 ID를 참조할 때는 아래처럼 작성해주어야 한다.
android:id="@android:id/empty"

android 패키지 네임스페이스가 들어가면 로컬 리소스 클래스가 아니라 android.R 리소스 클래스에서 ID를 참조한다.

일반적으로 뷰를 생성하고 이를 앱에서 참조할 때는

1. 레이아웃 파일에서 뷰/위젯을 정의, ID 할당

<Button android:id="@+id/my_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/my_button_text"/>

2. 

그리고 뷰 객체의 인스턴스를 생성하고 이를 레이아웃에서 받아온다.

val myButton: Button = findViewById(R.id.my_button)

레이아웃 매개변수

Layout의 종류에 따라 View가 있는 ViewGroup에 적절한 View 레이아웃 매개변수를 정의할 수 있다. (layout\_something)

즉, 상위 뷰 그룹이 각 하위 뷰의 레이아웃 매개변수를 정의한다. 물론 하위요소에 각기 다른 LayoutParams도 정의할 수 있다.

(layoutParams 는 이 레이아웃이 어디에 위치할지 결정해주는 속성이다.)

너비와 높이도 정확한 치수로 정할 수 있지만 보통 wrap\_contentmatch\_parent로 설정한다.

  • wrap\_content : 필요한 치수대로 알아서 크기를 조절하라.
  • match\_parent : 상위 뷰그룹이 허용하는 한 최대 크기로 커져라.

레이아웃 위치

뷰는 직사각형이다. 위치는 한쌍의 왼쪽 및 상단 좌표, 두개의 치수가 너비와 높이를 나타내는 형식[pixel]

뷰의 위치는 getLeft() 및 getTop() 메서드로 검색할 수 있다.

만약 getLeft()가 20을 리턴한다면 뷰가 그 뷰의 부모의 왼쪽 가장자리에서 오른쪽으로 20pix 떨어진 곳에 있다는 의미.

그 외 getRight() (= getLeft() + getWidth() ) , getBottom() 계산

사실 이러한 방식으로 위치는 거의 지정하지 않는다. 그래서 코드블록도 귀찮아서 안 했다. ㅎ

크기, padding, margin

1. 크기 : 하나의 뷰는 두 쌍의 너비 및 높이 값을 갖는다.

  • 첫번째 쌍 : 측정된 너비 및 측정된 높이. 이는 뷰가 부모 내에서 얼마나 커지고나 하는지? getMeasuredWidth() 및 getMeasuredHeight()로 치수를 리턴할 수 있다.
  • 두번째 쌍 : 단순 너비 및 높이(= drawable width / height) . 뷰가 화면에 표시되는 실제 크기이다. getWidth() 및 getHeight() 호출

2. padding

  • 정해진 픽셀 수를 사용하여 뷰의 내용을 안쪽으로 밀어낼 수 있다. 예를 들어 왼쪽 패딩 2 이면 뷰의 내용을 왼쪽 가장자리에서 오른쪽으로 2픽셀 밀어낸다. setPadding(left:int, top:int, right:int, bottom:int)로 설정, getPaddingLeft(), (Top(), Right(), Bottom()) 로 리턴한다.

3. margin

  • 뷰에서는 margin은 직접 제공하지 않는다. margin 상위 뷰그룹과 같이 존재할 때 사용된다.

실제로 자주 사용하는 공통 속성

layout\_height : 세로 길이

layout\_width : 가로 길이

layout\_margin : View와 상위 레이아웃 사이의 공간(바깥 쪽에 해당 크기만큼의 공간이 생김)

padding : View와 View 안의 내용 사이의 공간 (안쪽에 해당 크기만큼의 공간이 생김.)

layout\_gravity : 상위 레이아웃에서의 위치 정렬 (or연산자('|')를 이용해서 혼합 사용가능)

gravity : 해당 속성에서의 하위 속성 위치 정렬

일반 Layout의 종류

UI 디자인을 달성하기 위해 하나 이상의 레이아웃을 다른 레이아웃 안에 중첩할 수 있다.

하지만 최대한 중첩을 적게 하는 것이 좋다. 얕을수록 레이아웃이 더 빠르게 그려진다. (가로로 넓은 계층이 세로로 깊은 계층보다 좋다)

linearLayout

Linear : "선형의" -> 말그대로 레이아웃 안의 요소들을 가로 OR 세로 방향으로 선형적으로 나열 할 수 있다.

  1. orientation : 요소들이 나열되는 방향을 지정해준다. 지정할 수 있는 방향으로 horizontal(가로), vertical(세로)가 있다.
  2. layout_weight : 가중치를 의미한다. 요소들을 나열할 때 화면의 크기에 비례해서 요소의 크기를 결정할 때 사용한다.
  3. weightSum : 가중치의 합을 의미한다. 가중치를 주어서 요소를 나열할 때 공백을 구성하고 싶을 때 사용한다.

RelativeLayout

Relative : "상대적인" -> 레이아웃의 요소들을 상대적으로 나열하고 싶을 때 유용하게 사용.

  1. 뷰와 뷰 사이의 상대적인 위치를 결정할 때
  2. 부모인 RelativeLayout 기준으로 상대적인 위치를 결정할 때
  3. 맞춤 정렬을 하고자 할 때

FrameLayout

Frame : "액자" -> 액자에 사진을 꽂아놓고 보관하는 것처럼 여러 개의 뷰를 바꿔가면서 화면에 표시할 수 있다.

주로 하나의 자식 View 만 표시할 때 사용하는 Layout 클래스이다. 물론 여러 View을 자식으로 추가할 수 있다. 이 때 겹쳐진 형태로 표시되고, 가장 최근에 추가된 View 가 가장 위에 표시된다.

안드로이드에서는 특정한 이유가 없는 한 FrameLayout이 오직 하나의 뷰만 표시하도록 권고한다.(FrameLayout 안에 크기가 다른 뷰들을 겹치지 않게 구성하는 것이 어렵다네요)

TableLayout

Table : "표" -> 표처럼 사용할 수 있으며 열(Coulmn), 행(Row)로 표현된다.

  1. TableRow 라는 요소를 사용하여 행(Row)을 추가할 수 있다.
  2. TableRow 안에 View를 선언하여 열(column)을 추가할 수 있다.

GridLayout

자식을 직사각형 그리드에 배치하는 레이아웃.

그리드는 보이는 연역을 셀로 구분하는 선 집합으로 구성된다.

rowSpec 및 columnSpec 은 각각 행과, 열을 자식들이 그룹 안에서 어떻게 정렬되야 하는지를 정의한다.

DrawerLayout

Drawer : "서랍" -> 화면을 서랍처럼 열고 닫는 기능을 한다.

  1. DrawerLayout을 사용하기 위해서는 사용할 요소(View,TextView)가 자식(child)으로 추가되어 있어야 한다.
  2. 추가로 어느 방향에서 열릴지 layout_gravity값이 설정되어 있어야 한다.
  3. xml 파일을 새로 생성하여 DrawerLayout을 구현하든 DrawLayout 안에 TextView를 생성하든 DrawerLayout에서 제공하는 함수의 원형을 보면 두 개가 같다는 것을 알 수 있다.

ConstraintLayout

Constraint : "제약" -> 여러가지 제약 조건들을 이용해서 뷰의 크기와 위치를 결정한다.

RelativeLayout의 "상대적인 위치 관계", LinearLayout의 "가중치" , Chain의 "요소 그룹화" 의 특징을 모두 가진다.

 

대부분의 복잡한 뷰를 구성할 때 이 레이아웃은 항상 쓰인다고 볼 수 있다.

어댑터로 레이아웃 빌드

레이아웃의 콘텐츠가 동적이거나 미리 정의되지 않았다면, AdapterView 의 서브클래스인 레이아웃을 사용하여 런타임에 뷰로 레이아웃을 채울 수 있다.

우리는 Adapter로 레이아웃에 데이터를 바인딩하면 된다.

이 때 Adapter 는 데이터 소스와 AdapterView 레이아웃 사이의 중개자 역할을 한다. Adapter 가 데이터를 검색하여 각 항목을 뷰로 변환해서 AdpaterView 레이아웃에 추가될 수 있도록 한다.

 

이 포스팅에서는 안드로이드에서의 기본적인 레이아웃과 LinearLayout 등의 자주 쓰이는 여러 Layout을 소개했습니다. 예전에 공부하고 정리했던 내용을 다시 다듬기만 했는데 술술 읽혀서 다행이네요.

다음 포스팅에서부터 구체적으로 해당 Layout들을 알아볼 것입니다. 다 정리되면 아래 링크도 같이 포함해서 수정할게요! 

LinearLayout 에 대해 구체적으로 보기