Android는 기본 레이아웃 클래스 View
및 ViewGroup
에 기반하여 UI를 구축할 수 있는 정교하고 강력한 구성요소화된 모델을 제공합니다. 우선 플랫폼에는 UI를 구성하는 데 사용할 수 있는 미리 빌드된 다양한 View 및 ViewGroup 서브클래스(위젯 및 레이아웃)가 포함되어 있습니다.
사용 가능한 위젯으로는 Button
, TextView
, EditText
, ListView
, CheckBox
, RadioButton
, Gallery
, Spinner
가 있으며 좀 더 특수한 목적에 맞는 AutoCompleteTextView
, ImageSwitcher
및 TextSwitcher
가 있습니다.
사용 가능한 레이아웃에는 LinearLayout
, FrameLayout
, RelativeLayout
등이 있습니다. 더 많은 예는 일반 레이아웃 객체를 참고하세요.
미리 빌드된 위젯 또는 레이아웃이 필요에 맞지 않으면 View 서브클래스를 직접 만들 수 있습니다. 기존 위젯 또는 레이아웃을 약간만 조정해야 하는 경우 위젯 또는 레이아웃을 서브클래스로 만들고 그 메서드를 재정의할 수 있습니다.
View 서브클래스를 직접 만들면 화면 요소의 모양과 기능을 정밀하게 제어할 수 있습니다. 맞춤 뷰를 사용하여 제어하는 방법은 다음과 같은 몇 가지 예를 참고하세요.
- 완전히 맞춤 렌더링된 뷰 유형을 만들 수 있습니다. 예를 들어 '볼륨 컨트롤' 노브를 2D 그래픽을 사용하여 렌더링하면 아날로그 전자 컨트롤과 유사합니다.
- ComboBox(팝업 목록 및 자유 입력 텍스트 필드의 조합), 이중 창 선택기 컨트롤(목록이 있는 왼쪽 및 오른쪽 창으로, 원하는 항목을 원하는 목록에 재할당 가능) 등을 만들려면 View 구성요소 그룹을 새로운 단일 구성요소로 결합할 수 있습니다.
- EditText 구성요소가 화면에 렌더링되는 방법을 재정의할 수 있습니다(메모장 가이드에서는 효과적으로 이 기능을 사용하여 줄이 있는 메모장 페이지를 만듭니다).
- 키 누름과 같은 다른 이벤트를 캡처하고 맞춤 방식(예: 게임용)으로 처리할 수 있습니다.
다음 섹션에서는 맞춤 뷰를 만들어 애플리케이션에서 사용하는 방법을 설명합니다.
자세한 내용은 View
클래스를 참조하세요.
기본 접근법
다음 단계는 자체 View 구성요소를 만들기 시작할 때 알아야 하는 내용을 대략적으로 보여줍니다.
- 자체 클래스로 기존의
View
클래스 또는 서브클래스를 확장합니다. -
슈퍼클래스에서 일부 메서드를 재정의합니다. 재정의할 슈퍼클래스의 메서드는
on
으로 시작합니다(예:onDraw()
,onMeasure()
및onKeyDown()
). 이는 수명 주기 및 기타 기능 후크에 관해 재정의하는Activity
또는ListActivity
의on...
이벤트와 유사합니다. - 새 확장 클래스를 사용합니다. 완료되면, 기반이 되었던 뷰 대신 새 확장 클래스를 사용할 수 있습니다.
팁: 확장 클래스는 확장 클래스를 사용하는 활동 내에서 내부 클래스로 정의할 수 있습니다. 이러한 정의는 확장 클래스에 대한 액세스를 제어하므로 유용하지만 꼭 필요한 것은 아닙니다(애플리케이션에서 더 광범위하게 사용하기 위해 새로운 공개 뷰를 만들 수 있음).
완전히 맞춤설정된 구성요소
완전히 맞춤설정된 구성요소를 사용하여 원하는 모양의 그래픽 구성요소를 만들 수 있습니다. 오래된 아날로그 게이지처럼 보이는 그래픽 VU 미터 또는 노래방 기계에 맞춰 노래할 수 있도록 튀는 공이 단어를 따라 움직이는 긴 텍스트 뷰를 예로 들 수 있습니다. 어떤 방식이든, 결합 방법에 상관없이 내장된 구성요소가 할 수 없는 기능을 구현하고 싶을 것입니다.
다행히 원하는 모양과 동작의 구성요소를 손쉽게 만들 수 있습니다. 단지 화면 크기와 사용 가능한 처리 능력의 제약을 받을 뿐입니다(애플리케이션이 데스크톱 워크스테이션보다 성능이 훨씬 낮은 기기에서 실행될 수도 있다는 것을 기억하세요).
완전히 맞춤설정된 구성요소를 만들려면 다음과 같이 하세요.
- 확장할 수 있는 가장 일반적인 뷰는 당연히
View
이므로, 일반적으로 이를 확장하여 새로운 슈퍼 구성요소를 만듭니다. - XML에서 속성과 매개변수를 가져올 수 있는 생성자를 제공할 수 있으며, 이런 고유한 속성과 매개변수(VU 미터의 색상과 범위, 바늘의 너비와 댐핑 등)를 사용할 수도 있습니다.
- 고유한 이벤트 리스너, 속성 접근자 및 특수키를 만들고 구성요소 클래스에서 좀 더 정교한 동작을 만들 수도 있습니다.
-
거의 확실하게
onMeasure()
를 재정의해야 하고, 구성요소에 무언가를 표시하려면onDraw()
를 재정의해야 합니다. 둘 다 기본 동작이 있지만 기본값인onDraw()
는 아무것도 하지 않고 기본값인onMeasure()
가 항상 100x100 크기를 설정하는데, 이것이 원하는 크기가 아닐 수 있습니다. - 필요할 경우 다른
on...
메서드도 재정의할 수 있습니다.
onDraw()
및 onMeasure()
확장
onDraw()
메서드는 Canvas
를 제공하는데, 이를 통해 2D 그래픽, 기타 표준 또는 맞춤 구성요소, 스타일이 지정된 텍스트는 물론 상상할 수 있는 어떤 것이든 구현할 수 있습니다.
참고: 3D 그래픽에는 적용되지 않습니다. 3D 그래픽을 사용하려면 View 대신 SurfaceView
를 확장하고 별도의 스레드에서 그려야 합니다. 자세한 내용은 GLSurfaceViewActivity 샘플을 참고하세요.
onMeasure()
는 좀 더 복잡합니다. onMeasure()
는 구성요소와 컨테이너 간 렌더링 계약의 중요한 부분입니다. 포함된 부분의 측정값을 효율적으로 정확하게 보고하려면 onMeasure()
를 재정의해야 합니다. 이는 상위에서의 제한 요구사항(onMeasure()
메서드로 전달됨) 및 setMeasuredDimension()
메서드 호출을 위한 요구사항(계산된 후 측정된 너비 및 높이 사용)에 의해 좀 더 복잡해집니다. 재정의된 onMeasure()
메서드에서 이 메서드를 호출하지 못하면 측정 시간에 예외가 발생합니다.
요약하면 onMeasure()
의 구현은 다음과 같습니다.
-
재정의된
onMeasure()
메서드는 너비와 높이 측정치를 제한하기 위한 요구사항으로 취급해야 하는 너비 및 높이 측정 사양(widthMeasureSpec
및heightMeasureSpec
매개변수로 둘 다 치수를 나타내는 정수 코드)과 함께 호출됩니다. 이러한 사양에서 요구할 수 있는 종류의 제한에 관한 전체 참조 내용은View.onMeasure(int, int)
아래의 참조 문서에서 찾을 수 있습니다(이 참조 문서에는 전체 측정 작업도 상당히 잘 설명되어 있습니다). -
구성요소의
onMeasure()
메서드는 구성요소를 렌더링하는 데 필요한 측정 너비와 높이를 계산해야 합니다. 전달되는 사양의 범위 내에 있어야 하지만 확장도 가능합니다(이 경우 상위는 다른 측정 사양을 사용하여 클리핑, 스크롤, 예외 발생,onMeasure()
에 다시 시도하도록 요구 등을 포함하여 원하는 작업을 선택할 수 있습니다). -
너비와 높이가 계산되면 계산된 측정값으로
setMeasuredDimension(int width, int height)
메서드를 호출해야 합니다. 이렇게 하지 않으면 예외가 발생합니다.
다음은 프레임워크가 뷰에서 호출하는 다른 몇몇 표준 메서드를 요약한 것입니다.
카테고리 | 메서드 | 설명 |
---|---|---|
생성 | 생성자 | 코드에서 뷰를 생성할 때 호출되는 생성자의 양식과 레이아웃 파일에서 뷰가 확장될 때 호출되는 양식이 있습니다. 두 번째 양식은 레이아웃 파일에 정의된 모든 속성을 파싱하고 적용해야 합니다. |
|
뷰 및 뷰의 모든 하위가 XML에서 확장되었을 때 호출됩니다. | |
레이아웃 |
|
이 뷰 및 모든 하위의 크기 요구사항을 결정하기 위해 호출됩니다. |
|
뷰가 모든 하위에 크기와 위치를 할당해야 할 때 호출됩니다. | |
|
이 뷰의 크기가 변경되었을 때 호출됩니다. | |
그리기 |
|
뷰가 콘텐츠를 렌더링할 때 호출됩니다. |
이벤트 처리 |
|
새 주요 이벤트가 발생할 때 호출됩니다. |
|
주요 이벤트가 발생할 때 호출됩니다. | |
|
트랙볼 모션 이벤트가 발생하면 호출합니다. | |
|
터치스크린 모션 이벤트가 발생할 때 호출됩니다. | |
포커스 |
|
뷰가 포커스를 얻거나 잃을 때 호출됩니다. |
|
뷰가 포함된 창이 포커스를 얻거나 잃을 때 호출됩니다. | |
연결 |
|
뷰가 창에 연결될 때 호출됩니다. |
|
뷰가 창에서 분리될 때 호출됩니다. | |
|
뷰가 포함된 창의 가시성이 변경되었을 때 호출됩니다. |
복합 컨트롤
완전히 맞춤설정된 구성요소를 만들지 않고 기존 컨트롤 그룹으로 구성된 재사용 가능한 구성요소를 조합하려는 경우 복합 구성요소(또는 복합 컨트롤)를 만드는 것이 적합할 수 있습니다. 요약하면, 더 작은 많은 컨트롤(또는 뷰)을 하나로 취급할 수 있는 논리적 항목 그룹으로 만드는 것입니다. 예를 들어, 콤보 상자는 한 줄로 된 EditText 필드 및 연결된 PopupList가 있는 인접한 버튼의 조합으로 생각할 수 있습니다. 버튼을 누르고 목록에서 항목을 선택하면 EditText 필드가 채워지지만 EditText에 직접 입력할 수도 있습니다.
사실 Android에는 Spinner
와 AutoCompleteTextView
라는 사용 가능한 다른 뷰가 두 가지있지만, 콤보 상자의 개념이 이해하기 쉬운 예입니다.
복합 구성요소를 만들려면 다음과 같이 하세요.
- 일반적인 출발점은 일종의 레이아웃이기 때문에 레이아웃을 확장하는 클래스를 만듭니다. 콤보 상자의 경우 가로 방향의 LinearLayout을 사용할 수 있습니다. 다른 레이아웃이 내부에서 중첩될 수 있으므로, 복합 구성요소는 임의의 복잡성과 구조를 가질 수 있습니다. 활동과 마찬가지로 선언적(XML 기반) 접근 방식을 사용하여 포함된 구성요소를 만들 수도 있고, 코드에서 프로그래밍 방식으로 구성요소를 중첩할 수도 있습니다.
- 새 클래스의 생성자에서 슈퍼클래스가 예상하는 매개변수를 가져와서 먼저 슈퍼클래스 생성자에 전달합니다. 그런 다음 새 구성요소 내에서 사용할 다른 뷰를 설정할 수 있습니다. 여기에서 EditText 필드와 PopupList를 만들게 됩니다. 생성자가 가져와서 사용할 수 있는 자체 속성과 매개변수를 XML에 포함할 수도 있습니다.
- 포함된 뷰에서 생성할 수 있는 이벤트에 관한 리스너를 만들 수 있습니다. 예를 들어 List Item Click Listener에 관한 리스너 메서드는 목록 선택이 만들어질 경우 EditText의 내용을 업데이트합니다.
- 예를 들어 접근자 및 특수키로 자체 속성을 만들면, 구성요소에서 EditText 값을 초기에 설정하고 필요 시 내용을 쿼리할 수 있습니다.
-
레이아웃에는 정상적으로 작동할 기본 동작이 있으므로 레이아웃을 확장할 경우
onDraw()
및onMeasure()
메서드를 재정의할 필요가 없습니다. 하지만 필요한 경우 재정의할 수도 있습니다. - 특정 키를 눌렀을 때 콤보 상자의 팝업 목록에서 특정 기본값을 선택할 수 있도록 다른
on...
메서드(예:onKeyDown()
)를 재정의할 수 있습니다.
요약하면, 레이아웃을 맞춤 컨트롤의 기본으로 사용하면 다음과 같은 여러 가지 장점이 있습니다.
- 활동 화면처럼 선언적 XML 파일을 사용하여 레이아웃을 지정하거나 프로그래밍 방식으로 뷰를 만들고 코드의 레이아웃에 중첩할 수 있습니다.
onDraw()
및onMeasure()
메서드(그리고 대부분의 기타on...
메서드)는 적절한 동작을 가지게 되므로 재정의할 필요가 없습니다.- 마지막으로, 임의로 복잡한 복합 뷰를 빠르게 생성하고 단일 구성요소인 것처럼 재사용할 수 있습니다.
기존 뷰 유형 수정
특정 상황에서 유용한 맞춤 뷰를 훨씬 더 쉽게 만드는 옵션이 있습니다. 원하는 것과 이미 매우 유사한 구성요소가 있는 경우 이 구성요소를 확장하고 변경하려는 동작만 재정의하면 됩니다. 완전히 맞춤설정된 구성요소로 원하는 작업을 모두 할 수 있지만, 뷰 계층 구조에서 좀 더 전문화된 클래스로 시작하면 정확히 원하는 기능을 하는 많은 동작을 무료로 얻을 수 있습니다.
예를 들어 메모장 애플리케이션은 Android 플랫폼 사용의 여러 측면을 보여줍니다. 그중에서 EditText 뷰를 확장하면 줄이 있는 메모장을 만들 수 있습니다. 이는 완벽한 예는 아니며, 이 작업을 위한 API는 다를 수 있지만 원리를 보여줍니다.
Android 스튜디오로 메모장 샘플을 가져오세요(또는 제공된 링크를 사용하여 소스를 살펴보세요). 특히 NoteEditor.java 파일에 있는 LinedEditText
의 정의를 살펴보세요.
다음은 이 파일에서 참고할 사항입니다.
-
정의
클래스는 다음 행으로 정의됩니다.
public static class LinedEditText extends EditText
LinedEditText
는NoteEditor
활동 내에서 내부 클래스로 정의되지만, 원하는 경우NoteEditor
클래스 외부에서NoteEditor.LinedEditText
로 액세스할 수 있도록 공개되어 있습니다.-
이 클래스는
static
입니다. 즉, 상위 클래스에서 데이터에 액세스할 수 있는 소위 '합성 메서드'를 생성하지 않습니다. 따라서NoteEditor
와 강력하게 관련된 클래스가 아닌 별도의 클래스로 작동하게 됩니다. 외부 클래스에서 상태에 액세스할 필요가 없는 경우 이 방법으로 내부 클래스를 더 간단하게 만들 수 있으며, 생성된 클래스를 작게 유지하고 다른 클래스에서 쉽게 사용할 수 있습니다. -
이 경우 맞춤설정하기 위해 선택한 뷰인
EditText
를 확장합니다. 완료되면 새 클래스가 일반EditText
뷰를 대체할 수 있습니다.
-
클래스 초기화
항상 그렇듯이 슈퍼클래스가 먼저 호출됩니다. 또한 이는 기본 생성자가 아니라 매개변수화된 생성자입니다. EditText는 XML 레이아웃 파일에서 확장될 때 이러한 매개변수로 만들어지므로, 생성자는 이를 가져와서 슈퍼클래스 생성자에 전달해야 합니다.
-
재정의된 메서드
이 예에서는
onDraw()
메서드 하나만 재정의하지만 맞춤 구성요소를 직접 만들 때 다른 메서드를 재정의해야 할 수 있습니다.이 샘플의 경우
onDraw()
메서드를 재정의하면EditText
뷰 캔버스에서 파란색 선을 칠할 수 있습니다(캔버스는 재정의된onDraw()
메서드로 전달됩니다). super.onDraw() 메서드는 메서드가 종료되기 전에 호출됩니다. 슈퍼클래스 메서드를 호출해야 하며, 이 경우에는 포함할 선을 칠한 후 끝에서 호출합니다. -
맞춤 구성요소 사용
이제 맞춤 구성요소를 만들었는데, 어떻게 사용할 수 있을까요? 메모장 예에서는 맞춤 구성요소가 선언적 레이아웃에서 직접 사용되므로, res/layout 폴더에 있는
note_editor.xml
을 살펴보세요.<view xmlns:android="http://schemas.android.com/apk/res/android" class="com.example.android.notepad.NoteEditor$LinedEditText" android:id="@+id/note" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@android:color/transparent" android:padding="5dp" android:scrollbars="vertical" android:fadingEdge="vertical" android:gravity="top" android:textSize="22sp" android:capitalize="sentences" />
-
맞춤 구성요소는 XML의 일반 뷰로 생성되며, 클래스는 전체 패키지를 사용하여 지정됩니다. 정의한 내부 클래스는 자바 프로그래밍 언어에서 내부 클래스를 참조하는 표준 방법인
NoteEditor$LinedEditText
표기법을 사용하여 참조됩니다.맞춤 뷰 구성요소가 내부 클래스로 정의되지 않은 경우 대신 XML 요소 이름으로 뷰 구성요소를 선언하고
class
속성을 제외할 수 있습니다. 예:<com.example.android.notepad.LinedEditText id="@+id/note" ... />
LinedEditText
클래스는 이제 별도의 클래스 파일입니다. 클래스가NoteEditor
클래스에 중첩되어 있으면 이 기법이 작동하지 않습니다. - 정의에 있는 다른 속성과 매개변수는 맞춤 구성요소 생성자로 전달된 후 EditText 생성자로 전달되므로, EditText 뷰에 사용하는 것과 동일한 매개변수입니다. 고유한 매개변수를 추가할 수도 있으며, 이 내용은 아래에서 다시 설명하겠습니다.
-
맞춤 구성요소는 XML의 일반 뷰로 생성되며, 클래스는 전체 패키지를 사용하여 지정됩니다. 정의한 내부 클래스는 자바 프로그래밍 언어에서 내부 클래스를 참조하는 표준 방법인
이제 모두 끝났습니다. 위 사례는 분명히 단순한 예이지만 그 점이 바로 핵심입니다. 즉, 맞춤 구성요소는 필요에 따라 단순하거나 복잡하게 만들 수 있습니다.
좀 더 정교한 구성요소에서는 더 많은 on...
메서드를 재정의하고 몇몇 자체 도우미 메서드도 사용하여 속성과 동작을 실제로 맞춤설정할 수 있습니다. 별다른 제약 없이 원하는 대로 마음껏 구성요소를 맞춤설정할 수 있습니다.