RecyclerView로 동적 목록 만들기 Android Jetpack의 구성요소
RecyclerView를 사용하면 대량의 데이터 세트를 효율적으로 표시할 수 있습니다. 개발자가 데이터를 제공하고 각 항목의 모양을 정의하면 RecyclerView 라이브러리가 필요할 때 요소를 동적으로 생성합니다.
이름에서 알 수 있듯이 RecyclerView는 이러한 개별 요소를 재활용합니다. 항목이 스크롤되어 화면에서 벗어나더라도 RecyclerView는 뷰를 제거하지 않습니다. 대신 RecyclerView는 화면에서 스크롤된 새 항목의 뷰를 재사용합니다. RecyclerView는 성능과 앱의 응답성을 개선하고 전력 소모를 줄입니다.
주요 클래스
동적 목록을 만드는 데는 여러 클래스가 함께 사용됩니다.
RecyclerView
는 데이터에 해당하는 뷰가 포함된ViewGroup
입니다. 이는 뷰 자체이므로, 다른 UI 요소를 추가할 때처럼 레이아웃에RecyclerView
를 추가하면 됩니다.목록의 각 개별 요소는 뷰 홀더 객체로 정의됩니다. 뷰 홀더가 생성되었을 때는 뷰 홀더에 연결된 데이터가 없습니다. 뷰 홀더가 생성된 후
RecyclerView
가 뷰 홀더를 뷰의 데이터에 바인딩합니다.RecyclerView.ViewHolder
를 확장하여 뷰 홀더를 정의할 수 있습니다.RecyclerView
는 뷰를 요청한 다음, 어댑터에서 메서드를 호출하여 뷰를 뷰의 데이터에 바인딩합니다.RecyclerView.Adapter
를 확장하여 어댑터를 정의할 수 있습니다.레이아웃 관리자는 목록의 개별 요소를 정렬합니다. RecyclerView 라이브러리에서 제공하는 레이아웃 관리자 중 하나를 사용하거나 레이아웃 관리자를 직접 정의할 수도 있습니다. 레이아웃 관리자는 모두 라이브러리의
LayoutManager
추상 클래스를 기반으로 합니다.
모든 요소가 RecyclerView 샘플 앱(Kotlin) 또는 RecyclerView 샘플 앱(Java)에서 조화를 이루며 작동하는지 확인할 수 있습니다.
RecyclerView 구현 단계
RecyclerView를 사용하려면 몇 가지 작업을 실행해야 합니다. 세부정보는 다음 섹션에 설명되어 있습니다.
목록 또는 그리드의 모양을 결정합니다. 일반적으로 RecyclerView 라이브러리의 표준 레이아웃 관리자 중 하나를 사용할 수 있습니다.
목록에 있는 각 요소의 모양과 동작 방식을 설계합니다. 이 설계에 따라
ViewHolder
클래스를 확장합니다. 사용 중인ViewHolder
버전은 목록 항목에 필요한 모든 기능을 제공합니다. 뷰 홀더는View
의 래퍼이고 그 뷰는RecyclerView
로 관리됩니다.데이터를
ViewHolder
뷰와 연결하는Adapter
를 정의합니다.
RecyclerView를 정확한 요구 사항에 맞게 조정할 수 있는 고급 맞춤설정 옵션도 있습니다.
레이아웃 계획
RecyclerView의 항목은 LayoutManager
클래스를 통해 정렬됩니다. RecyclerView 라이브러리는 가장 일반적인 레이아웃 상황을 처리하는 3가지 레이아웃 관리자를 제공합니다.
LinearLayoutManager
는 항목을 1차원 목록으로 정렬합니다.GridLayoutManager
는 항목을 2차원 그리드로 정렬합니다.- 그리드가 세로로 정렬된 경우
GridLayoutManager
는 각 행의 모든 요소를 동일한 너비와 높이로 만들려고 하지만 행마다 높이가 다를 수 있습니다. - 그리드가 가로로 정렬되는 경우
GridLayoutManager
는 각 열의 모든 요소를 동일한 너비와 높이로 만들려고 열마다 너비가 다를 수 있습니다.
- 그리드가 세로로 정렬된 경우
StaggeredGridLayoutManager
는GridLayoutManager
와 비슷합니다. 하지만 행의 항목이 동일한 높이(세로 그리드인 경우)이거나 동일한 열의 항목이 동일한 너비(가로 그리드인 경우)일 필요가 없습니다. 결과적으로 행 또는 열의 항목이 서로 오프셋 상태가 될 수 있습니다.
개별 항목의 레이아웃도 디자인해야 합니다. 다음 섹션에 설명된 것처럼 뷰 홀더를 설계할 때 이 레이아웃이 필요합니다.
어댑터 및 뷰 홀더 구현
레이아웃을 결정했으면 Adapter
및 ViewHolder
를 구현해야 합니다. 이 두 클래스가 함께 작동하여 데이터 표시 방식을 정의합니다. ViewHolder
는 목록에 있는 개별 항목의 레이아웃을 포함하는 View
의 래퍼입니다. Adapter
는 필요에 따라 ViewHolder
객체를 만들고 이러한 뷰에 데이터를 설정하기도 합니다. 뷰를 데이터에 연결하는 프로세스를 바인딩이라고 합니다.
어댑터를 정의할 때는 다음 세 가지 키 메서드를 재정의합니다.
onCreateViewHolder()
:RecyclerView
는ViewHolder
를 새로 만들어야 할 때마다 이 메서드를 호출합니다. 이 메서드는ViewHolder
와 그에 연결된View
를 생성하고 초기화하지만 뷰의 콘텐츠를 채우지는 않습니다.ViewHolder
가 아직 특정 데이터에 바인딩된 상태가 아니기 때문입니다.onBindViewHolder()
:RecyclerView
는ViewHolder
를 데이터와 연결할 때 이 메서드를 호출합니다. 이 메서드는 적절한 데이터를 가져와서 그 데이터를 사용하여 뷰 홀더의 레이아웃을 채웁니다. 예를 들어RecyclerView
가 이름 목록을 표시하는 경우 메서드는 목록에서 적절한 이름을 찾아 뷰 홀더의TextView
위젯을 채울 수 있습니다.getItemCount()
:RecyclerView
는 데이터 세트 크기를 가져올 때 이 메서드를 호출합니다. 예를 들어 주소록 앱에서는 총 주소 개수가 여기에 해당할 수 있습니다. RecyclerView는 이를 사용하여 더 이상 표시할 수 있는 항목이 없는 경우를 확인합니다.
다음은 간단한 어댑터의 일반적인 예로, 이 어댑터에는 데이터 목록을 표시하는 중첩된 ViewHolder
가 있습니다. 이 경우 RecyclerView는 텍스트 요소의 간단한 목록을 표시합니다. 어댑터에는 ViewHolder
요소의 텍스트가 포함된 문자열 배열이 전달됩니다.
Kotlin
class CustomAdapter(private val dataSet: Array<String>) : RecyclerView.Adapter<CustomAdapter.ViewHolder>() { /** * Provide a reference to the type of views that you are using * (custom ViewHolder) */ class ViewHolder(view: View) : RecyclerView.ViewHolder(view) { val textView: TextView init { // Define click listener for the ViewHolder's View textView = view.findViewById(R.id.textView) } } // Create new views (invoked by the layout manager) override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): ViewHolder { // Create a new view, which defines the UI of the list item val view = LayoutInflater.from(viewGroup.context) .inflate(R.layout.text_row_item, viewGroup, false) return ViewHolder(view) } // Replace the contents of a view (invoked by the layout manager) override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) { // Get element from your dataset at this position and replace the // contents of the view with that element viewHolder.textView.text = dataSet[position] } // Return the size of your dataset (invoked by the layout manager) override fun getItemCount() = dataSet.size }
자바
public class CustomAdapter extends RecyclerView.Adapter<CustomAdapter.ViewHolder> { private String[] localDataSet; /** * Provide a reference to the type of views that you are using * (custom ViewHolder) */ public static class ViewHolder extends RecyclerView.ViewHolder { private final TextView textView; public ViewHolder(View view) { super(view); // Define click listener for the ViewHolder's View textView = (TextView) view.findViewById(R.id.textView); } public TextView getTextView() { return textView; } } /** * Initialize the dataset of the Adapter * * @param dataSet String[] containing the data to populate views to be used * by RecyclerView */ public CustomAdapter(String[] dataSet) { localDataSet = dataSet; } // Create new views (invoked by the layout manager) @Override public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) { // Create a new view, which defines the UI of the list item View view = LayoutInflater.from(viewGroup.getContext()) .inflate(R.layout.text_row_item, viewGroup, false); return new ViewHolder(view); } // Replace the contents of a view (invoked by the layout manager) @Override public void onBindViewHolder(ViewHolder viewHolder, final int position) { // Get element from your dataset at this position and replace the // contents of the view with that element viewHolder.getTextView().setText(localDataSet[position]); } // Return the size of your dataset (invoked by the layout manager) @Override public int getItemCount() { return localDataSet.length; } }
각 뷰 항목의 레이아웃은 종전처럼 XML 레이아웃 파일에서 정의됩니다.
이 경우 앱에 다음과 같은 text_row_item.xml
파일이 들어 있습니다.
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="@dimen/list_item_height"
android:layout_marginLeft="@dimen/margin_medium"
android:layout_marginRight="@dimen/margin_medium"
android:gravity="center_vertical">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/element_text"/>
</FrameLayout>
다음 단계
다음 코드 스니펫은 RecyclerView
를 사용하는 방법을 보여줍니다.
Kotlin
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val dataset = arrayOf("January", "February", "March") val customAdapter = CustomAdapter(dataset) val recyclerView: RecyclerView = findViewById(R.id.recycler_view) recyclerView.layoutManager = LinearLayoutManager(this) recyclerView.adapter = customAdapter } }
자바
RecyclerView recyclerView = findViewById(R.id.recycler_view); recyclerView.layoutManager = new LinearLayoutManager(this) recyclerView.setAdapter(customAdapter);
또한 이 라이브러리는 구현을 맞춤설정할 수 있는 다양한 방법을 제공합니다. 자세한 내용은 고급 RecyclerView 맞춤설정을 참고하세요.
추가 리소스
Android에서 테스트하는 방법을 자세히 알아보려면 다음 자료를 참고하세요.