본문 바로가기
안드로이드 자바

[Android][Java] 두개의 리사이클러뷰를 사용해 카테고리별 아이템 보여주기.

by teamnova 2022. 12. 7.

안녕하세요! 많은 어플리케이션에서 상위 카테고리를 클릭하면 해당 카테고리에 해당하는 아이템만 보여줄 수 있도록 동작하는 리사이클러뷰들을 자주 찾아볼 수있는데요, 오늘은 두개의 리사이클러뷰 컴포넌트를 사용하여 어떻게 아래와 같이 구현할 수 있는지 알아봅시다. 

 

우선 레이아웃 xml 파일입니다. 리사이클러뷰 두개가 있는 것을 확인할 수 있습니다. 저는 각각 staticRv, dynamicRv라고 아이디 값을 지정해주었습니다.

<?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"
    tools:context=".MainActivity"
    android:orientation="vertical">
    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/staticRv"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>
    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/dynamicRv"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</LinearLayout>

리사이클러뷰에 사용할 아이템 클래스도 생성해주었습니다.

public class RvItem {
    int staticRvImg;
    String staticRvStr;

    public RvItem(int staticRvImg, String staticRvStr) {
        this.staticRvImg = staticRvImg;
        this.staticRvStr = staticRvStr;
    }

    public int getRvImg() {return staticRvImg;}

    public String getRvStr() {return staticRvStr;}
    
    //setter는 사용하지않아 generate 하지않음.
}

우선 상위 리사이클러뷰 아이템 클릭 시 하위 리사이클러뷰가 함께 움직일 수 있도록 도와주는 리스너를 생성합니다. 파라미터로는 하위 리사이클러뷰에 적용시킬 어레이리스트를 받습니다. 

public interface UpdateRvListener {
    void onUpdateDynamicRv(ArrayList<RvItem> list);
}

이제 카테고리 부분에 해당하는 상위 리사이클러뷰 컴포넌트의 어댑터 클래스를 생성해줍니다. (StaticAdapter)

public class StaticAdapter extends RecyclerView.Adapter<StaticAdapter.StaticViewHolder>{

    ArrayList<RvItem> arrayList;
    ArrayList<RvItem> dynamicArrayList = new ArrayList<>();//하위리사이클러뷰에 넘겨줄 어레이리스트
    UpdateRvListener mListener;//하위 리사이클러뷰 업데이트를 위한 리스너
    int index = -1;//첫클릭을 파악하기 위해 -1값 사용. 이후 포지션 값

    public StaticAdapter(ArrayList<RvItem> arrayList, UpdateRvListener mListener) {
        this.arrayList = arrayList;
        this.mListener = mListener;
    }

    @NonNull
    @Override
    public StaticAdapter.StaticViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {

        return new StaticViewHolder(LayoutInflater.from(parent.getContext())
                .inflate(R.layout.static_frame,parent,false));
    }

    @Override
    public void onBindViewHolder(@NonNull StaticAdapter.StaticViewHolder holder,
                                 @SuppressLint("RecyclerView") int position) {

        RvItem staticRvItem = arrayList.get(position);
        holder.staticRvImg.setImageResource(staticRvItem.getRvImg());
        holder.staticRvStr.setText(staticRvItem.getRvStr());

        if(index==-1){
            callItems(0);//미클릭 상태에서 아이템 표기
        }

        holder.itemView.setOnClickListener(new View.OnClickListener() {
            @SuppressLint("NotifyDataSetChanged")
            @Override
            public void onClick(View v) {
                //첫클릭 시 -1 >> position 값으로 변화
               index = position;
               notifyDataSetChanged();
               callItems(position);
            }
        });

        if(index==position){
            //선택된 아이템은 글씨색상 purple_500으로
            holder.staticRvStr.setTextColor(holder.itemView.getContext().getColor(R.color.purple_500));
        }else{
            //미선택 아이템은 black으로 글씨가 보여지도록하기.
            holder.staticRvStr.setTextColor(holder.itemView.getContext().getColor(R.color.black));
        }
    }

    @Override
    public int getItemCount() {

        return arrayList.size();
    }

    public class StaticViewHolder extends RecyclerView.ViewHolder {
        ImageView staticRvImg;
        TextView staticRvStr;
        public StaticViewHolder(@NonNull View itemView) {
            super(itemView);
            staticRvImg = itemView.findViewById(R.id.staticRvImg);
            staticRvStr = itemView.findViewById(R.id.staticRvStr);
        }
    }

	//포지션에 따라 아이템을 불러오는 함수
    public void callItems(int position){

        if(dynamicArrayList.size()>0){
            dynamicArrayList.clear();
        }

        if(position==0){//과일

            dynamicArrayList.add(new RvItem(R.drawable.pineapple,"과일1"));
            dynamicArrayList.add(new RvItem(R.drawable.pineapple,"과일2"));
            dynamicArrayList.add(new RvItem(R.drawable.pineapple,"과일3"));
            dynamicArrayList.add(new RvItem(R.drawable.pineapple,"과일4"));
            dynamicArrayList.add(new RvItem(R.drawable.pineapple,"과일5"));
            dynamicArrayList.add(new RvItem(R.drawable.pineapple,"과일6"));

        }else if(position==1){//채소

            dynamicArrayList.add(new RvItem(R.drawable.radish,"채소1"));
            dynamicArrayList.add(new RvItem(R.drawable.radish,"채소2"));
            dynamicArrayList.add(new RvItem(R.drawable.radish,"채소3"));
            dynamicArrayList.add(new RvItem(R.drawable.radish,"채소4"));
            dynamicArrayList.add(new RvItem(R.drawable.radish,"채소5"));
            dynamicArrayList.add(new RvItem(R.drawable.radish,"채소6"));

        }else if(position==2){//육류

            dynamicArrayList.add(new RvItem(R.drawable.meat,"육류1"));
            dynamicArrayList.add(new RvItem(R.drawable.meat,"육류2"));
            dynamicArrayList.add(new RvItem(R.drawable.meat,"육류3"));
            dynamicArrayList.add(new RvItem(R.drawable.meat,"육류4"));
            dynamicArrayList.add(new RvItem(R.drawable.meat,"육류5"));
            dynamicArrayList.add(new RvItem(R.drawable.meat,"육류6"));

        }else{//생선

            dynamicArrayList.add(new RvItem(R.drawable.salmon,"생선1"));
            dynamicArrayList.add(new RvItem(R.drawable.salmon,"생선2"));
            dynamicArrayList.add(new RvItem(R.drawable.salmon,"생선3"));
            dynamicArrayList.add(new RvItem(R.drawable.salmon,"생선4"));
            dynamicArrayList.add(new RvItem(R.drawable.salmon,"생선5"));
            dynamicArrayList.add(new RvItem(R.drawable.salmon,"생선6"));
        }

        if(mListener!= null){
            mListener.onUpdateDynamicRv(dynamicArrayList);
        }
    }
}

파라미터로 받은 어레이리스트를 하위 리사이클러뷰에 적용해줄 하위 리사이클러뷰 어댑터 클래스도 생성해줍니다. (DynamicAdapter)

public class DynamicAdapter extends RecyclerView.Adapter<DynamicAdapter.DynamicViewHolder>{

    ArrayList<RvItem> dynamicArrayList;

    public DynamicAdapter(ArrayList<RvItem> dynamicArrayList) {
        this.dynamicArrayList = dynamicArrayList;
    }

    @NonNull
    @Override
    public DynamicAdapter.DynamicViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        return new DynamicViewHolder(LayoutInflater.from(parent.getContext())
                .inflate(R.layout.dynamic_frame,parent,false));
    }

    @Override
    public void onBindViewHolder(@NonNull DynamicAdapter.DynamicViewHolder holder, int position) {

        RvItem dynamicRvItem = dynamicArrayList.get(position);
        holder.dynamicRvImg.setImageResource(dynamicRvItem.getRvImg());
        holder.dynamicRvStr.setText(dynamicRvItem.getRvStr());

        holder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(holder.itemView.getContext(),
                        dynamicRvItem.getRvStr()+"가 클릭되었습니다",
                        Toast.LENGTH_SHORT).show();
            }
        });
    }

    @Override
    public int getItemCount() {
        return dynamicArrayList.size();
    }

    public class DynamicViewHolder extends RecyclerView.ViewHolder {
        ImageView dynamicRvImg;
        TextView dynamicRvStr;
        public DynamicViewHolder(@NonNull View itemView) {
            super(itemView);
            dynamicRvImg = itemView.findViewById(R.id.dynamicRvImg);
            dynamicRvStr = itemView.findViewById(R.id.dynamicRvStr);
        }
    }
}

이제 어댑터들과 리사이클러뷰들을 서로 연결해주고 리스너 동작을 구현해줍시다.

public class MainActivity extends AppCompatActivity {

    private RecyclerView dynamicRv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        RecyclerView staticRv = findViewById(R.id.staticRv);
        dynamicRv = findViewById(R.id.dynamicRv);

        //리사이클러뷰 리니어레이아웃매니저 세팅
        //staticRv 부분은 가로로 되어있기 때문에 두번째 파라미터 값을 Horizontal 로 주기.
        staticRv.setLayoutManager(new LinearLayoutManager(this,RecyclerView.HORIZONTAL,false));
        dynamicRv.setLayoutManager(new LinearLayoutManager(this,RecyclerView.VERTICAL,false));

        //리사이클러뷰 크기가 고정되어있음을 안드로이드에 알려 레이아웃을 다시 그리는 작업을 피하게 해주기.
        //setHasFixedItemSize(false)인 경우 >리사이클러뷰의 아이템이 들어오거나 나갈 때마다 RecyclerView 전체의 레이아웃을 다시 그려준다.
        staticRv.setHasFixedSize(true);
        dynamicRv.setHasFixedSize(true);

		
        final ArrayList<RvItem> rvItems = new ArrayList<>();
        rvItems.add(new RvItem(R.drawable.pineapple,"과일"));
        rvItems.add(new RvItem(R.drawable.radish,"채소"));
        rvItems.add(new RvItem(R.drawable.meat,"육류"));
        rvItems.add(new RvItem(R.drawable.salmon,"생선"));

        //staticAdapter 생성
        StaticAdapter staticAdapter = new StaticAdapter(rvItems, new UpdateRvListener() {
            @SuppressLint("NotifyDataSetChanged")
            @Override
            public void onUpdateDynamicRv(ArrayList<RvItem> list) {
                DynamicAdapter adapter = new DynamicAdapter(list);
                dynamicRv.setAdapter(adapter);
                adapter.notifyDataSetChanged();
            }
        });

        //staticAdapter 세팅하기.
        staticRv.setAdapter(staticAdapter);
    }
}

이렇게 위에 보이는 형식의 리사이클러뷰를 구현해보았습니다.