RecyclerView
RecyclerView는 ListView와 비슷하나 조금 더 진보한 View입니다. RecyclerView는 ListView에 비해서 많은 최적화를 거치게 됐고, 수 많은 데이터를 처리하는데 효과적입니다.
그렇다면 RecyclerView가 어떻게 사용되는지 알아보도록 할까요? 여기서는 RecyclerView의 사용법을 위주로 설명할 것이기 때문에 디자인은 저 멀리 버리도록 하겠습니다.
1) RecyclerView를 사용하기 전에 라이브러리를 추가
File - Project Structure - App - Dependancies 의 오른쪽 +를 누른 후 recyclerview를 검색하여
com.android.support:recyclerview를 추가합시다.
androidx를 사용하신다면 이 과정은 건너 뛰어도 상관없겠네요.
2) activity_main.xlm에 RecyclerView 추가
간단하게 RecyclerView만을 추가합니다.
<?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:orientation="vertical"
tools:context=".MainActivity">
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/recyclerView"/>
</LinearLayout>
3) item_recycler_view.xlm 추가
RecyclerView에 사용할 item입니다. 간단하게 TextView하나만을 포함하고 있습니다.
<?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="wrap_content"
android:orientation="vertical"
android:padding="20dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="20dp"
android:id="@+id/item"/>
</LinearLayout>
이제 준비는 끝났습니다. item_recycler_view를 Adapter의 ViewHolder에 이어주고 이 Adapter를 RecyclerView에 설정하면 됩니다.
4) MainActivity의 RecyclerView에 Adapter 설정
아직 RecyclerAdapter가 없기 때문에 오류가 발생하는데 우선 할당해놓고 이후에 Adapter class를 만들어봅시다.
이후 딱히 설명할 건 없습니다.
몇가지 첨언한다면..
RecyclerView에 setLayoutManager는 어떤 레이아웃을 사용할 것인지 설정하는데, 여기서는 LinearLayoutManager를 사용했습니다.
격자형을 사용하고 싶다면 GridLayoutManager를 사용하시기 바랍니다.
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
RecyclerView recyclerView=findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(new RecyclerAdapter());
}
}
5) RecyclerAdapter(Adapter class) 정의
우리는 우선 RecyclerView.Adapter를 상속을 받아야합니다.
5-1) ViewHolder 정의
Adpater안에는 반드시 ViewHolder가 있어야하는데 이 ViewHolder는 RecyclerView의 개별 Item의 대한 view를 가지고 있습니다. item_recycler_view가 이 ViewHolder의 View로 생성되며 필요에 따라 custom할 수 있겠죠?
비록 우리는 item_recycler_view에 TextView만 추가했지만 뭐 ImageView도 추가할 수 있고 말이죠.
5-2) 메소드 Overriding
이제 필수적으로 Overriding할 메소드는 다음과 같습니다.
- onCreateViewHolder : ViewHolder를 콘텐츠를 표시하기 위해 사용하는 뷰를 설정하는데, 우리는 이전에 item_recycler_view를 만들었으니 이것을 사용합니다.
- onBindViewHolder : ViewHolder를 binding합니다. 여기서 ViewHolder의 각각 View를 설정할 수 있습니다.
- getItemCount : item의 갯수를 반환합니다. 대부분은 List를 사용하기 때문에 이 List의 size를 반환합니다.
import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{
private List<String> itemList=new ArrayList<>();
public RecyclerAdapter(){
itemList.clear();
for(int i=0;i<10;i++){
itemList.add(i+"번째 아이템");
}
notifyDataSetChanged();
}
class ViewHolder extends RecyclerView.ViewHolder{
private TextView item;
public ViewHolder(View itemView){
super(itemView);
item=(TextView)itemView.findViewById(R.id.item);
}
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
View view=LayoutInflater.from(viewGroup.getContext())
.inflate(R.layout.item_recycler_view,viewGroup,false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, final int i) {
ViewHolder holder=(ViewHolder)viewHolder;
holder.item.setText(itemList.get(i));
}
@Override
public int getItemCount() {
return itemList.size();
}
}
ViewHolder를 정의하는데 여기서는 아까 만들었던 item_recycler_view.xlm의 View를 갖고 있습니다. 이 ViewHolder는 개별 item을 갖고 있는 것이나 같죠. 이 ViewHolder는 onCreateViewHolder에서 객체가 생성됩니다.
생성자에는 itemList를 초기화하는데 이 itemList에서 adapter가 binding할때 아이템을 꺼내옵니다. 우선 10개의 아이템을 추가하고 있네요.
이후 notifyDataSetChanged()를 호출하면 이제 데이터의 Set이 바뀌었다고 다시 RecyclerAdapter의 데이터를 표시하게됩니다.
즉, itemList의 아이템이 생기거나 제거됐다면 이후 notifyDataSetChanged()를 호출하세요.
onBindViewHolder에는 ViewHolder에 있는 TextView의 문자열 설정을 하고 끝나네요.
여기까지 잘따라왔다면 프로젝트 구성은 이렇게 되겠네요.
결과
이벤트 추가
여기까지만 진행하면 조금 심심하겠죠? 이제 아이템이 클릭이 되면 Toast로 클릭되었다는 메시지를 띄어봅시다.
interface를 사용하여 listener를 설정할때가 왔군요.
6) RecyclerAdapter에 OnItemClickListener 설정
여기서는 OnItemClickListener라는 interface를 정의하고 객체를 생성합니다.
onBindViewHolder에서 ViewHolder의 아이템이 클릭되었을때 onItemClickListener의 onClick 메소드를 호출합니다. 이 onClick메소드는 바로 MainActivity에서 구현하거나 객체 생성해서 넘길테지요.
import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{
private List<String> itemList=new ArrayList<>();
public interface OnItemClickListener{
public void onClick(String str);
}
private OnItemClickListener onItemClickListener=null;
public RecyclerAdapter(){
itemList.clear();
for(int i=0;i<10;i++){
itemList.add(i+"번째 아이템");
}
notifyDataSetChanged();
}
class ViewHolder extends RecyclerView.ViewHolder{
private TextView item;
public ViewHolder(View itemView){
super(itemView);
item=(TextView)itemView.findViewById(R.id.item);
}
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
View view=LayoutInflater.from(viewGroup.getContext())
.inflate(R.layout.item_recycler_view,viewGroup,false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, final int i) {
ViewHolder holder=(ViewHolder)viewHolder;
holder.item.setText(itemList.get(i));
holder.item.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(onItemClickListener!=null){
onItemClickListener.onClick(itemList.get(i)+" 클릭!!");
}
}
});
}
@Override
public int getItemCount() {
return itemList.size();
}
public void setOnItemClickListener(OnItemClickListener onItemClickListener){
this.onItemClickListener=onItemClickListener;
}
}
7) MainActivity에서 onClick 구현
별로 특별한 기능은 하지 않고 onClick시 Toast를 이용해 메시지를 보여줍니다.
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
RecyclerView recyclerView=findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
RecyclerAdapter adapter=new RecyclerAdapter();
recyclerView.setAdapter(adapter);
adapter.setOnItemClickListener(new RecyclerAdapter.OnItemClickListener() {
@Override
public void onClick(String str) {
Toast.makeText(MainActivity.this,str,Toast.LENGTH_LONG).show();
}
});
}
}
인터페이스를 사용하여 MainActivity에서 Toast 메시지를 실행시킨 이유는 Activity에서만 사용할 수 있는 메소드를 사용하기 위해서입니다(예를 들면 startActivity).
물론 RecyclerAdapter에 MainActivity의 Context를 전달해서 구현할 수도 있었지요.
이제 아이템을 누르면 아래와 같이 메시지가 나타납니다.
특정 위치에 다른 View 설정
맨 처음 또는 특정 position에 다른 item을 할당하고 싶다면 어떻게 해야할까요?
그 경우에는 getItemViewType를 Overriding하여 해결할 수 있습니다.
getItemViewType은 인자로 전달된 position에 따라 ViewType이 무엇인지 결정하고
onCreateViewHolder에 두번째 인자로 그 ViewType으로 전달됩니다. onCreateViewHolder는 그 뷰타입을 보고 생성되는 ViewHolder를 결정하면 되지요.
저는 0번째 item만 다른 view로 보여주려고 합니다.
1) item_header.xml
우선 0번째 아이템을 위해 사용할 layout은 다음과 같습니다. 아주 간단하지요.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/header_msg"
android:textAlignment="center"
android:paddingTop="20dp"
android:paddingBottom="20dp"/>
</LinearLayout>
2) RecyclerAdapter에 HeaderViewHolder 추가
필요한 부분만 간추려서 보면 아래의 코드와 같습니다. 우선 HeaderViewHolder라는 0번째 아이템을 위한 ViewHolder를 추가하지요.
0번째 아이템이면 TYPE_HEADER라는 ViewType을, 그 외에는 평범한 아이템을 나타내는 TYPE_ITEM이라는 것을 알려주는 getItemViewType을 오버라이딩하고 있네요.
이제 이 ViewType은 onCreateViewHolder의 두번째 인자인 i로 전달됩니다. 여기서 ViewHolder를 두 가지로 나눠서 반환하게 됩니다. HeaderViewHolder 또는 ViewHolder(RecyclerAdapter에서 정의한)로 말입니다.
이후 onBindViewHolder에서 각각 다른 처리를 하면 됩니다.
public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{
...
private final int TYPE_HEADER=0;
private final int TYPE_ITEM=1;
public RecyclerAdapter(){
itemList.clear();
itemList.add("Header Message");
for(int i=0;i<10;i++){
itemList.add(i+"번째 아이템");
}
notifyDataSetChanged();
}
...
class HeaderViewHolder extends RecyclerView.ViewHolder{
private TextView headerItem;
public HeaderViewHolder(View itemView){
super(itemView);
headerItem=(TextView)itemView.findViewById(R.id.header_msg);
}
}
@Override
public int getItemViewType(int position) {
if(position==0) return TYPE_HEADER;
return TYPE_ITEM;
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
if(i==TYPE_HEADER){
return new HeaderViewHolder(LayoutInflater.from(viewGroup.getContext())
.inflate(R.layout.item_header,viewGroup,false));
}
return new ViewHolder(LayoutInflater.from(viewGroup.getContext())
.inflate(R.layout.item_recycler_view,viewGroup,false));
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, final int i) {
if(i==0){
((HeaderViewHolder) viewHolder).headerItem.setText(itemList.get(i));
return;
}
((ViewHolder) viewHolder).item.setText(itemList.get(i));
}
...
}
이후 결과는 아래와 같습니다. 0번째 아이템이 다른 view를 가지고 있지요.
'안드로이드' 카테고리의 다른 글
[안드로이드] 서울시 버스 도착 정보 조회 api 사용 - 공공데이터포털 Open API (0) | 2021.05.01 |
---|---|
[안드로이드] RecyclerView 수평 이동 및 한번에 항목 하나만 보이게 만들기 (0) | 2020.07.01 |
[안드로이드] AsyncTask를 이용하여 시계(Timer) 구현 (0) | 2020.06.29 |
[안드로이드] ViewPager와 TabLayout으로 slide화면 구현 (0) | 2020.03.22 |
android 프래그먼트(Fragment) (0) | 2018.09.27 |