Spinner가 RecyclerView 안에 들어가 있어야 된다면, Spinner용 ViewHolder가 추가되어야 하고, Spinner를 세팅하고 Spinner 아이템을 받아오는 코드를 ViewHolder 쪽에 추가해 주어야 하기 때문에 코드가 좀 복잡해 집니다. 이렇게 하려면
먼저 MainAdapter 를 멀티뷰타입을 처리할 수 있도록 해주던가 ConcatAdapter를 사용해서 처리해주어야 합니다. 첫번째 옵션으로 구현을 한다고 했을 때, MainAdapter의 모습이 많이 달라집니다.
1. MainAdapterr용 Item class 세팅
// Constructor, Getter, Setter는 코드가 길어져서 생략합니다.
abstract public class ListItem {
abstract int getViewType();
}
public class Movie {
public static final int VIEW_TYPE = R.layout.item_movie;
private final String title;
private final String imageUrl;
@Override
int getViewType() {
return return Movie.VIEW_TYPE;
}
}
// Spinner 클래스
public class ReviewScore extends ListItem {
public static final int VIEW_TYPE = R.layout.item_review_score;
private final int selectedIndex;
@Override
int getViewType() {
return ReviewScore.VIEW_TYPE;
}
}
2. BaseViewHolder 를 만들고 Spinner가 들어갈 ViewHolder도 만들어 줍니다.
public abstract class BaseViewHolder extends RecyclerView.ViewHolder {
public BaseViewHolder(@NonNull View itemView) {
super(itemView);
}
abstract void bindItem(ListItem item);
}
public class MovieViewHolder extends BaseViewHolder {
private final TextView textView;
private final ImageView imageView;
public MovieViewHolder(View view) {
super(view);
textView = view.findViewById(R.id.textView1);
imageView = view.findViewById(R.id.imageView);
}
@Override
void bindItem(ListItem item) {
if (item instanceof Movie) {
Movie movie = (Movie) item;
textView.setText(movie.getTitle());
}
}
}
public class ReviewScoreViewHolder extends BaseViewHolder {
public ReviewScoreViewHolder(@NonNull View itemView, ReviewAdapter.Listener listener) {
super(itemView);
Spinner spinner = itemView.findViewById(R.id.spinner);
String[] items = new String[]{"-점수 선택-", "1", "2", "3", "4", "5", "보지 않음"};
ArrayAdapter<String> adapter = new ArrayAdapter<String>(itemView.getContext(), android.R.layout.simple_spinner_item, android.R.id.text1, items);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(adapter);
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
if (listener != null) {
listener.onReviewScoreClickedAt(position);
}
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
}
@Override
void bindItem(ListItem item) {
if (item instanceof ReviewScore) {
ReviewScore reviewScore = (ReviewScore) item;
spinner.setSelection(reviewScore.getSelectedIndex());
}
}
}
3. MyAdapter를 멀티뷰타입을 지원할 수 있도록 리팩토링 해줍니다.
public class ReviewAdapter extends RecyclerView.Adapter<BaseViewHolder> {
interface Listener {
void onReviewScoreClickedAt(int position);
}
private List<ListItem> items = new ArrayList<>();
private Listener listener;
public ReviewAdapter(Listener listener) {
this.listener = listener;
}
public void submitList(List<ListItem> items) {
this.items = items;
notifyDataSetChanged();
}
private ListItem getItem(int position) {
return items.get(position);
}
@Override
public int getItemViewType(int position) {
return getItem(position).getViewType();
}
@NonNull
@Override
public BaseViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
View view = LayoutInflater.from(viewGroup.getContext())
.inflate(viewType, viewGroup, false);
switch (viewType) {
case Movie.VIEW_TYPE:
return new MovieViewHolder(view);
case ReviewScore.VIEW_TYPE:
return new ReviewScoreViewHolder(view, listener);
default:
throw new NoSuchElementException("Cannot find view type");
}
}
@Override
public void onBindViewHolder(BaseViewHolder viewHolder, final int position) {
viewHolder.bindItem(getItem(position));
}
@Override
public int getItemCount() {
return items.size();
}
}
ReviewScore 아이템을 별도로 클래스 등으로 분리하는 등, 개션해야할 부분들이 있지만, 기본적인 기능은 완성되었습니다.
ReviewScore 를 선택하고 나서의 처리를 위해서 MainAdapter의 생성자에 MainAdapter.Listener를 전달 해주세요. 이 인터페이스는 ReviewScoreViewHolder에 전달되고 spinner을 선택할 때 전달된 리스너를 호출합니다. 이렇게 함으로써 MainAdapter의 외부와 커뮤니케이션이 가능하게 됩니다. 이건 흔하게 쓰는 콜백패턴입니다.
onReviewScoreClicked가 호출되고 나서 필요한 처리 후 어댑터를 다시 갱신해야 한다면, MainAdapter.submitList를 통해 변경된 items를 제공해주시면 됩니다. 리스트의 사이즈가 많아 퍼포먼스 등이 중요하다면, ListAdapter나 DiffUtil 을 이용해서 변경된 아이템만 갱신되도록 처리해 줄 수 있습니다.
보여드린 방법은 RecyclerView.Adpater를 가지고 여러가지 타입의 뷰타입을 처리할 때 가장 많이 사용하는 방법 중의 하나입니다.