마스터Q&A 안드로이드는 안드로이드 개발자들의 질문과 답변을 위한 지식 커뮤니티 사이트입니다. 안드로이드펍에서 운영하고 있습니다. [사용법, 운영진]

리사이클러뷰에 spinner 추가하는법좀 알려주세요.

0 추천

영화리스트를 보여주고, 드랍다운형태의 spinner를 이용하여 영화마다 사용자가 점수를 남길수있는 화면을 구현중에있습니다.

 

리사이클러뷰활용하느라 어댑터랑 메인액티비티 만들었구, 카드뷰있는 xml에 <spinner></spinner> 추가하고,

spinner이제 제대로 구현하려고 밑에 코드를 일반적인 방법으로 메인액티비티에 넣으면 정상적으로 안돌아가더라구요.

밑에 코드를 어댑터에 어떻게 잘 집어넣어야하는거같은데...쉽지않네요.

spinner=(Spinner)findViewById(R.id.spinner);

item= new String[]{"-점수 선택-","1","2","3","4","5","보지 않음"};
ArrayAdapter<String> adapter=new ArrayAdapter<String>(this,android.R.layout.simple_spinner_item,item);
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) {

    }

    @Override
    public void onNothingSelected(AdapterView<?> parent) {

    }
});

 

 

지금 밑에 코드가, 어댑터 파일인데, 여기서 어떻게 코드를 어떻게 넣어야 하나요?

 

 

public class MainAdapter extends RecyclerView.Adapter<MainAdapter.MyViewHolder> {

    String data1[], data2[];
    int images[];
    Context context;

    public MainAdapter(Context ct, String s1[], String s2[], int img[]){
        context=ct;
        data1=s1;
        data2=s2;
        images=img;
    }

    public class MyViewHolder extends RecyclerView.ViewHolder{
        TextView textView1;
        //TextView textView2;
        ImageView imageView;

        public MyViewHolder(@NonNull View itemView){
            super(itemView);
            textView1=itemView.findViewById(R.id.textView);
            //textView2=itemView.findViewById(R.id.textView2);
            imageView=itemView.findViewById(R.id.imageView);
        }
    }

    @NonNull
    @Override
    public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        LayoutInflater inflater = LayoutInflater.from(context);
        View view = inflater.inflate(R.layout.my_row, parent, false);
        return new MyViewHolder(view);
    }

    @Override //값전달 ( 텍스트에 값을 넣겠다 )
    public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
        holder.textView1.setText(data1[position]);
        //holder.textView2.setText(data2[position]);
        holder.imageView.setImageResource(images[position]);
    }

    @Override //아이템의 개수
    public int getItemCount() {
        return images.length;
    }

}
자린이안린이 (120 포인트) 님이 2021년 10월 12일 질문
자린이안린이님이 2021년 10월 12일 수정

1개의 답변

0 추천

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를 가지고 여러가지 타입의 뷰타입을 처리할 때 가장 많이 사용하는 방법 중의 하나입니다.

spark (226,420 포인트) 님이 2021년 10월 12일 답변
spark님이 2021년 10월 13일 수정
...