ListView는 이름에서도 알 수 있듯이 리스트 즉, 목록을 구현하는데 사용된다.
안드로이드에 임베디드 되어 있는 코드로 동작하며, API level 1부터 존재함
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
Holder holder = new Holder();
View rowView = inflater.inflate(R.layout.item_list, null);
holder.tv = (TextView) rowView.findViewById(R.id.text);
holder.img = (ImageView) rowView.findViewById(R.id.image);
holder.tv.setText(result[position]);
holder.img.setImageResource(imageId[position]);
rowView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
Toast.makeText(context, "You Clicked " + result[position], Toast.LENGTH_LONG).show();
}
});
return rowView;
}
// 가장 일반적인 ListView의 getView() 접근 방법
// 하지만 위와 같이 동작하게 되면 getView() 즉, ListView의 재사용성이 떨어지게 된다.
// inflate와 findViewById를 리스트뷰에서 연속적으로 발생시키면 메모리와 성능에 악영향을 미칠 수 있다.
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
// 최초에 convertView가 null이므로, inflate를 처리한다
if (convertView == null) {
// 전역으로 생성한 rootView에 inflate
rootView = inflater.inflate(R.layout.item_list, null);
// ViewHolder을 생성
Holder holder = new Holder();
holder.tv = (TextView) rowView.findViewById(R.id.text);
holder.img = (ImageView) rowView.findViewById(R.id.image);
// setTag : holder 임시 저장
rootView.setTag(holder);
} else {
// rootView에 convertView를 셋팅
rootView = convertView;
// rootView에서 holder을 꺼내온다
holder = (Holder) rootView.getTag();
}
holder.tv.setText(result[position]);
holder.img.setImageResource(imageId[position]);
rowView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
Toast.makeText(context, "You Clicked " + result[position], Toast.LENGTH_LONG).show();
}
});
return rootView;
}
하나의 리스트에 다양한 ViewHolder를 만들기가 쉽지 않다. 예를 들면 다음과 같은 경우
사진이 포함된 ViewHolder
텍스트만 있는 ViewHolder
오른쪽이 스크롤 되는 ListView가 포함된 ViewHolder
장점
단점
RecyclerView는 ListView의 문제를 해결하기 위해 개발자에게 더 다양한 형태로 커스터마이징 할 수 있도록 제공되었다. RecyclerView와 ListView의 가장 큰 차이점은 LayoutManager와 ViewHolder 패턴의 의무적인 사용, Item에 대한 뷰의 변형이나 애니메이션할 수 있는 개념이 추가된 것
Create Lists
creating Lists and Cards에 정의된 List 표현이다.
widget인 RecyclerView는 LayoutManager를 통해서 View를 그리는 방법을 정의한다. RecyclerView.Adapter에서는 Data의 ViewHolder 정의에 따라서 UI가 선택되고 이를 표현하게 된다.
주요 클래스
Adapter
ListView는 데이터가 어디서 왔느냐에 따라 BaseAdapter를 상속한 ArrayAdapter(배열로부터 데이터를 가져올 때 사용), CursorAdapter(DB로부터 데이터를 가져올 때 사용), SimpleAdapter(XMl 등으로부터 가져올 때 사용)를 구분하여 사용
RecyclerView는 Universal한 Adapter를 사용하여 데이터 소스를 처리한다. 이것은 리싸이클러뷰의 유연성을 보여준다. 다음의 3가지 인터페이스를 구현해야 한다.
onCreateViewHolder(ViewGroup parent, int viewType) : 뷰 홀더를 생성하고 뷰를 붙여주는 부분이다.
onBindViewHolder(CustomViewHolder holder, int position) : 재활용 되는 뷰가 호출하여 실행되는 메소드, 뷰 홀더를 전달하고 어댑터는 position의 데이터를 결합시킨다.
getItemCount() : 데이터의 개수 반환
getItemCount() → onCreateViewHolder() → onBindViewHolder() 순으로 호출
ListView가 사용했던 getView() 메소드는 매번 호출되면서 null 처리를 해주는 귀찮은 작업을 해줘야했다면, onCreateViewHolder는 새롭게 생성될 때만 호출
ViewHolder
리스트뷰에서는 뷰홀더 패턴을 권장했다. UI를 수정할 때마다 부르는 findViewById()를 뷰홀더 패턴을 이용해 한번만 호출함으로써 리스트뷰의 지연을 초래하는 무거운 연산을 줄여주었다. 이 문제를 리싸이클러뷰에서는 뷰홀더 패턴을 항상 사용하도록(강제하도록) 함으로써 해결했다.
하지만 실제로 앱의 퍼포먼스를 향상시켜주지만 최신의 디바이스는 뷰홀더 패턴을 사용하지 않은 리스트뷰나 리싸이클러뷰의 성능 차이는 미세하다.
단지 차이점은 리싸이클러뷰는 뷰홀더 패턴이 강제되는 것일 뿐이다. 이전의 리스트뷰는 선택적이었지만 성능 차이가 너무 컸기 때문에 변화된 것으로 생각된다. 간과하기 쉬운 중요한 점은 뷰홀더 패턴을 사용한 리스트뷰와 리싸이클러뷰의 성능은 같다.
LayoutManager
LayoutManager의 종류
Item Decoration
ListView에서는 XML에 파라미터를 추가함으로써 쉽게 divide할 수 있었다.
RecyclerView에서는 RecyclerView.ItemDecoration 클래스를 통해 divider를 원하는 아이템에 추가할 수 있도록 되었다. 조금 복잡해졌지만 동적인 데코레이팅이 가능
Item Animator
Material Design에 대해 조명된 이후로 리스트에서의 애니메이션을 무궁무진한 가능성을 가지게 되었다. 리스트뷰에서는 아이템의 삽입이나 삭제에 특별한 애니메이션이 없었다. 하지만,
리싸이클러뷰에서는 RecyclerView.ItemAnimator 클래스를 통해 애니메이션을 핸들링 할 수 있게 되었다.
이 클래스를 통해서 아이템의 삽입, 삭제, 이동에 대한 커스터마이징이 가능하고, 또한 DefaultItemAnimator가 제공되므로 커스터마이징이 필요 없이 사용할 수도 있다.
notifyItemChanged(int position), notifyItemInserted(int position), notifyItemRemoved(int position)을 ItemAnimator을 통해 특정 아이템에 대한 애니메이션을 발생시킬 수 있습니다.
클릭 이벤트 처리
터치 이벤트를 통해 사용자가 아이템을 클릭했는지 롱클릭 했는지를 직접처리
RecycleView.OnItemTouchListener은 리싸이클러뷰의 터치 이벤트를 감지한다. 좀 복잡하지만 개발자에게 터치 이벤트를 인터셉트하는 제어권한을 주게 되었다.
안드로이드 공식 문서에서는 터치 이벤트를 인터셉트함으로서 리싸이클러뷰에게 전달하기 전에 조작함으로써 유용하게 사용될 수 있다고 한다.(ListView에서 아이템을 클릭시 콜백 받을 수 있는 리스너는 RecyclerViewd에는 존재하지 않음.)
즉, RecyclerView에는 Click 이벤트에 대한 처리를 자체적으로 할 수 없다. 그래서 onClickListener를 달아줘야 하는 문제가 발생한다.
사용하는 방법으로는 RecyclerView를 사용하는 액티비티에서 View.OnClickListener를 상속받고 그 액티비티의 Context를 RecyclerView.Adapter에서 만든 함수에게 넘긴다.
이 함수는 액티비티에서 받은 Context(여기에 View.OnClickListener이 포함되어 있음)를 Adapter 클래스의 View.OnClickListener 타입의 변수인 ItemClick을 초기화
public class ViewHolder extends BaseViewHolder<Post> {
private ViewHolder(View itemView) {
super(itemView);
itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 클릭이벤트설정
}
});
}
DiffUtil
다음 세가지를 비교/정리