뷰의 레이아웃

kosdjs·2025년 8월 10일
0

Android

목록 보기
18/24
  • 레이아웃은 액티비티와 같이 앱의 사용자 인터페이스 구조를 정의함

  • 레이아웃의 요소들은 ViewViewGroup 객체의 계층 구조를 사용해 구성됨

  • View는 일반적으로 사용자가 보고 상호작용하는 것을 그림

  • ViewGroup은 그림 1에서 보이는 것과 같이 다른 ViewViewGroup 객체를 위해 레이아웃 구조를 정의하는 보이지 않는 컨테이너임

그림 1. UI 레이아웃을 정의하는 View 계층 구조 그림

  • View 객체는 보통 Widget 이라고 불리며 Button 이나 TextView 와 같은 여러 하위 클래스 중 하나가 될 수 있음

  • ViewGroup 객체는 보통 레이아웃이라고 불리며 LinearLayout 이나 ConstraintLayout 과 같이 다양한 레이아웃 구조를 제공하는 여러 유형중 하나가 될 수 있음

  • 레이아웃을 선언하는 두 가지 방법

    • XML에 UI 요소 선언: 안드로이드는 위젯이나 레이아웃과 같은 View 클래스와 서브클래스에 대응하는 간단한 XML 문법을 제공함, 안드로이드 스튜디오의 Layout Editor 를 사용해 XML 레이아웃을 드래그 앤 드롭으로 구성할 수 있음
    • 런타임에 레이아웃 요소 인스턴스화: 앱에서 ViewViewGroup 객체를 생성해 프로그래밍적으로 속성을 조작할 수 있음
  • UI를 XML로 선언하는 것은 앱의 표현층을 행동을 제어하는 코드에서 분리시킴, XML 파일을 사용하는 것은 다양한 화면 크기와 방향에 맞는 다양한 레이아웃을 제공하기 더 쉽게함

  • 안드로이드 프레임워크는 앱의 기본 레이아웃을 XML로 선언하고 런타임에서 수정하는 것과 같이 앱의 UI를 만드는 방법들을 사용하는 데 유연성을 줌

노트: 레이아웃을 런타임에 디버깅하려면 Layout Inpector 툴을 사용하십시오.

XML 작성

  • 안드로이드의 XML 문법을 사용하는 것은 중첩된 요소들로 HTML을 사용해 웹 페이지를 생성하는 것과 같은 방식으로 UI 레이아웃과 화면 요소를 빠르게 디자인할 수 있음

  • 각 레이아웃 파일은 반드시 하나의 View 또는 ViewGroup 객체를 루트 요소로 가지고 있어야 함, 루트 요소를 선언한 이후에 레이아웃을 정의하는 View 계층 구조를 만들기 위해 추가 레이아웃 객체나 위젯을 하위 요소로 추가할 수 있음

TextViewButton 을 가지는 수직 LinearLayout의 XML 레이아웃 예시

<?xml version="1.0" encoding="utf-8"?>
<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 확장자로 파일을 저장하고 안드로이드 프로젝트의 res/layout/ 경로에 넣어야 정상적으로 컴파일됨

XML 리소스 로드

  • 앱을 컴파일할 때 각 XML 레이아웃 파일은 View 리소스로 컴파일됨, 앱의 Activity.onCreate() 콜백 구현에서 레이아웃 리소스를 불러와야 함, setContentView() 에 레이아웃 리소스를 R.layout.layout_file_name 과 같은 형식으로 전달해 불러올 수 있음

액티비티에서 main_layout.xml 에 저장된 XML 레이아웃을 불러오는 예시

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

속성

  • 모든 ViewViewGroup 객체는 각자의 다양한 XML 속성들을 지원함, TextViewtextSize 속성을 지원하는 것과 같이 특정 속성은 특정 View 객체만 지원함, 이런 특정 속성들은 그 특정 클래스를 상속받는 View 객체에도 상속됨

  • id 속성과 같은 일부 속성들은 루트 View 클래스로부터 상속되었기 때문에 모든 View 객체가 지원함, 다른 속성들은 View 객체의 특정 레이아웃 방향을 설명하는 레이아웃 매개변수로 간주되고 이는 그 객체의 부모 ViewGroup 객체에 의해 정의됨

ID

  • 모든 View 객체는 트리에서 View 를 고유하게 식별하기 위해서 정수 ID 값을 가짐

  • 앱이 컴파일되었을 때 ID는 정수로 참조되지만, ID는 일반적으로 XML 파일의 id 속성에 문자열로 할당됨

  • 모든 View 객체에 공통적인 XML 속성이며, View 클래스에 정의되어 있고 자주 사용하는 속성임

XML 태그 내부에서 ID를 지정하는 구문 예시

android:id="@+id/my_button"
  • 문자열 맨 앞의 기호 @ 는 XML 파서가 문자열의 나머지를 파싱하고 확장해서 ID 리소스로 식별함을 나타냄, + 기호는 새로운 리소스 이름이라는 것을 뜻하며 이는 새로 생성되어 R.java 파일에 반드시 추가되어야 하는 것을 의미함

  • 안드로이드 프레임워크는 여러 다른 ID 리소스도 제공함, 안드로이드 리소스 ID를 참조할 때는 + 기호가 필요하지 않지만, 반드시 android 패키지 테임스페이스를 다음 예시와 같이 추가해야 함

android:id="@android:id/empty"
  • android 패키지 네임스페이스는 ID를 로컬 리소스 클래스가 아닌 android.R 리소스 클래스에서 참조한다는 것을 나타냄

  • 뷰를 생성하고 앱에서 참조하는 일반적인 패턴

    1. 다음 예시와 같이 뷰를 레이아웃 파일에 정의하고 고유한 ID를 할당함
    <Button android:id="@+id/my_button"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:text="@string/my_button_text"/>
    1. 일반적으로 onCreate() 메소드에서 다음 예시와 같이 뷰 객체의 인스턴스를 생성하고 레이아웃에서 가져옴
    val myButton: Button = findViewById(R.id.my_button)
  • 뷰 객체를 위해 ID를 정의하는 것은 RelativeLayout 을 생성할 때 특히 중요함, RelativeLayout 에는 형제 뷰가 고유 ID로 참조되는 다른 형제 뷰와의 관계로 레이아웃을 정의할 수 있기 때문임

  • ID는 트리 전체에서 고유할 필요는 없지만, 반드시 검색하는 범위 내에서는 고유해야 함, 보통 전체 트리에서 검색하기 때문에 가능하면 전체 트리에서 고유하게 만드는 것이 좋음

레이아웃 매개변수

  • layout_ 으로 시작하는 XML 레이아웃 속성은 특정 View 가 속한 ViewGroup 에 맞게 정의된 레이아웃 매개변수를 나타냄

  • 모든 ViewGroup 클래스는 ViewGroup.LayoutParams 를 확장한 중첩 클래스를 구현함, 이 서브클래스는 뷰 그룹에 맞게 자식 뷰의 크기와 위치를 정의하는 속성 유형을 포함함, 그림 2. 에서 볼 수 있는 것처럼, 부모 뷰 그룹은 자식 뷰 그룹을 포함한 각각의 자식 뷰를 위해 레이아웃 매개변수를 정의함

그림 2. 연관된 각 뷰의 레이아웃 매개변수가 있는 뷰 계층구조의 시각화

  • 모든 LayoutParams 서브 클래스는 값을 설정하는 자신만의 구문이 있음, 각 자식 요소는 반드시 LayoutParams 를 부모에 맞게 정의해야 하고, 자식을 위한 다른 LayoutParams 를 정의할 수도 있음

  • 모든 뷰 그룹은 너비와 높이를 layout_widthlayout_height 를 사용해 포함하고 각 뷰는 이를 반드시 정의해야 함, 많은 LayoutParams 는 추가적인 marginborder 를 포함함

  • 너비와 높이를 정확한 측정치로 지정할 수 있지만 이를 자주 사용되지 않음, 이보다 더 많이 쓰는 방법으로 다음 중 하나의 상수로 너비와 높이를 설정할 수 있음

    • wrap_content : 내용이 필요로 하는 공간에 크기를 맞추게 함
    • match_parent : 부모 뷰 그룹이 허용하는 만큼 크게 함
  • 일반적으로 레이아웃의 너비와 높이를 픽셀과 같은 정확한 단위로 지정하는 것을 추천하지 않음, 밀도 독립형 픽셀 단위(dp), wrap_content, match_parent 와 같이 상대 측정치를 사용하는 것이 앱이 다양한 화면 크기의 기기에서 제대로 표시되는 것을 도우므로 더 좋은 방법임

레이아웃 위치

  • 뷰는 직사각형의 형태를 가짐, 이는 왼쪽과 위쪽 좌표의 쌍으로 표현되는 위치가 있고, 너비와 높이로 나타내는 크기가 있음, 높이와 크기의 단위는 픽셀임

  • getLeft()getTop() 메소드를 호출해 위치를 가져올 수 있음, getLeft() 는 뷰를 나타내는 직사각형의 왼쪽(x) 좌표를 반환하고, getTop() 은 뷰를 나타내는 직사각형의 위쪽(y) 좌표를 반환함, 이 메소드들은 뷰의 위치를 그 뷰의 부모에 연관되게 반환함, 예시로 getLeft() 가 20을 반환했을 때, 이는 뷰가 그 부모의 왼쪽 가장자리에서 20픽셀 오른쪽에 위치했다는 것을 뜻함

  • 추가로, 불필요한 연산을 피하기 위한 편리한 메소드인 getRight()getBottom() 이 있음, 이 메소드는 뷰를 나타내는 직사각형의 오른쪽과 아래쪽 가장자리의 좌표를 반환함, 예시로 getRight() 를 호출하는 것은 getLeft() + getWidth() 와 같음

크기, padding, margin

  • 뷰의 크기는 너비와 높이로 표현됨, 뷰는 두 쌍의 너비와 높이 값을 가지고 있음

  • 첫 쌍은 측정된 너비와 높이로 알려져 있음, 이는 뷰가 부모 뷰 내에서 가지길 원하는 크기를 나타냄, 측정된 크기는 getMeasuredWidth()getMeasuredHeight() 를 호출해 얻을 수 있음

  • 두 번째 쌍은 너비와 높이, 또는 그려지는 너비와 높이로 알려져 있음, 이는 레이아웃 배치가 완료된 후 뷰가 실제 화면에 그려질 때 뷰의 실제 크기를 정의함, 이 값들은 측정된 너비와 높이로부터 다른 값일 수 있지만 반드시 그럴 필요는 없음, getWidth()getHeight() 를 호출해 너비와 높이를 얻을 수 있음

  • 뷰의 크기를 측정할 때 뷰의 padding 을 반영함, padding 은 뷰의 왼쪽, 위쪽, 오른쪽, 아래쪽 부분을 픽셀로 표현함, padding 은 뷰의 내용을 특정 숫자의 픽셀만큼 밀어내는데 사용할 수 있음, 예시로 왼쪽 padding 이 2라면 뷰의 내용을 왼쪽 가장자리에서 2 픽셀만큼 밀어냄, setPadding(int, int, int, int) 메소드를 사용해 padding 을 설정할 수 있고 getPaddingLeft(), getPaddingTop(), getPaddingRight(), getPaddingBottom() 을 호출해 값을 얻을 수 있음

  • 뷰는 padding 을 지원하지만 margin 을 지원하지 않음, 하지만 뷰 그룹은 margin 을 지원함

XML 레이아웃에서 marginpadding 을 설정하는 예시

  <?xml version="1.0" encoding="utf-8"?>
  <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:layout_margin="16dp"
                android:padding="8dp"
                android:text="Hello, I am a TextView" />
      <Button android:id="@+id/button"
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:layout_marginTop="16dp"
              android:paddingBottom="4dp"
              android:paddingEnd="8dp"
              android:paddingStart="8dp"
              android:paddingTop="4dp"
              android:text="Hello, I am a Button" />
  </LinearLayout>
  • 위 예제는 marginpadding 이 적용되는 것을 보여줌, TextView 에는 전체적으로 균일한 marginpadding 이 적용되었고, Button 에는 각 가장자리마다 독립적으로 적용하는 방법을 보여줌

노트: paddingLeft, paddingRight, layout_marginLeft, layout_marginRight 보다 paddingStart, paddingEnd, layout_marginStart, layout_marginEnd 를 사용하는 것이 왼쪽에서 오른쪽으로 읽는 언어 체계와 오른쪽에서 왼쪽으로 읽는 언어 체계 모두에서 잘 작동하기 때문에 더 좋은 방법입니다.

일반 레이아웃

  • ViewGroup 클래스의 각 서브클래스는 그 안에 중첩된 뷰를 표시하는 고유한 방법을 제공함, ConstraintLayout 은 가장 유연한 레이아웃 유형이며 레이아웃 계층구조를 얕게 유지하는데 가장 적합한 도구를 제공함

노트: UI 디자인을 충족하기 위해 다른 레이아웃에 하나 이상의 레이아웃을 중첩시킬 수 있지만, 최대한 계층구조를 얕게 유지하십시오. 중첩된 레이아웃이 적수록 레이아웃이 더 빨리 그려집니다. 넓은 뷰 계층구조가 깊은 뷰 계층구조보다 더 좋습니다.

  • 다음은 안드로이드 플랫폼 내부에 있는 일반 레이아웃 유형 중 일부임
    LinearLayout : 자식들을 세로 또는 가로로 나열하고 길이가 화면 길이를 넘는다면 스크롤 바를 생성하는 레이아웃
    WebView : 웹 페이지를 표시하는 레이아웃

동적 목록 생성

  • 레이아웃의 내용이 동적이거나 사전에 결정되지 않는 경우 RecyclerViewAdapterView 의 서브 클래스를 사용할 수 있음, RecyclerViewAdapterView 보다 메모리를 더 효율적으로 쓰기 때문에 일반적으로 더 좋음

  • RecyclerViewAdapterView 로 만들 수 있는 레이아웃은 다음과 같음

    List : 스크롤 가능한 단일 열의 리스트를 표시함
    Grid : 스크롤 가능한 행과 열의 격자를 표시함
  • RecyclerView 는 사용자 지정 레이아웃 매니저를 직접 생성할 수 있으므로 더 많은 가능성을 제공함

데이터로 어댑터 뷰 채우기

  • ListViewGridView 와 같은 AdapterView 는 인스턴스를 외부 소스에서 데이터를 가져와 각 데이터를 나타내는 View 를 생성하는 Adapter 에 연결해 데이터를 채울 수 있음

  • 안드로이드는 여러 데이터 종류의 데이터를 가져와 AdapterView 를 위해 뷰를 생성하는 몇가지 Adapter 의 서브 클래스를 제공함, 다음은 가장 일반적인 두 어댑터임

  • ArrayAdapter

    • 데이터 소스가 배열일 때 사용하는 어댑터, 기본적으로 각 배열의 아이템마다 toString() 을 호출해 그 내용을 TextView 에 담음

    문자열 배열을 ListView 에 표시하고 싶을 때 ArrayAdapter 를 배열의 각 문자열의 레이아웃을 지정하는 생성자를 이용해 초기화하는 예시

    val adapter = ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, myStringArray)
    • 생성자의 매개변수는 앱의 Context, TextView 를 포함하는 배열의 각 문자열의 레이아웃, 문자열 배열임

    이렇게 생성된 어댑터를 ListViewsetAdapter() 를 사용해 연결하는 예시

    val listView: ListView = findViewById(R.id.listview)
    listView.adapter = adapter
    • 각 아이템이 보여지는 모습을 변경하려면 배열의 객체의 toString() 메소드를 오버라이드 하거나, 배열의 각 아이템에 해당하는 ImageView 가 필요한 것 같이 각 아이템에 대해 TextView 외의 다른 것이 필요하다면 ArrayAdapter 클래스를 확장하고 getView() 메소드를 아이템을 위해 필요한 종류의 뷰를 반환하게 오버라이드 해야 함
  • SimpleCursorAdapter

    • 데이터가 Cursor 형태일때 사용함, SimpleCursorAdapter 를 사용할 때는 Cursor 의 각 행을 나타내기 위한 레이아웃과 그 레이아웃의 뷰에 삽입할 Cursor 의 행을 지정해야 함, 예시로 사람들의 이름과 전화번호 리스트를 만들고 싶다면 각 사람의 행과 이름과 전화번호 열을 포함하는 Cursor 를 반환하는 쿼리를 수행할 수 있음, 그 후에 다음과 같이 레이아웃의 각 결과마다 레이아웃에 표시할 Cursor 의 열을 지정하는 문자열 배열을 생성하고 각 행의 값이 들어가야 하는 뷰를 지정하는 문자열 배열을 만듬
    val fromColumns = arrayOf(ContactsContract.Data.DISPLAY_NAME,
                                ContactsContract.CommonDataKinds.Phone.NUMBER)
    val toViews = intArrayOf(R.id.display_name, R.id.phone_number)
    • SimpleCursorAdapter 를 초기화 할 때 다음과 같이 각 결과에 사용할 레이아웃, 결과를 포함하는 Cursor 와 위의 두 배열을 전달해야 함
    val adapter = SimpleCursorAdapter(this,
              R.layout.person_name_and_number, cursor, fromColumns, toViews, 0)
    val listView = getListView()
    listView.adapter = adapter
    • 그러면 SimpleCursorAdapterCursor 의 각 행에 해당하는 뷰를 제공된 레이아웃에 fromColumns 에 해당하는 열의 값을 toView 에 해당하는 뷰에 삽입해 생성함
  • 앱의 수명동안 어댑터가 읽는 데이터를 변경한다면 notifyDataSetChanged() 를 호출해야 함, 이는 어댑터에 포함된 뷰에 데이터가 변경되었다는 것을 알려 스스로 새로고침을 할 수 있게 함

클릭 이벤트 처리

  • 다음과 같이 AdapterView.OnItemClickListener 인터페이스를 구현해 AdapterView 의 각 아이템의 클릭 이벤트에 반응하게 할 수 있음
listView.onItemClickListener = AdapterView.OnItemClickListener { parent, view, position, id ->
    // 클릭에 대응하는 일을 함
}

원문: https://developer.android.com/develop/ui/views/layout/declaring-layout

0개의 댓글