오늘은 RecyclerView를 활용해서 게시판을 만들어 볼 예정이다!
DB에 있는 게시글 데이터를 Retrofit으로 불러와서 게시판에 찍어내는 과정까지 해보려고 한다.
오늘도 파이팅❣❣
이 부분은 Retrofit 완전 정복을 향해🤸♀️ 게시글에서 자세히 다루고 있기 때문에 상세한 코드는 생략하도록 하겠다.
RecyclerView에는 layout이 두 개가 필요하다.
하나는 Recycler하게 목록을 띄울 화면 레이아웃이고 또 하나는 목록 하나하나, 즉 각 View행 마다의 공통된 레이아웃이다.
RecyclerView는 각 View행 하나를 재활용해서 여러 개 띄워낸다는 의미를 갖고 있다. 그 의미에 걸맞게 띄우고 싶은 형식이 반복(중복)되는 레이아웃을 하나 생성하고 그 레이아웃을 띄울 하나의 화면을 만들면 되는 것이다.
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
tools:context=".MainActivity">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
예시니까 간단하게 recyclerView 만 넣었지만 해당 화면에 다른 요소들을 넣고 싶다면 기존의 화면 레이아웃 구성처럼 다양하게 해도 무방하다. 단지 recyclerView 영역에서 view 아이템들이 recycle된다는 것만 알고 있으면 된다.
<?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"
android:weightSum="100"
android:padding="5dp">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="100dp"
android:layout_gravity="center"
android:background="#F2F8EB"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="2.5"
android:background="#A5E4DF"
android:orientation="horizontal">
<TextView
android:id="@+id/tv_postlist_title"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center|left"
android:text="Title" />
<TextView
android:id="@+id/tv_postlist_timestamp"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center|right"
android:layout_weight="2"
android:background="@color/teal_200"
android:gravity="center|right"
android:text="2022-01-01-XX:XX:XX"
android:textSize="10sp" />
</LinearLayout>
<TextView
android:id="@+id/tv_postlist_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center|left"
android:text="Content" />
</LinearLayout>
</LinearLayout>
디자인 옵션에서 보는 예시의 레이아웃은 이런 느낌이다.
이 레이아웃은 그냥 하나의 예시일 뿐 개발자의 입맛에 맞게 충분히 유연하게 바꿔주어도 된다.
그냥 한 가지 알아야 할 점은 이 파일은 activity가 아니라 layout이라는 점! 그리고 여기서 만들어준 layout이 위의 recyclerview위치에서 반복될거라는 점이다.
RecyclerView는 ListView와는 다르게 한 화면에서 표기하기 힘든 많은 양의 데이터를 스크롤 기능을 지원해서 계속해서 스크롤하면 재생산해낼 수 있도록 하는 위젯이다. 여기서 재활용이라는 의미의 recycle 이름을 따게 된 것이다. 위의 view 아이템 layout 틀을 재활용하기 위해서는 Adapter로 itemView를 만들어내야 한다.
구현할 Adapter에는 크게 Adapter와 Holder 두 가지가 있는데
import android.content.Context;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import org.techtown.knockknock.R;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
//Adapter : listview를 만들어서 recyclerview와 연결해줌 , Holder에서 만들어 준 listView를 inflater를 이용해 객체화 시키고 실제 데이터를 담아줌
//즉, Holder가 listview 그릇을 만들면 Adapter가 실제 데이터를 담은 listView를 만들어주는 것.
public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerAdapter.MyViewHolder> {
private Context c;
private List<PostData> postlist;
public RecyclerAdapter(Context c, List<PostData> postlist){
this.c = c;
this.postlist = postlist;
}
@NonNull
@Override
public RecyclerAdapter.MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType){
//inflater: xml을 객체화
View view = LayoutInflater.from(c).inflate(R.layout.listview_post,parent,false);
return new MyViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull RecyclerAdapter.MyViewHolder holder, int position){
holder.title.setText(postlist.get(position).getTitle());
holder.date.setText(postlist.get(position).getDate());
holder.content.setText(postlist.get(position).getContent());
}
@Override
public int getItemCount() {
int size = postlist.size();
return size;
}
//Holder: 레이아웃과 연결해서 listView를 만들어주는 역할 (단순 연결)
public class MyViewHolder extends RecyclerView.ViewHolder {
TextView title;
TextView content;
TextView date;
public MyViewHolder(@NonNull View itemView) {
super(itemView);
title = (TextView)itemView.findViewById(R.id.tv_postlist_title);
content = (TextView)itemView.findViewById(R.id.tv_postlist_content);
date = (TextView)itemView.findViewById(R.id.tv_postlist_timestamp);
}
}
}
MyViewHolder 안의 멤버변수는 당연히 View 아이템 레이아웃의 구성요소와 일치해야 한다.
이제 다시 RecyclerView layout이 올라가있는 Activity파일에서 사용을 위한 추가 사항만 추가해주면 된다.
public class UserPostActivity extends AppCompatActivity {
TextView writerInfo;
// 우선 데이터를 담을 list를 하나 만든다.
PostListData postlist; //📌
List<PostData> postInfo; //📌
RecyclerView recyclerView; //📌
RecyclerAdapter recyclerAdapter; //📌
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_user_post);
//📌
postInfo = new ArrayList<>();
recyclerView = findViewById(R.id.recyclerView);
//📌
//layoutManager: recyclerview에 listview 객체를 하나씩 띄움
RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
//sharedPreferences 에서 현재 로그인 되어있는 유저의 id가져오기
SharedPreferences sharedPreferences = getSharedPreferences("UserInfo",MODE_PRIVATE);
String id = sharedPreferences.getString("userId","");
String nickname = sharedPreferences.getString("nickname","");
writerInfo = findViewById(R.id.tv_writerInfo);
writerInfo.setText(nickname+" 님이 작성하신 글 입니다.");
PostAPI postAPI = RetrofitClient.getInstance().create(PostAPI.class);
Call<PostListData> call = postAPI.getPostListDatabyUserId(id);
call.enqueue(new Callback<PostListData>() {
@Override
public void onResponse(Call<PostListData> call, Response<PostListData> response) {
if(response.isSuccessful()){
postlist = response.body();
Log.d("UserPostActivity",postlist.toString());
postInfo = postlist.data;
//📌
//Adapter를 이용해서 postInfo에 있는 내용을 가져와서 저장해둔 listView 형식에 맞게 띄움
recyclerAdapter = new RecyclerAdapter(getApplicationContext(),postInfo);
recyclerView.setAdapter(recyclerAdapter);
}
else{
ErrorBody errorBody = new Gson().fromJson(response.errorBody().charStream(),ErrorBody.class);
Log.d("UserPostActivity",errorBody.getMessage());
}
}
@Override
public void onFailure(Call<PostListData> call, Throwable t) {
Log.d("UserPostActivity",t.toString());
}
});
}
}
📌
표시된 부분과 유사하게 코드에 맞춰 추가해주면 Recycler View 활용까지 완료할 수 있다!
이 부분은 개발일지에서 자세히 다루고 있어서 다음 링크를 참고하면 될 것이다 🥰
Item view의 가장 상위 layout의 height는 반드시 match_parent가 아니여야 한다! match_parent로 줘버리면 recyclerview 부분을 하나의 item view가 다 덮어버려서 아이템이 하나밖에 안뜬다. 이거 때문에 버그인줄 알고 이틀 헤맴 ㅠㅠㅠㅠㅠ