이번에 MVVM 패턴에 대해서 공부하던중 튜토리얼 강의가 Recycler View를 토대로 만들길래
우선적으로 Recycler View에 대해서 공부를 해봤다.
Recycle View란?
RecyclerView는 이름에 있는 Recycler 라는 단어만으로도 대충 감이 잡힐거다.
Recycle의 단어 뜻은 재활용하다 라는 뜻을 가지고 있다.
이처럼 Recycler View는 리스트내에 아이템을 어떤 특정 View를 가지고
계속해서 재활용해가면서 사용하는 라이브러리이다.
(특정 View를 재활용해서 쓰기 때문에 리스트내에 아이템을 생성할때마다 새로운 View를 연결해주는 ListView보다 성능이 좋다.)
RecyclerView 사용법
1. 라이브러리 추가
Recycler View를 사용하려면 dependencies에서 라이브러리를 추가해야한다.
build.gradle(Module:app) 파일에서 dependencies부분을 수정하였다.
나는 안드로이드 스튜디오 버전이 높아서 androidx로 migrate 시켜줬기 때문에 그에 맞는 라이브러리를 추가하였다.
//recyclerview
implementation "androidx.recyclerview:recyclerview:1.2.1"
그리고 내가 보고 따라한 튜토리얼에서는 이미지를 동그랗게 만드는 라이브러리와
사진을 인터넷으로부터 가져오게 해주는 라이브러리를 추가해서 해당 라이브러리로 RecyclerView를 만들었기때문에
해당 라이브러리도 같이 추가해줬다.
//glide 라이브러리 : 사진을 인터넷으로부터 가져오게 해주는 라이브러리
implementation 'com.github.bumptech.glide:glide:4.12.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0'
//circle imageview 라이브러리 : 이미지를 동그랗게 만들어주는 라이브러리
implementation 'de.hdodenhof:circleimageview:3.1.0'
그리고 위에서 만든 라이브러리는 인터넷과 연결해야하기 때문에 manifest에서 인터넷 사용할수 있게 권한을 줘야 한다.
<!-- 사진을 인터넷으로 가져오는 라이브러리때문에 인터넷 사용가능하게 만들어줘야 함 -->
<uses-permission android:name="android.permission.INTERNET"/>
이렇게 라이브러리와 manifest 추가를 해줬다면 이제 바뀐 gradle을 프로젝트와 다시 연결하여서
추가한 라이브러리를 사용할수 있게 해줘야 한다.
해당 버튼은 우측상단에 있다.
gradle이 프로젝트와 성공적으로 연결이 되었다면 이제 라이브러리를 사용할수 있게된다.
2. xml에서 RecyclerView 만들기
이제 xml에서 RecyclerView를 만들어줘야 한다.
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:orientation="vertical">
<androidx.recyclerview.widget.RecyclerView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/recycler_view">
</androidx.recyclerview.widget.RecyclerView>
</RelativeLayout>
이부분은 메인 액티비티 화면에 recyclerview를 만들어서 넣어놓은것이다.
해당 xml에서 보이는 보습은 아래와 같다.
그럼이제 우리가 계속해서 재활용해가며 사용할 RecyclerView를 만들어야 한다.
layout_listitem.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/parent_layout">
<de.hdodenhof.circleimageview.CircleImageView
android:layout_width="80dp"
android:layout_height="80dp"
android:id="@+id/image"
android:src="@mipmap/ic_launcher"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="뭐징"
android:layout_toRightOf="@id/image"
android:textColor="#000"
android:layout_centerVertical="true"
android:layout_marginLeft="30dp"
android:textSize="17sp"
android:id="@+id/image_name"/>
</RelativeLayout>
일단은 위 소스에서 우리가 이미지를 동그랗게 만들어주는 라이브러리를 추가 해주고
해당 사진의 이름을 넣어둘 TextView를 추가해줬다.
이렇게 추가해주면 아래와 같은 모습이 된다.
이제 xml을 완료 했으니 Adapter를 추가해주자.
3. Adpater 클래스 만들기
RecyclerView를 만들려면 Adpater 클래스를 따로 만들어줘야 한다.
Adpater 클래스란 우리가 2번 과정에서 만든 리스트 아이템을 만들어주는 역할을 한다.
그리고 해당 클래스는 RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder>를 상속받는다.
그래서 Adpater 클래스에서는 아래와 같은 메소드 및 클래스를 통해서 리스트 아이템을 만든다.
1. ViewHolder class
2. onCreateViewHolder()
3. onBindViewHolder()
4. getItemCount()
1번 같은 경우에는 따로 만들어줘야하지만
2,3,4번은 RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder>를 상속 받으면 해당 메소드를 정의해야만 해서
빨간줄이 뜰텐데 그 빨간줄에서
해당 부분을 클릭하고
위 메소드를 클릭하고 ok를 누른다.
그럼 아래 메소드들이 만들어져있다.
그럼 이제 각각의 메소드가 뭘하는지 알아보자.
1. ViewHolder class
해당 클래스는 list안에 들어갈 아이템들에 대한 정의를 하는 부분이다.
그리고 ViewHolder로서 사용을 하려면 RecyclerView.ViewHolder을 상속받아야 한다.
내가 만들려는 예제에서는 동그란 ImageView와 이미지 이름을 담을 TextView
그리고 해당 뷰들을 담은 뷰그룹 RelativeLayout을 담은 멤버변수들을 선언해주고
해당 클래스 생성자에서 뷰들을 초기화해준다.
//화면에 표시될 아이템 뷰를 저장하는 객체를 정의하는 부분
public class ViewHolder extends RecyclerView.ViewHolder{ //
CircleImageView image; //동그란 이미지뷰를 가져옴
TextView imageName; //textview 가져오고
RelativeLayout parentLayout; //relativeLayout을 거기서 가져옴 layoutListItem에서 가져옴
public ViewHolder(@NonNull View itemView) {
super(itemView);
image = itemView.findViewById(R.id.image);
imageName = itemView.findViewById(R.id.image_name);
parentLayout = itemView.findViewById(R.id.parent_layout);
}
}
2. onCreateViewHolder()
이 메소드는 1번에서 만든 ViewHolder를 생성하는 부분이다.
해당 View를 아래와 같은소스로 만들어준다.
//뷰홀더를 생성하는 부분
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
//부모로부터 Context를 가져오고 해당 Context에 있는것으로 R.layout.layout_listitem를 만듬
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_listitem,parent,false);
ViewHolder holder = new ViewHolder(view);
return holder;
}
1번에서 만든 ViewHolder로 Context를 가져오고 해당 Context를 통해서 View를 inflate시켜줘서
View를 생성하는것이다.
그리고 ViewHolder 객체를 만들어 생성자로 위에서 만든 View를 넣어줘서 ViewHolde를 만들어 반환해준다.
3. onBindViewHolder()
해당 메소드는 뷰 홀더가 재활용 될때마다 호출되는 메소드이다.
//뷰 홀더가 재활용될때 호출되는 메소드
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, final int position) { //position은 해당 ViewHolder의 위치index를 나타내는것 같음
Log.d(TAG,"onBindViewHolder : called");
Glide.with(mContext)
.asBitmap() //bitmap으로 받을거고
.load((mImage.get(position))) //해당 url로 가져올거고
.into(holder.image); //ViewHolder에 잇는 Image에다가 집어 넣는다.여기에다가 집어넣을것이다.
holder.imageName.setText(mImageNames.get(position)); //mImageName List에 있는것준 해당 ViewHolder의 index 위치 값에 있는 imageName을 받는다.
//해당 Layout이 클릭됐을때
holder.parentLayout.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.d(TAG, "onClick: clicked on : "+mImageNames.get(position));
Toast.makeText(mContext, mImageNames.get(position),Toast.LENGTH_SHORT).show();
}
});
}
그래서 나는 해당 메소드로 안에 ViewHolder안에 들어간 내용물을 넣어줬다.
4. getItemCount()
이 메소드는 몇개의 listItem을 만들지 정해서 해당 값을 return 하는것이다.
//listItem안에 몇개의 item들이 있는지 return 하는것 그래서 return 되는 숫자만큼 item들을 보여줌줌
@Override
public int getItemCount() {
return mImageNames.size();
}
그래서 adapter 클래스의 전체 소스는아래와 같다.
RecyclerViewAdapterView class
package wook.co.coc;
import android.content.Context;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import java.util.ArrayList;
import de.hdodenhof.circleimageview.CircleImageView;
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder>{
private static final String TAG = "RecyclerViewAdapter"; //디버깅을 위한것
private ArrayList<String> mImageNames = new ArrayList<>(); //이미지이름을 담는 ArraysList
private ArrayList<String> mImage = new ArrayList<>(); //이미지 URL을 담는 ArrayList
private Context mContext; // Context를 담는 변수
public RecyclerViewAdapter(ArrayList<String> mImageNames, ArrayList<String> mImage, Context mContext) {
this.mImageNames = mImageNames;
this.mImage = mImage;
this.mContext = mContext;
}
//화면에 표시될 아이템 뷰를 저장하는 객체를 정의하는 부분
public class ViewHolder extends RecyclerView.ViewHolder{ //
CircleImageView image; //동그란 이미지뷰를 가져옴
TextView imageName; //textview 가져오고
RelativeLayout parentLayout; //relativeLayout을 거기서 가져옴 layoutListItem에서 가져옴
public ViewHolder(@NonNull View itemView) {
super(itemView);
image = itemView.findViewById(R.id.image);
imageName = itemView.findViewById(R.id.image_name);
parentLayout = itemView.findViewById(R.id.parent_layout);
}
}
//뷰홀더를 생성하는 부분
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
//부모로부터 Context를 가져오고 해당 Context에 있는것으로 R.layout.layout_listitem를 만듬
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_listitem,parent,false);
ViewHolder holder = new ViewHolder(view);
return holder;
}
//뷰 홀더가 재활용될때 호출되는 메소드
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, final int position) { //position은 해당 ViewHolder의 위치index를 나타내는것 같음
Log.d(TAG,"onBindViewHolder : called");
Glide.with(mContext)
.asBitmap() //bitmap으로 받을거고
.load((mImage.get(position))) //해당 url로 가져올거고
.into(holder.image); //ViewHolder에 잇는 Image에다가 집어 넣는다.여기에다가 집어넣을것이다.
holder.imageName.setText(mImageNames.get(position)); //mImageName List에 있는것준 해당 ViewHolder의 index 위치 값에 있는 imageName을 받는다.
//해당 Layout이 클릭됐을때
holder.parentLayout.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.d(TAG, "onClick: clicked on : "+mImageNames.get(position));
Toast.makeText(mContext, mImageNames.get(position),Toast.LENGTH_SHORT).show();
}
});
}
//listItem안에 몇개의 item들이 있는지 return 하는것 그래서 return 되는 숫자만큼 item들을 보여줌줌
@Override
public int getItemCount() {
return mImageNames.size();
}
}
이렇게 Adapter를 만들어줬으니 이제 MainActivity에서 Adapter 클래스를 이용해서 RecyclerView를 만들어보자.
4. MainActivity Class에서 RecyclerView 만들기
MainActivity에서는 RecycleView를 만들어줘서 RecyclerView를 만들어주었다.
소스는 아래와 같다.
소스에 대한 설명은 주석으로 해놨으니 확인해보면 되겠다.
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
//vars
private ArrayList<String> mNames = new ArrayList<>();
private ArrayList<String> mImageUrls = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.d(TAG, "onCreate: started.");
initImageBitmaps(); //이미지를 가져올 사이크를 ArrayList에 추가
}
private void initImageBitmaps(){
Log.d(TAG, "initImageBitmaps: preparing bitmaps.");
mImageUrls.add("https://c1.staticflickr.com/5/4636/25316407448_de5fbf183d_o.jpg");
mNames.add("Havasu Falls");
mImageUrls.add("https://i.redd.it/tpsnoz5bzo501.jpg");
mNames.add("Trondheim");
mImageUrls.add("https://i.redd.it/qn7f9oqu7o501.jpg");
mNames.add("Portugal");
mImageUrls.add("https://i.redd.it/j6myfqglup501.jpg");
mNames.add("Rocky Mountain National Park");
mImageUrls.add("https://i.redd.it/0h2gm1ix6p501.jpg");
mNames.add("Mahahual");
mImageUrls.add("https://i.redd.it/k98uzl68eh501.jpg");
mNames.add("Frozen Lake");
mImageUrls.add("https://i.redd.it/glin0nwndo501.jpg");
mNames.add("White Sands Desert");
mImageUrls.add("https://i.redd.it/obx4zydshg601.jpg");
mNames.add("Austrailia");
mImageUrls.add("https://i.imgur.com/ZcLLrkY.jpg");
mNames.add("Washington");
initRecyclerView();
}
private void initRecyclerView(){
Log.d(TAG, "initRecyclerView: init recyclerview.");
RecyclerView recyclerView = findViewById(R.id.recycler_view); //Recycler View를 만든것 xml id로 받아옴
RecyclerViewAdapter adapter = new RecyclerViewAdapter(mNames, mImageUrls,this); //해당 ArrayList와 Content로 Adapter class 생성
recyclerView.setAdapter(adapter); //recyclerView는 setAdapter를 통해서 adapter를 받아야 한다.
//recycler view가 화면에 표시될때 아이템 뷰들이 리사이클러 내부에서 배치되는 형태를 관리하는 요소가 LinearLayoutManager이다.
//따라서 수직수평을 나타낼수 있는 LinearLayoutManager, 바둑판 모양의 GridLayoytManager, 엇갈림 격자 형태인 StaggerdedGridLayoutManager등이 있다.
//아래 소스와 같은 경우엔 LinearLayoutManager이니 수평 혹은 수직이 된다.
LinearLayoutManager mLayoutManager = new LinearLayoutManager(this);
//mLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL); //수평으로
mLayoutManager.setOrientation(LinearLayoutManager.VERTICAL); //수직으로
recyclerView.setLayoutManager(mLayoutManager);
}
}
참조 사이트
https://www.youtube.com/watch?v=Vyqz_-sJGFk&t=1096s
'코딩일기 > android studio' 카테고리의 다른 글
안드로이드 독학 22일차 : LiveData와 MutuableLiveData 설명 및 사용법 (0) | 2021.07.13 |
---|---|
안드로이드 독학 22일차 : ViewModel 설명 및 사용법 (0) | 2021.07.13 |
안드로이드 독학 20일차 : androidx란? (0) | 2021.07.06 |
안드로이드 독학 20일 차 : Gradle 이란? (0) | 2021.07.06 |
안드로이드 독학 19일차 : 알림(Notification) (6) | 2021.04.09 |