-
[Android] RecyclerView 사용하기프로그래밍/Android 2019. 1. 21. 16:42
요즘 나이 먹으면 시간이 빨리 간다는 말에 격하게 공감이 가네요.....
새해가 밝은지 얼마나 됐다고 벌써 1월도 끝나가다니.. ㅠ
다들 연초에 세웠던 계획들 아직 잘 지켜지고 계신가요?
나름 지킬 수 있을 만한 것들로 세웠는데도 참 쉽지가 않네요. 그래도 계획이 조금 틀어졌다고 해서 포기하는 것보다 끝까지 노력하려고 하는 마음이 중요하다고 생각합니다. 모두들 열심히 노력해서 원하시는 결과 얻으시길
오늘은 RecyclerView에 대해 알아보려 합니다. RecyclerView는 ListView와 마찬가지로 리스트 형태의 데이터를 표현하기에 적합한 위젯인데요, ListView와는 어떤 차이가 있는지, 어떻게 사용해야 하는지에 대해 알아보도록 하겠습니다.
RecyclerView?
RecyclerView는 Lollipop(Anroid 5.0) 버전에서 처음 소개되었는데요, ListView보다 유연하고 성능이 향상되었다고 합니다. 그렇다면, RecyclerView에 대해 얘기하기 전에 ListView에 대해 먼저 알아야겠죠?
ListView에 대한 내용은 이전 포스트를 통해 확인하시기 바랍니다.
ListView 사용하기
그럼, 이제 RecyclerView와 ListView가 어떤 차이가 있는지 간단히 짚고 넘어가도록 하겠습니다.
ViewHoler 패턴의 의무화
ListView에서는 선택적으로 권장되던 ViewHolder 패턴이 RecyclerView에서는 이제 의무적으로 꼭! 사용하도록 변경 되었습니다.
LayoutManager 제공
ListView에서는 기본적으로 세로 타입의 리스트만 지원합니다. 물론 커스텀을 통해 여러 타입의 리스트들을 구현 할 수 있지만, 복잡한 과정을 거쳐야 했습니다. RecyclerView에서는 LayoutManager를 통해 여러 타입의 리스트들을 지원하고 커스텀 할 수 있도록 지원합니다.
LinearLayoutManager
수직 또는 수평 타입의 리스트를 지원합니다.
GridLayoutManager
격자형 타입의 리스트를 지원합니다.
StaggeredGridLayoutManager ItemView 마다 크기가 다른 리스트를 만들 수 있도록 지원합니다.
ItemDecoration 클래스 제공
ListView에서는 ItemView 사이의 간격에 대한 설정을 Divider를 통해 설정 하였다면, RecyclerView에서는 ItemDecoration 클래스를 통해 설정이 가능합니다. 다소 사용하기에 복잡해지긴 했지만 좀 더 동적인 사용과 커스텀에 유리해졌습니다.
ItemAnimator 클래스 제공
RecyclerView에서는 애니메이션에 대한 지원을 위해 ItemAnimator 클래스를 제공합니다. ItemAnimator 클래스를 통해 ItemView의 삽입이나 삭제, 이동 등의 애니메이션을 커스텀 할 수 있습니다.
터치 이벤트 강화
ListView에서는 OnItemClickListener를 통해 아이템들의 클릭 여부를 감지 했는데요, RecyclerView에서는 onItemTouchListener를 통해 뷰의 터치 이벤트를 감지합니다. 조금 복잡해 보이실 수 있겠지만, 터치 이벤트를 RecyclerView에 전달하기 전에 제어권을 가져옴으로써, 좀 더 유연한 대처가 가능해졌습니다.
RecyclerView는 ListView에 비해 좀 더 사용자의 확장성에 중점을 둔 모습이네요.소개는 이쯤 마무리 하고 바로 실전으로 넘어가 보도록 하겠습니다.Gradle에 라이브러리 추가하기
RecyclerView는 Supprot Library에 포함되어 있으므로, 사용을 위해서는 gradle에 추가해주어야 합니다.
build.gradle (Module: app)
dependencies {
implementation 'com.android.support:recyclerview-v7:26.1.0'
}
버전은 다른 Support Library와 동일하게 맞추어 주어야 합니다.
레이아웃에 RecyclerView 추가하기
이어서, 레이아웃을 먼저 작성하겠습니다.
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#000000"
tools:context="rebuild.com.scenetransitionanimation.Activity.MainActivity">
<android.support.v7.widget.RecyclerView
android:id="@+id/rcc_album"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:overScrollMode="never"
android:scrollbars="none" />
</LinearLayout>
Adapter 클래스 구현하기
Adapter를 작성하기에 앞서, 각 아이템에 사용 될 레이아웃을 먼저 꾸며보겠습니다.
custom_album_item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/layout_album_panel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageView
android:id="@+id/img_thumb"
android:layout_width="match_parent"
android:layout_height="150dp"
android:scaleType="centerCrop" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingBottom="5dp"
android:paddingTop="5dp">
<TextView
android:id="@+id/txt_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#ffffff"
android:textSize="16dp" />
<TextView
android:id="@+id/txt_artist"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#8e8e8e"
android:textSize="14dp" />
</LinearLayout>
</LinearLayout>
간단하게 앨범의 썸네일 이미지를 배치 할 ImageView와 앨범의 타이틀과 가수를 표시 할 TextView들을 배치하였습니다.
이제 데이터와 RecyclerView를 연결할 Adapter를 만들어 보겠습니다.
AlbumAdapter.java
public class AlbumAdapter extends RecyclerView.Adapter<AlbumAdapter.ViewHolder> {
private Context mContext;
private ArrayList<AlbumVO> list_album;
public OnItemClickListener mOnItemClickListener = null;
public interface OnItemClickListener {
void onItemClick(View view, AlbumVO albumVO);
}
public void setOnItemClickListener(OnItemClickListener listener) {
mOnItemClickListener = listener;
}
public AlbumAdapter(Context mContext, ArrayList<AlbumVO> list_album) {
this.mContext = mContext;
this.list_album = list_album;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View convertView = LayoutInflater.from(mContext).inflate(R.layout.custom_album_item, parent, false);
return new ViewHolder(convertView);
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
AlbumVO albumVO = list_album.get(position);
Glide.with(mContext)
.load(albumVO.getThumb())
.thumbnail(0.5f)
.into(holder.img_thumb);
holder.txt_artist.setText(albumVO.getArtist());
holder.txt_title.setText(albumVO.getTitle());
holder.layout_album_panel.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mOnItemClickListener.onItemClick(v, albumVO);
}
});
}
@Override
public int getItemCount() {
return list_album.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
private LinearLayout layout_album_panel;
private ImageView img_thumb;
private TextView txt_artist;
private TextView txt_title;
public ViewHolder(View convertView) {
super(convertView);
layout_album_panel = (LinearLayout) convertView.findViewById(R.id.layout_album_panel);
img_thumb = (ImageView) convertView.findViewById(R.id.img_thumb);
txt_artist = (TextView) convertView.findViewById(R.id.txt_artist);
txt_title = (TextView) convertView.findViewById(R.id.txt_title);
}
}
}
Adapter는 RecyclerView.Adapter<ViewHolder viewHolder>를 상속 받아 작성하였는데요, 어떻게 구성되어 있는지 살펴보도록 하겠습니다.
onCreateViewHolder(ViewGroup parent, int ViewType)
ViewHolder 생성 시에 호출됩니다. ViewType에 따라 여러 타입의 ViewHolder를 생성할 수도 있습니다.
처음 화면에 보이는 View에 대해 ViewHolder가 생성되며, 이후 스크롤 시에 보여지는 View들은 재활용되는 View이므로 다시 호출되지 않습니다.
onBindViewHolder(ViewHolder holder, int position)
화면에 ViewHolder가 붙을 때마다 호출됩니다. 화면에 새로운 View가 붙을 때마다 호출 되므로, 실질적인 데이터 처리는 이곳에서 이루어집니다.
getItemCount()
Item의 개수를 반환 합니다. 반환 되는 개수에 따라 생성되는 Item의 개수가 정해집니다.
위의 예제에서는 onCreateViewHolder()에서 만들어두었던 레이아웃을 ViewHolder와 연결하고, onBindViewHolder()에서는 넘겨 받은 데이터를 가져와 썸네일 이미지, 타이틀, 가수명 등을 각각 ImageView와 TextView에 세팅하는 것을 확인 하실 수 있습니다.
OnItemClickListener에 사용 된 커스텀 이벤트 리스너에 대한 상세한 내용은 지난 포스팅을 참고하시기 바랍니다.
Custom EventListener 사용하기
LayoutManager 설정 및 Adapter 연결하기
이어서, MainActivity에서 RecyclerView를 설정하는 방법에 대해 알아보겠습니다.
MainActivity.java
public class MainActivity extends AppCompatActivity implements AlbumAdapter.OnItemClickListener {
private Context mContext;
private RecyclerView rcc_album;
private AlbumAdapter mAlbumAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mContext = this;
init();
}
private void init() {
rcc_album = (RecyclerView) findViewById(R.id.rcc_album);
RecyclerView.LayoutManager mLayoutManager = new GridLayoutManager(mContext, 2);
rcc_album.setLayoutManager(mLayoutManager);
mAlbumAdapter = new AlbumAdapter(mContext, getAlbumList());
mAlbumAdapter.setOnItemClickListener(this);
rcc_album.setAdapter(mAlbumAdapter);
}
@Override
public void onItemClick(View view, AlbumVO albumVO) {
Log.e("RecyclerVIew :: ", albumVO.toString())
}
}
LayoutManager를 통해 리스트의 형태를 GridLayout으로 지정 한 뒤, RecyclerView.setLayoutManager()로 RecyclerView에 적용해주었습니다. 또한 좀 전에 만든 AlbumAdapter를 RecyclerView.setAdapter를 통해 연결해주었습니다. 간단하죠?
이렇게 설정을 끝마쳐도 결과는 잘 나오겠지만, 리스트의 Item 사이의 간격을 조절해보기 위해 ItemDecoration을 사용해 보도록 하겠습니다.
ItemDecoration 적용하기
ItemDecoration.java
public class ItemDecoration extends RecyclerView.ItemDecoration {
private int spanCount;
private int spacing;
private int outerMargin;
public ItemDecoration(Activity mActivity) {
spanCount = 2;
spacing = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
12, mActivity.getResources().getDisplayMetrics());
outerMargin = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
50, mActivity.getResources().getDisplayMetrics());
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
int maxCount = parent.getAdapter().getItemCount();
int position = parent.getChildAdapterPosition(view);
int column = position % spanCount;
int row = position / spanCount;
int lastRow = (maxCount - 1) / spanCount;
outRect.left = column * spacing / spanCount;
outRect.right = spacing - (column + 1) * spacing / spanCount;
outRect.top = spacing * 2;
if (row == lastRow) {
outRect.bottom = outerMargin;
}
}
}
ItemDecoration 클래스는 RecyclerView.ItemDecoration 클래스를 상속받아 작성하였습니다. Item의 간격의 설정은 Override된 getItemOffsets()에서 해주시면 됩니다. 예제에서는 복잡하게 작성된 듯 보이지만 실질적으로 outRect의 left, right, top, bottom 값만 조절 해주시면 됩니다.
그럼 다시 Activity로 돌아와서,
MainActivity.java
public class MainActivity extends AppCompatActivity implements AlbumAdapter.OnItemClickListener {
private void init() {
rcc_album = (RecyclerView) findViewById(R.id.rcc_album);
RecyclerView.LayoutManager mLayoutManager = new GridLayoutManager(mContext, 2);
rcc_album.setLayoutManager(mLayoutManager);
rcc_album.addItemDecoration(new ItemDecoration(this))
mAlbumAdapter = new AlbumAdapter(mContext, getAlbumList());
rcc_album.setAdapter(mAlbumAdapter);
}
}
이렇게 한 줄만 추가해주시면 되겠습니다.
실행 화면
예제 실행 화면
이번에는 RecyclerView에 대해 간단히 알아보았는데요, 사실 깊게 파고자 하면 끝이 없어서 어디까지 포스팅을 해야할지 고민이 많습니다.
다음 포스팅은 오늘 예제에 이어 Scene Transition Animation에 대해 알아보도록 하겠습니다.
'프로그래밍 > Android' 카테고리의 다른 글
[Android] Shared Elements Transition (2) 2019.01.28 [Android] GSON 라이브러리 사용하기 (0) 2019.01.17 [Android] Circular Reveal Amimation (0) 2019.01.09 [Android] SharedPreferences 사용하기 (2) 2019.01.04 [Android] 해상도별 레이아웃 대응에 대한 고찰 (5) 2018.10.23