Android View
View 생명주기의 핵심 단계
- 생성 (Construction):
- View 객체가 생성되는 단계입니다.
- 생성자를 통해 초기화 작업을 수행합니다.
- 주로 XML 레이아웃 파일에서 View가 inflate 되거나, Kotlin/Java 코드에서 View 객체를 생성할 때 호출됩니다.
- 부착 (Attachment):
- View가 윈도우(Window)에 부착되는 단계입니다.
onAttachedToWindow()
메서드가 호출됩니다.- 이 시점부터 View는 화면에 표시될 준비가 되었으며, 사용자 입력을 처리할 수 있습니다.
- 리소스 할당이나 이벤트 리스너 등록 등 초기화 작업을 수행하기에 적합한 시점입니다.
- 측정 (Measurement):
- View의 크기를 결정하는 단계입니다.
onMeasure()
메서드가 호출됩니다.- 부모 View로부터 전달받은 제약 조건과 View 자신의 특성을 고려하여 View의 크기를 계산합니다.
- 배치 (Layout):
- View의 위치를 결정하는 단계입니다.
onLayout()
메서드가 호출됩니다.- 부모 View로부터 전달받은 크기와 위치 정보를 기반으로 View의 위치를 설정합니다.
- 자식 View가 있다면, 자식 View들의 위치도 결정합니다.
- 그리기 (Drawing):
- View의 내용을 화면에 그리는 단계입니다.
onDraw()
메서드가 호출됩니다.- Canvas 객체를 사용하여 View의 배경, 텍스트, 이미지 등을 그립니다.
- 분리 (Detachment):
- View가 윈도우에서 분리되는 단계입니다.
onDetachedFromWindow()
메서드가 호출됩니다.- 이 시점부터 View는 더 이상 화면에 표시되지 않으며, 사용자 입력을 처리할 수 없습니다.
- 사용하지 않는 리소스를 해제하고 이벤트 리스너를 제거하는 등 정리 작업을 수행하기에 적합한 시점입니다.
View 생명주기 활용 예시
|
|
invalidate()
vs. requestLayout()
: 차이점 및 사용 시점
invalidate()
- 역할: View의 외관(appearance)이 변경되었을 때 호출하여 해당 View만 다시 그리도록 요청합니다.
- 동작:
invalidate()
메서드가 호출되면 View는 “dirty” 상태로 표시됩니다.- 다음 UI 렌더링 주기에서 시스템은 “dirty” 상태인 View의
onDraw()
메서드를 호출하여 다시 그립니다. - View의 크기나 위치는 변경되지 않습니다.
- 사용 시점:
- View의 내용(텍스트, 색상, 이미지 등)이 변경되었을 때
- View의 일부 영역만 다시 그려야 할 때 (예:
invalidate(Rect)
사용) - 애니메이션 효과를 구현할 때
requestLayout()
- 역할: View의 크기나 위치가 변경되었을 때 호출하여 View의 측정 및 배치 과정을 다시 실행하도록 요청합니다.
- 동작:
requestLayout()
메서드가 호출되면 View는 “layout requested” 상태로 표시됩니다.- 다음 UI 렌더링 주기에서 시스템은 “layout requested” 상태인 View와 해당 View의 모든 자식 View에 대해 측정 (
onMeasure()
) 및 배치 (onLayout()
) 과정을 다시 실행합니다. - View의 내용은 변경되지 않을 수도 있습니다.
- 사용 시점:
- View의 크기 (
LayoutParams
)가 변경되었을 때 - View의 내용 변경으로 인해 View의 크기가 변경될 수 있을 때
- 화면 회전 등으로 인해 레이아웃이 변경되어야 할 때
- View의 크기 (
주의 사항
invalidate()
는 View의 외관만 변경하므로, 크기나 위치 변경에는 영향을 미치지 않습니다.requestLayout()
는 View의 크기나 위치를 변경하므로, 해당 View와 모든 자식 View의 측정 및 배치 과정을 다시 실행하여 성능에 영향을 줄 수 있습니다.- 불필요한
invalidate()
또는requestLayout()
호출은 UI 성능 저하를 야기할 수 있으므로 주의해야 합니다.
예시
|
|
Android View 계층 구조
Android View는 트리 형태의 계층 구조를 이루며, 이를 View 계층 구조(View Hierarchy)라고 합니다.
각 View는 트리의 부모-자식 관계로 연결되어 있으며, 최상위 View를 루트 View(Root View) 라고 합니다.
전위 순회 (root-> left -> right)
View와 ViewGroup
- View: 화면에 보이는 UI 요소의 기본 단위입니다. TextView, ImageView, Button 등이 View에 해당합니다.
- ViewGroup: 다른 View들을 담는 컨테이너 역할을 하는 View입니다.
- LinearLayout, RelativeLayout, ConstraintLayout 등이 ViewGroup에 해당합니다.
- ViewGroup은 View를 상속받기 때문에 View의 모든 속성과 기능을 가지면서, 추가로 자식 View들을 관리하는 기능을 제공합니다.
View 계층 구조의 특징
- 트리 구조: View 계층 구조는 하나의 루트 View를 시작으로 여러 개의 자식 View들이 연결되어 트리 형태를 이룹니다.
- 부모-자식 관계: 각 View는 하나의 부모 View를 가지며, 여러 개의 자식 View를 가질 수 있습니다.
- 레이아웃 관리: ViewGroup은 자식 View들의 크기와 위치를 결정하여 화면에 배치하는 역할을 합니다.
- 이벤트 전달: 터치, 클릭 등 사용자 이벤트는 View 계층 구조를 따라 전달됩니다.
View 계층 구조의 예시
DecorView (Root View)
ㄴ LinearLayout
ㄴ TextView
ㄴ ImageView
ㄴ Button
위 예시에서 DecorView는 루트 View이며, LinearLayout은 DecorView의 자식 View이자 ViewGroup입니다.
TextView, ImageView, Button은 LinearLayout의 자식 View입니다.
View 계층 구조의 활용
- UI 구성: View 계층 구조를 통해 다양한 UI 요소를 조합하여 복잡한 화면을 구성할 수 있습니다.
- 레이아웃 관리: ViewGroup을 사용하여 자식 View들의 배치를 효율적으로 관리할 수 있습니다.
- 커스텀 View 생성: View 클래스를 상속받아 새로운 View를 만들거나 기존 View의 기능을 확장할 수 있습니다.
- 이벤트 처리: View 계층 구조를 통해 사용자 이벤트를 처리하고 적절한 동작을 수행할 수 있습니다.
추가 정보
- View 계층 구조는 액티비티(Activity)가 시작될 때
setContentView()
메서드를 통해 설정됩니다. - View 계층 구조는
findViewById()
메서드를 통해 특정 View를 찾을 수 있습니다. - View 계층 구조는
getParent()
메서드를 통해 부모 View를,getChildCount()
및getChildAt()
메서드를 통해 자식 View들을 확인할 수 있습니다.
Android에서 ConstraintLayout을 사용하는 이유와 설명
ConstraintLayout은 Android UI 개발에서 복잡한 레이아웃을 효율적으로 구성하기 위해 설계된 강력한 ViewGroup입니다.
기존의 RelativeLayout이나 LinearLayout 등의 레이아웃 방식보다 더욱 유연하고 성능이 뛰어나다는 장점을 가지고 있습니다.
ConstraintLayout 사용 이유
- 복잡한 레이아웃 구현 용이:
- 제약 조건(constraints) 을 사용하여 View 간의 관계를 정의함으로써 복잡한 레이아웃을 간결하게 표현할 수 있습니다.
- 상대적인 위치, 정렬, 비율 등 다양한 제약 조건을 설정하여 View를 원하는 위치에 배치할 수 있습니다.
- Guideline, Barrier, Group 등의 기능을 활용하여 더욱 유연한 레이아웃 구성이 가능합니다.
- 중첩 레이아웃 감소:
- ConstraintLayout은 View 간의 관계를 직접적으로 정의하므로, 불필요한 중첩 레이아웃을 줄일 수 있습니다.
- 중첩 레이아웃은 UI 성능 저하의 주요 원인이 될 수 있으므로, ConstraintLayout을 사용하면 성능 향상을 기대할 수 있습니다.
- 성능 향상:
- ConstraintLayout은 내부적으로 효율적인 알고리즘을 사용하여 레이아웃을 계산합니다.
- 따라서 렌더링 속도가 빠르고, 특히 복잡한 레이아웃에서 성능 이점을 얻을 수 있습니다.
- 다양한 화면 크기 지원:
- ConstraintLayout은 다양한 화면 크기와 해상도에 대한 적응성이 뛰어납니다.
- 제약 조건을 사용하여 View의 크기와 위치를 상대적으로 설정하므로, 다양한 기기에서 일관된 UI를 유지할 수 있습니다.
ConstraintLayout은 다음과 같은 주요 기능을 제공합니다.
- 제약 조건 (Constraints): View의 위치, 크기, 정렬 등을 다른 View나 부모 레이아웃에 상대적으로 정의합니다.
- Guideline: View의 위치를 잡기 위한 보조선 역할을 합니다. 수직 또는 수평 Guideline을 생성하여 View를 정렬하거나 배치할 수 있습니다.
- Barrier: 여러 View의 위치를 기준으로 동적으로 생성되는 보이지 않는 장벽입니다. Barrier를 사용하여 특정 View들이 특정 위치를 넘어가지 않도록 제한할 수 있습니다.
- Group: 여러 View를 하나의 그룹으로 묶어서 관리합니다. 그룹 단위로 Visibility를 설정하거나 제약 조건을 적용할 수 있습니다.
- Chains: 여러 View를 연결하여 일렬로 배치하고 간격을 조절하는 기능입니다.
Android Data Binding
Android Data Binding은 UI 레이아웃과 데이터를 연결하여 UI 업데이트를 단순화하는 라이브러리입니다.
Data Binding의 핵심 기능:
데이터 바인딩 표현식: 레이아웃 파일에서 데이터를 직접 참조하고 조작할 수 있습니다.
- 예를 들어
@{user.name}
과 같이 데이터 객체의 속성을 참조하여 TextView에 표시할 수 있습니다.
- 예를 들어
이벤트 핸들링: 버튼 클릭과 같은 이벤트 발생 시 데이터 객체의 메서드를 호출하거나 특정 로직을 실행할 수 있습니다.
Observable 객체: 데이터가 변경될 때 UI를 자동으로 업데이트할 수 있도록 Observable 객체를 사용합니다. LiveData, ObservableField 등이 이에 해당합니다.
Binding Adapter: Data Binding 표현식에서 사용할 수 있는 커스텀 속성을 정의하고, 속성 값에 따라 View의 속성을 변경하는 로직을 구현할 수 있습니다.
Data Binding 사용 이유:
- UI 코드 간소화: UI 업데이트를 위한 findViewById() 및 setText()와 같은 반복적인 코드 작성을 줄여줍니다.
- 가독성 향상: UI 로직이 레이아웃 파일에 명확하게 표현되어 코드의 가독성이 좋아집니다.
- 오류 감소: 데이터 바인딩 표현식의 유효성을 컴파일 타임에 검사하여 런타임 오류 발생 가능성을 줄여줍니다.
- 유지보수성 향상: UI 로직과 데이터 로직이 분리되어 유지보수가 용이해집니다.
- 프로그래매틱 방식이 아니라, 선언적 방식을 사용하여 UI 구성요소를 데이터에 결합할 수 있는 지원 라이브러리
- 뷰를 데이터 객체와 결합하는 데 필요한 클래스를 자동으로 생성 (view와 data를 바인딩)
- DataBinding으로 인해 ViewModel이 View를 알지 못해도, 다양한 인터랙션 처리 가능
- Model에서 데이터가 변경되면, ViewModel에 전달
- 변경된 데이터가 ViewModel에서 View로 전달되도록 LiveData, RxJava, Kotlin Flow을 사용해 구현
- 옵저버 패턴 or Reactive(반응형) 프로그래밍
binding 클래스 자동 생성
Android Data Binding 라이브러리를 사용하면 레이아웃 파일(*.xml)에 대응하는 바인딩 클래스가 자동으로 생성됩니다.
이 바인딩 클래스는 레이아웃 파일의 UI 요소와 데이터를 연결하는 데 사용됩니다.
자동 생성 클래스 규칙
- 클래스 이름: 레이아웃 파일 이름을 파스칼 표기법(PascalCase)으로 변환하고 “Binding” 접미사를 붙입니다. 예를 들어
activity_main.xml
레이아웃 파일은ActivityMainBinding
클래스로 변환됩니다. - 클래스 위치:
build
디렉토리 안에 생성됩니다. (예:app/build/generated/data_binding_base_class_source_out/debug/out/com/example/databinding/
) - 클래스 구성:
- 레이아웃 파일의 각 View 요소에 대한 참조 변수
- 데이터 바인딩 표현식에 정의된 변수에 대한 getter/setter 메서드
inflate()
메서드: 레이아웃 파일을 inflate하고 바인딩 객체를 생성하는 정적 메서드
Android BindingAdapter
DataBinding에서, 데이터 바인딩 표현식과 View의 속성을 연결하는 역할을 합니다.
기본적으로 제공되는 BindingAdapter 외에도 개발자가 직접 정의하여 커스텀 속성과 View의 동작을 연결할 수 있습니다.
BindingAdapter의 주요 기능:
- 커스텀 속성:
@BindingAdapter
어노테이션을 사용하여 View의 속성과 연결할 커스텀 속성을 정의할 수 있습니다. 예를 들어, ImageView에 이미지 URL을 바인딩하는app:imageUrl
속성을 만들 수 있습니다. - 속성 값 변환: BindingAdapter 메서드를 통해 데이터 모델의 값을 View의 속성 값으로 변환하는 로직을 구현할 수 있습니다. 예를 들어,
app:imageUrl
속성에 전달된 URL 문자열을 Glide 라이브러리를 사용하여 이미지로 변환하여 ImageView에 표시할 수 있습니다.
Android View Binding vs. Data Binding 비교
Android View Binding과 Data Binding은 모두 레이아웃 파일의 View에 쉽게 접근하고 UI 업데이트를 용이하게 하는 데 사용되는 라이브러리입니다.
View Binding
- 핵심 기능:
- 레이아웃 파일에 정의된 View에 대한 참조를 자동으로 생성합니다.
findViewById()
메서드를 사용하지 않고 View에 직접 접근할 수 있습니다.- Null 안정성을 보장합니다. (ID가 없는 View에 접근하려고 하면 컴파일 오류 발생)
- 장점:
- 빌드 시간에 바인딩 클래스를 생성하므로 런타임 성능에 영향을 주지 않습니다.
- Data Binding보다 가볍고 간결합니다.
- findViewById() 메서드 사용 시 발생할 수 있는 null 관련 오류를 방지합니다.
- 단점:
- 레이아웃 파일과 코드 간의 데이터 바인딩 기능은 제공하지 않습니다.
- 양방향 데이터 바인딩이나 복잡한 UI 로직을 구현하기 어렵습니다.
Data Binding
- 핵심 기능:
- 레이아웃 파일과 데이터를 직접 연결하여 UI 업데이트를 자동화합니다.
- 데이터 변경 시 UI를 자동으로 갱신합니다.
- 이벤트 리스너를 간편하게 설정할 수 있습니다.
- BindingAdapter를 사용하여 커스텀 속성을 정의하고 View의 동작을 제어할 수 있습니다.
- 장점:
- UI 로직을 레이아웃 파일에 명시하여 코드의 가독성을 높입니다.
- 데이터 변경에 따른 UI 업데이트를 자동화하여 생산성을 향상시킵니다.
- 양방향 데이터 바인딩과 복잡한 UI 로직 구현이 가능합니다.
- 단점:
- 빌드 시간이 다소 증가할 수 있습니다.
- View Binding보다 학습 곡선이 가파릅니다.
- 복잡한 표현식 사용 시 런타임 성능에 영향을 줄 수 있습니다.
Android Custom View 설명
Android Custom View는 기본으로 제공되는 View 클래스들을 조합하거나 상속하여 개발자가 직접 정의하는 View입니다.
기존 View로 표현하기 어려운 복잡한 UI나 특정 기능을 구현할 때 유용하게 사용됩니다.
Custom View를 만드는 이유
- 재사용성: 자주 사용되는 UI 요소를 Custom View로 만들어 여러 곳에서 재사용할 수 있습니다.
- 캡슐화: Custom View 내부의 로직을 캡슐화하여 코드의 복잡성을 줄이고 유지보수를 용이하게 합니다.
- 유연성: 기존 View의 기능을 확장하거나 새로운 기능을 추가하여 원하는 디자인과 동작을 구현할 수 있습니다.
Custom View 종류
- 기존 View 조합: 여러 개의 기존 View를 조합하여 새로운 UI 요소를 만듭니다. 예: 복합적인 레이아웃, 특정 디자인의 버튼 등
- View 상속: View 클래스를 상속받아 새로운 View를 만듭니다. 예: 원형 ProgressBar, 특정 모양의 그래프 등
- ViewGroup 상속: ViewGroup 클래스를 상속받아 여러 자식 View를 포함하는 Custom View를 만듭니다. 예: RecyclerView와 유사한 리스트 뷰 등
Custom View 생성 방법
- 클래스 생성: View 또는 ViewGroup 클래스를 상속받아 새로운 클래스를 만듭니다.
- 생성자 정의:
Context
와AttributeSet
을 매개변수로 받는 생성자를 정의합니다. - 속성 정의:
attrs.xml
파일에 Custom View의 속성을 정의합니다. - 메서드 오버라이딩: 필요에 따라 View의 생명주기 메서드 (
onMeasure()
,onLayout()
,onDraw()
)를 오버라이딩하여 Custom View의 동작을 구현합니다.
RecyclerView
RecyclerView : 많은 수의 데이터 집합(Data Set)을 개별 아이템 단위로 구성하여 화면에 출력하는 뷰그룹(ViewGroup) 이며, 한 화면에 표시되기 힘든 많은 수의 데이터를 스크롤 가능한 리스트로 표시해주는 위젯입니다.
리스트뷰(ListView)
의 경우, 문제점 중의 하나는, 리스트 항목이 갱신될 때마다, 매번 아이템 뷰를 새로 구성해야 한다는 것이었습니다.- 리스트뷰의 단점을 참고하여, 리사이클러뷰는 아이템을 표시하기 위해 생성한 뷰를 재활용(recycle)합니다. 애니메이션 불가, 수직만 가능
Adapter : 리사이클러뷰에 표시될 ‘아이템 뷰’를 생성하는 역할은 어댑터가 담당합니다. 사용자 데이터 리스트로부터 ‘아이템 뷰’를 만드는 것, 그것이 바로 어댑터가 하는 역할이죠.
layoutManger : 레이아웃매니저는 리사이클러뷰가 아이템을 화면에 표시할 때, 아이템 뷰들이 리사이클러뷰 내부에서 배치되는 형태를 관리하는 요소입니다
ViewHolder : 뷰홀더(ViewHolder)는 화면에 표시될 아이템 뷰를 저장하는 객체입니다.
RecyclerView.Adapter
를 상속받아 새로운 어댑터를 만들 때, 오버라이드가 필요한 메서드는 아래와 같습니다.onCreateViewHolder() : ViewHolder를 새로 만들어야 할 때 호출되는 메서드이다. 이 메서드를 통해 각 아이템을 위한 XML 레이아웃을 이용한 뷰 객체를 생성하고 뷰 홀더에 담아 리턴한다. 이때는 뷰의 콘텐츠를 채우지 않는다. 왜냐하면 아직 ViewHolder가 특정 데이터에 바인딩된 상태가 아니기 때문이다.
onBindViewHolder() : ViewHolder 를 ’어떠한 데이터와 연결할 때’ 호출되는 메소드로, 이를 통해 ’뷰 홀더 객체들의 레이아웃’을 채우게 된다.
position
이라는 파라미터를 활용하여 데이터의 순서에 맞게 아이템 레이아웃을 바인딩해줄 수 있다.
ViewHolder 패턴은, 각 뷰의 객체를 ViewHolder 에 보관함으로써 뷰의 내용을 업데이트하기 위한
findViewById()
메소드 호출을 줄여 효과적으로 퍼포먼스 개선을 할 수 있는 패턴이다.ViewHolder 패턴을 사용하면, 한 번 생성하여 저장했던 뷰는 다시
findViewById()
를 통해 뷰를 불러올 필요가 사라지게 된다.- 만약 데이터가 1번부터 10번까지 10개 있고, 이를 리스트 형태로 보여줄 때 스마트폰의 화면 크기 상 1번부터 5번까지 보여준다고 가정해보자. 그럼 사용자가 스크롤을 하게 되면, 최상단에 있던 1번 및 2번 아이템의 레이아웃은 눈에 보이지 않게 될 것이다. 그와 동시에 6번 및 7번 아이템이 화면에 새롭게 보여지지 않겠는가?
- 이 때, 6번 및 7번 아이템을 화면에 표시하기 위해
findViewById()
를 일일히 호출하여 레이아웃에 데이터를 바인딩하지 않고, - 기존에 1번 및 2번 아이템을 그려줄 때 사용했던 View 를 재사용하여 이미 불러왔었던 레이아웃에 데이터만 채워주는 것이다.
- 재사용성을 높였을 뿐더러 불필요한 High-Cost 동작을 줄인 것이다.
Android RecyclerView 구성 요소
Android RecyclerView는 복잡한 리스트를 효율적으로 표시하기 위한 강력한 도구입니다.
ViewHolder 패턴: 각 아이템을 나타내는 ViewHolder를 사용하여 뷰 재활용을 통해 성능을 향상시킵니다.
1. RecyclerView:
- 역할: 화면에 아이템 목록을 표시하는 뷰(View)입니다. 스크롤, 아이템 추가/삭제 등의 기능을 담당합니다.
- 특징:
- 유연한 레이아웃: 다양한 LayoutManager를 사용하여 리스트, 그리드, 폭포수 등 다양한 형태로 아이템을 배치할 수 있습니다.
- 효율적인 뷰 재활용: ViewHolder 패턴을 사용하여 화면 밖으로 사라진 아이템 뷰를 재활용하여 메모리 사용량을 줄이고 성능을 향상시킵니다.
2. Adapter:
- 역할: 데이터와 RecyclerView를 연결하는 다리 역할을 합니다. 데이터를 ViewHolder에 바인딩하고, ViewHolder를 생성하여 RecyclerView에 제공합니다.
- 핵심 메서드:
onCreateViewHolder()
: ViewHolder 객체 생성, 이 메서드를 통해 각 아이템을 위한 XML 레이아웃을 이용한 뷰 객체를 생성하고 ViewHodler에 담아 리턴한다. 이때는 뷰의 콘텐츠를 채우지 않는다. 왜냐하면 아직 ViewHolder가 특정 데이터에 바인딩된 상태가 아니기 때문이다.onBindViewHolder()
: ViewHolder에 데이터를 바인딩합니다.getItemCount()
: 전체 아이템 개수를 반환합니다.
3. ViewHolder:
- 역할: 말 그대로, 각 아이템의 View를 담고(Hold) 있는 객체입니다. 뷰 재활용을 위해 뷰를 참조하고 데이터를 바인딩하는 역할을 합니다.
- 특징:
itemView
속성을 통해 아이템 뷰에 접근할 수 있습니다.- adapter의
onBindViewHolder()
에서 ViewHolder에서 우리가 작성한bind 함수
를 호출해 데이터를 바인딩합니다.
4. LayoutManager:
- 역할: 아이템 뷰를 화면에 배치하는 방식을 결정합니다.
- 종류:
LinearLayoutManager
: 아이템을 수직 또는 수평으로 배치합니다.GridLayoutManager
: 아이템을 격자 형태로 배치합니다.StaggeredGridLayoutManager
: 아이템을 엇갈리게 배치합니다.- (Custom LayoutManager): 개발자가 직접 정의한 레이아웃 방식을 사용할 수 있습니다.
5. ItemDecoration:
- 역할: 아이템 뷰 사이에 구분선, 간격 등을 추가하여 시각적인 효과를 줍니다.
6. ItemAnimator:
- 역할: 아이템 추가, 삭제, 이동 등의 변경 사항에 대한 애니메이션 효과를 제공합니다.
RecyclerView 작동 방식:
1. 초기화 단계:
- RecyclerView는 Adapter에게 화면에 표시될 아이템 개수(
getItemCount()
)를 요청합니다. - LayoutManager는 화면 크기와 아이템 크기를 기반으로 초기 화면에 표시할 아이템 개수를 계산합니다.
- RecyclerView는 Adapter에게 필요한 만큼의 ViewHolder를 요청합니다.
- Adapter는
onCreateViewHolder()
메서드를 호출하여 ViewHolder를 생성하고 각 아이템에 해당하는 레이아웃을 inflate 합니다. - RecyclerView는 생성된 ViewHolder를 LayoutManager에게 전달합니다.
- LayoutManager는 ViewHolder를 화면에 배치하고, Adapter에게 각 ViewHolder의 데이터 바인딩을 요청합니다.
- Adapter는
onBindViewHolder()
메서드를 호출하여 ViewHolder에 데이터를 설정합니다.
2. 스크롤 및 뷰 재활용 단계:
사용자가 RecyclerView를 스크롤하면 화면 밖으로 사라지는 아이템 뷰는 RecyclerView의 Recycler Pool로 이동합니다.
화면에 새로운 아이템이 나타나면 RecyclerView는 Recycler Pool에서 재활용 가능한 ViewHolder를 찾습니다.
재활용 가능한 ViewHolder가 있으면, Adapter는
onBindViewHolder()
메서드를 호출하여 새로운 데이터를 바인딩합니다.재활용 가능한 ViewHolder가 없으면, Adapter는
onCreateViewHolder()
메서드를 호출하여 새로운 ViewHolder를 생성합니다.LayoutManager는 새로운 또는 재활용된 ViewHolder를 화면에 배치합니다.
RecyclerView 장점:
- 유연성: 다양한 레이아웃과 아이템 유형을 지원하여 복잡한 UI를 쉽게 구현할 수 있습니다.
- 성능: ViewHolder 패턴과 뷰 재활용을 통해 대량의 데이터를 효율적으로 처리할 수 있습니다.
|
|
Android ListAdapter 설명
RecyclerView.Adapter를 상속받아 구현되었으며, DiffUtil을 내부적으로 사용하여 데이터 변경 시 효율적인 업데이트를 수행합니다.
ListAdapter의 주요 특징:
DiffUtil 기반 업데이트: DiffUtil을 사용하여 이전 목록과 새 목록 간의 차이점을 계산하고, 변경된 부분만 업데이트합니다.
- 이는
notifyDataSetChanged()
처럼 전체 목록을 새로고침하는 것보다 성능이 훨씬 좋습니다.
- 이는
백그라운드 스레드 활용: DiffUtil의 차이점 계산은 백그라운드 스레드에서 수행되므로 UI 스레드를 차단하지 않아 부드러운 UI를 유지할 수 있습니다.
간편한 사용:
submitList()
메서드를 통해 새 목록을 전달하면 ListAdapter가 알아서 차이점 계산 및 업데이트를 처리합니다.애니메이션 지원:
DefaultItemAnimator
와 함께 사용하면 변경된 아이템에 대한 애니메이션 효과를 자동으로 적용할 수 있습니다.
ListAdapter 사용 방법:
- ListAdapter 상속:
RecyclerView.Adapter
대신ListAdapter
를 상속받아 Adapter 클래스를 생성합니다. - DiffUtil.ItemCallback 구현: 데이터 클래스의 어떤 속성을 기준으로 아이템의 동일성과 내용 변경을 판단할지 정의하는 DiffUtil.ItemCallback 객체를 생성합니다.
- submitList() 호출: 데이터 변경 시
submitList()
메서드를 호출하여 새 목록을 전달합니다.
DiffUtil.ItemCallback 사용 방법:
ItemCallback 구현:
DiffUtil.ItemCallback
을 상속받아 추상 메서드인areItemsTheSame()
과areContentsTheSame()
를 구현합니다.DiffUtil.calculateDiff() 호출:
DiffUtil.calculateDiff()
메서드에 구현한ItemCallback
객체와 이전 목록, 새 목록을 전달하여DiffResult
객체를 얻습니다.1 2 3 4
val diffCallback = MyDiffCallback() val diffResult = DiffUtil.calculateDiff(diffCallback) diffResult.dispatchUpdatesTo(myAdapter)
Adapter 업데이트:
DiffResult
객체의dispatchUpdatesTo()
메서드를 호출하여 RecyclerView의 Adapter에 변경 사항을 알립니다.
ListAdapter 사용 시 주의 사항:
DiffUtil.ItemCallback을 정확하게 구현해야 합니다. 아이템의 동일성과 내용 변경을 정확하게 판단하지 못하면 업데이트가 제대로 이루어지지 않을 수 있습니다.
areItemsTheSame()
함수가 먼저 실행이 되고 해당 함수의 결과로 true 가 반환됐을 경우에만,areContentsTheSame()
이 호출됩니다.그렇기 때문에
areItemsTheSame()
에는 id 처럼 아이템을 식별할 수 있는 유니크한 값을 비교하고,areContentsTheSame()
에는 아이템의 내부 정보가 모두 동일한지 비교합니다.
submitList()
메서드 호출 시 이전 목록과 새 목록을 비교하는 데 시간이 소요될 수 있습니다. 따라서 너무 자주 호출하지 않도록 주의해야 합니다.ListAdapter는 RecyclerView.Adapter의 모든 기능을 지원하지 않을 수 있습니다. 필요한 기능이 있는지 확인하고, 필요하다면 직접 구현해야 합니다.