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

파이어베이스_안드로이드_자바_리사이클러뷰

0 추천
안드로이드 스튜디오를 이용하여 자바언어를 사용하고, 파이어베이스(realtime db)를 사용하여 쇼핑몰 앱을

개발하고 있습니다.

현재 리사이클러뷰를 이용하여 db에서데이터를 가져와서 데이터정보들을 나열하고 해당하는 아이템을 클릭하였을시 그 해당하는 아이템 상세정보 창으로 넘어가는 기능을 구현하려고 하는데 감이 안잡혀서 어떤 코드로 리사이클러뷰에 있는 아이템을 선택한것만 끌어와서 창을 띄울수 있을까요..
Occur (140 포인트) 님이 2021년 11월 7일 질문
혹시 리스트를 보여주는 코드는 작성을 하셨나요. 파이어베이스의 구조와 그 부분을 보여주시면
기본적인 방향은 잡아드릴 수 있을 것 같습니다.
상품목록을 보여준다고 했을 때, 상품을 클릭하면 선택한 상품에 대한 디테이을 보여주는 건 굉장히 흔한 예입니다. 사용자가 어떤 상품을 클릭했는지 알아야 하기 때문에  Recycler.Adapter에서 클릭한 상품에 대한 정보를 받아와야 하므로, 상품 클릭에 대한 리스너가 필요하고 상품목록을 상품에 대한 리스트를 보여주어야 합니다. 상품은 키값이 될 수 있는 파이어베이스에 연결이 되는 고유값(예를 들면, 상품코드)이 있어야 이 값을 가지고 파이어베이스에서 디테일 정보를 가져올 수 있을 겁니다.
아래에 해당하는게 상품리스트를 보여주는 어댑터에 해당하는 부분이고 그아래에 해당하는게 메인에서 어댑터와 파이어베이스를 연동하여 실제 구현 코드 부분입니다. 리사이클러뷰와 파이어베이스를 이용하여 연동하여 출력하는 것 까지는 성공하였지만 클릭리스너를 이용하여 상세페이지로 넘어가는 것을 어떻게 구현하는지 모르겠습니다...

package com.example.shoppingmall;

import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.os.Parcelable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.appcompat.view.menu.MenuView;
import androidx.recyclerview.widget.RecyclerView;

import com.bumptech.glide.Glide;

import java.util.ArrayList;

public class CustomAdapter extends RecyclerView.Adapter<CustomAdapter.CustomViewHolder>    {



    private ArrayList<ItemList> arrayList;
    private Context context;   //선택한 액티비티에 대한 context

    public CustomAdapter(ArrayList<ItemList> arrayList, Context context) {
        this.arrayList = arrayList;
        this.context = context;
    }


    @NonNull
    @Override
    public CustomViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {


        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item, parent, false);
        CustomViewHolder holder = new CustomViewHolder(view);
        return holder;
    }

    @Override
    public void onBindViewHolder(@NonNull CustomViewHolder holder, int position) {   //실제 아이템들에 대한 매칭

        Glide.with(holder.itemView)
                .load(arrayList.get(position).getImage())  //ItemList객체가 있는 arrylist에 담아서 adpater로 쏴준다. 이떄 여기서 glide로 받아서 로드 해주는 동작
                .into(holder.et_image);
        holder.et_artName.setText(arrayList.get(position).getArtName());
        holder.et_artist.setText(arrayList.get(position).getArtist());
        holder.et_price.setText(String.valueOf(arrayList.get(position).getPrice())); //int형의 값이 setText가능



    }

    @Override
    public int getItemCount() {
        //삼항연산자
        return (arrayList != null ? arrayList.size() : 0);
    }




    public class CustomViewHolder extends RecyclerView.ViewHolder {  //CustomViewHolder에 리스트아이템에 있는 것들을 불러들임
        ImageView et_image;
        TextView et_artName;
        TextView et_artist;
        TextView et_price;



        public CustomViewHolder(@NonNull View itemView ) {
            super(itemView);
            this.et_image = itemView.findViewById(R.id.et_image);
            this.et_artName = itemView.findViewById(R.id.et_artName);
            this.et_artist = itemView.findViewById(R.id.et_artist);
            this.et_price = itemView.findViewById(R.id.et_price);



            itemView.setOnClickListener(new View.OnClickListener() {
                private boolean et_artName;   //리사이클러뷰 클릭이벤트 처리 메소드
                @Override
                public void onClick(View v) {

                    int currentPos = getAbsoluteAdapterPosition(); //각 어댑터의 위치 클릭된
                    ItemList itemList = arrayList.get(currentPos);

                    Intent intent =  new Intent(context, IteminformationActivity.class);   //화면 넘겨주기
                    intent.putExtra("artName", itemList.getArtName());

                    context.startActivity(intent);




                    Toast.makeText(context, itemList.getArtName() +
                            "\n" + itemList.getArtist() +
                            "\n" + itemList.getPrice() , Toast.LENGTH_SHORT).show();   //해당위치 아이템 클릭시 토스트메시지창 띄우기


                }
            });
        }
    }
}



MainActivity.java

package com.example.shoppingmall;

import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;


import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.database.DataSnapshot;
import com.google.firebase.database.DatabaseError;
import com.google.firebase.database.DatabaseReference;
import com.google.firebase.database.FirebaseDatabase;
import com.google.firebase.database.ValueEventListener;

import java.util.ArrayList;


public class MainActivity extends AppCompatActivity {

    private FirebaseAuth mFirebaseAuth;
    private RecyclerView recyclerView;
    private RecyclerView.Adapter adapter;
    private RecyclerView.LayoutManager layoutManager;
    private ArrayList<ItemList> arrayList;
    private FirebaseDatabase database;
    private DatabaseReference databaseReference;

    // 마지막으로 뒤로 가기 버튼을 눌렀던 시간 저장
    private long backKeyPressedTime = 0;
    // 첫 번째 뒤로 가기 버튼을 누를 때 표시
    private Toast toast;

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    @Override
    public void onBackPressed() {
        //super.onBackPressed();
        // 기존 뒤로 가기 버튼의 기능을 막기 위해 주석 처리 또는 삭제

        // 마지막으로 뒤로 가기 버튼을 눌렀던 시간에 2.5초를 더해 현재 시간과 비교 후
        // 마지막으로 뒤로 가기 버튼을 눌렀던 시간이 2.5초가 지났으면 Toast 출력
        // 2500 milliseconds = 2.5 seconds
        if (System.currentTimeMillis() > backKeyPressedTime + 2500) {
            backKeyPressedTime = System.currentTimeMillis();
            toast = Toast.makeText(this, "뒤로 가기 버튼을 한 번 더 누르시면 종료됩니다.", Toast.LENGTH_LONG);
            toast.show();
            return;
        }
        // 마지막으로 뒤로 가기 버튼을 눌렀던 시간에 2.5초를 더해 현재 시간과 비교 후
        // 마지막으로 뒤로 가기 버튼을 눌렀던 시간이 2.5초가 지나지 않았으면 종료
        if (System.currentTimeMillis() <= backKeyPressedTime + 2500) {
            moveTaskToBack(true);                  // 태스크를 백그라운드로 이동
            finishAndRemoveTask();                  // 액티비티 종료 + 태스크 리스트에서 지우기
            android.os.Process.killProcess(android.os.Process.myPid());
            toast.cancel();
            toast = Toast.makeText(this,"이용해 주셔서 감사합니다.",Toast.LENGTH_LONG);
            toast.show();
        }
    }

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

        recyclerView = findViewById(R.id.recyclerView); //연결
        recyclerView.setHasFixedSize(true); //리사이클러뷰 기존성능 강화
        layoutManager = new LinearLayoutManager(this);
        recyclerView.setLayoutManager(layoutManager);
        arrayList = new ArrayList<>(); //ItemList객체를 담을 arraylsit(어댑터 쪽으로)
        
        database = FirebaseDatabase.getInstance(); //파이어베이스 데이터베이스 연동
        databaseReference = database.getReference("Itemlist"); //DB 테이블 연결  databaseReference 파이어베이스 db를 읽기위한 인스턴스


        databaseReference.addListenerForSingleValueEvent(new ValueEventListener() {
            @Override
            public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
                // 파이어베이스 DB의 데이터를 받아오는 곳
                arrayList.clear();  // 기존 배열리스트가 존재하지 않도록 초기화
                for(DataSnapshot snapshot : dataSnapshot.getChildren()) { //반복문으로 데이터 list를 추출해냄
                    ItemList itemList = snapshot.getValue(ItemList.class); //만들어둔 ItemList객체에 데이터를 담는다.
                    arrayList.add(itemList); // 담은 데이터들을 배열리스트에 넣고 리사이클러뷰로 보낼 준비
                }
                adapter.notifyDataSetChanged(); //리스트 저장 및 새로고침
            }

            @Override
            public void onCancelled(@NonNull DatabaseError error) {
                // DB를 가져오던중 에러 발생시
                Log.e("MainActivity", String.valueOf(error.toException())); //에러문 출력
            }
        });

        adapter = new CustomAdapter(arrayList, this);

        recyclerView.setAdapter(adapter);  //리사이클러뷰에 어댑터 연결




    }






}
해당하는 파이어베이스(리얼타임db) json
{
  "Itemlist" : {
    "Itme_01" : {
      "artName" : "절규",
      "artist" : "뭉크",
      "detail" : "《절규》(노르웨이어: Skrik, 독일어: Der Schrei der Natur, 영어: The Scream of Nature; 1893-1910년 작)는 노르웨이의 예술가 에드바르 뭉크의 연작 중 하나인 표현주의 그림으로, 핏빛의 노을지는 하늘을 배경으로 괴로워하는 인물을 묘사하였다. 배경의 풍경은 노르웨이 오슬로(당시 크리스티아니아)의 이케베르크 언덕에서 보이는 오슬로피오르이다.",
      "image" : "https://firebasestorage.googleapis.com/v0/b/test-project-ee252.appspot.com/o/%EB%AD%89%ED%81%AC%EC%A0%88%EA%B7%9C.jpg?alt=media&token=fedbfb17-f844-4ccb-bc2c-7edd16a7c545",
      "maketime" : "1893년",
      "price" : 1500,
      "size" : "73.5 x 91cm",
      "way" : "tempera"
    },
    "item_02" : {
      "artName" : "별이 빛나는 밤",
      "artist" : "반고흐",
      "image" : "https://firebasestorage.googleapis.com/v0/b/test-project-ee252.appspot.com/o/%EB%B0%98%EA%B3%A0%ED%9D%90.jpg?alt=media&token=013dfcff-f3e6-439d-9159-1ff282ce85ad",
      "price" : 2000
    },
    "item_03" : {
      "artName" : "모나리자",
      "artist" : "레오나르도 다빈치",
      "image" : "https://firebasestorage.googleapis.com/v0/b/test-project-ee252.appspot.com/o/%EB%AA%A8%EB%82%98%EB%A6%AC%EC%9E%90.jpg?alt=media&token=7485b4f7-619d-481e-9134-aaad251d18d4",
      "price" : 3000
    },
    "item_04" : {
      "artName" : "진주 귀걸이를 한 소녀",
      "artist" : "요하네스 페르메이르",
      "image" : "https://firebasestorage.googleapis.com/v0/b/test-project-ee252.appspot.com/o/%EC%A7%84%EC%A3%BC%20%EA%B7%80%EA%B1%B8%EC%9D%B4%EB%A5%BC%20%ED%95%9C%EC%86%8C%EB%85%80.jpg?alt=media&t",
      "price" : 4500
    },
    "item_05" : {
      "artName" : "최후의 만찬",
      "artist" : "레오나르도 다빈치",
      "image" : "https://firebasestorage.googleapis.com/v0/b/test-project-ee252.appspot.com/o/%EC%B5%9C%ED%9B%84%EC%9D%98%EB%A7%8C%EC%B0%AC.jpg?alt=media&token=bcf1f77b-1bd2-40ba-a03e-0ced7a543cd2",
      "price" : 5500
    },
    "item_06" : {
      "artName" : "이삭줍는 여인들",
      "artist" : "밀레",
      "image" : "https://firebasestorage.googleapis.com/v0/b/test-project-ee252.appspot.com/o/%EB%B0%A4%EC%9D%98%EC%B9%B4%ED%8E%98%2C%ED%85%8C%EB%9D%BC%EC%8A%A4(%EA%B3%A0%ED%9D%90).jpg?alt=media&token=3469fb2a-cae5-4533-8ad7-645e2286cc04",
      "price" : 6000
    },
    "item_07" : {
      "artName" : "기억의 영속",
      "artist" : "알리",
      "image" : "https://firebasestorage.googleapis.com/v0/b/test-project-ee252.appspot.com/o/%EA%B8%B0%EC%96%B5%EC%9D%98%20%EC%98%81%EC%86%8D.jpg?alt=media&token=70f689ac-ba94-4d92-8490-a7727e69b892",
      "price" : 8000
    },
    "item_08" : {
      "artName" : "밤의 카페, 테라스",
      "artist" : "반 고흐",
      "image" : "https://firebasestorage.googleapis.com/v0/b/test-project-ee252.appspot.com/o/%EB%B0%A4%EC%9D%98%EC%B9%B4%ED%8E%98%2C%ED%85%8C%EB%9D%BC%EC%8A%A4(%EA%B3%A0%ED%9D%90).jpg?alt=media&token=3469fb2a-cae5-4533-8ad7-645e2286cc04",
      "price" : 1000000
    }
  }
}

2개의 답변

0 추천

먼저  ItemList클래스에 데이터베이스에서 키값이 될 수 있는 값을 추가하시는게 좋은데, 기존에 포지션 베이스로 아이템을 처리하고 계시기 때문에 그냥 포지션 베이스를 하는 로직을 적을게요.

public class CustomAdapter extends RecyclerView.Adapter<CustomAdapter.CustomViewHolder>    {
     interface Listener {
           void onItemClickedAt(int position);
     }

     private Listener listener;
     public void setListener(Listener listener) {
          this.listener = listener;
     }

    public void submitList(ArrayList<ItemList> items) {
         this.arrayList = items;
         notifyDataSetChanged();
    }

    @NonNull
    @Override
    public CustomViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        ...
        CustomViewHolder holder = new CustomViewHolder(view, this.listener);
        ...
    }

    public class CustomViewHolder extends RecyclerView.ViewHolder {  //CustomViewHolder에 리스트아이템에 있는 것들을 불러들임

       public CustomViewHolder(@NonNull View itemView, CustomAdapter.Listener itemListListener) {
            super(itemView);
          
            ....

            itemView.setOnClickListener(new View.OnClickListener() {
                private boolean et_artName;   //리사이클러뷰 클릭이벤트 처리 메소드
                @Override
                public void onClick(View v) {
                    int currentPos = getAbsoluteAdapterPosition(); //각 어댑터의 위치 클릭된
                    itemListListener.onItemClickedAt(currentPos);
                }
            });
        }
    }
}

 위처럼, CustomAdapter에 Listener를 하나추가하고 setListner 를 통해 외부에서 Listener를 설정할 수 있도록 합니다.
ViewHolder  를 생성할 때 이 Listener  를 넘겨주고 ViewHolder에서는 해당 뷰를 클릭할 때,  Listener를 호출합니다. 이렇게 하면 Adapter를 설정한 쪽에서 어떤 포지션에 있는 아이템이 눌렸는지 알 수 있겠죠?

MainActivity에서 CustomerAdapter.Listener를 구현합니다.

public class MainActivity extends AppCompatActivity implements CustomerAdapter.Listener {
   
  ....
   
 @Override
  public void onCreate(Bundle saveInstanceState) {
      super.onCreate(saveInstanceState);
    
      ...
     // CustomAdapter 초기화
     adapter = new CustomAdapter(arrayList, this);
     adapter.setListener(this);
     recyclerView.setAdapter(adapter);  //리사이클러뷰에 어댑터 연결
      
     databaseReference.addListenerForSingleValueEvent(new ValueEventListener() {
            @Override
            public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
                // 파이어베이스 DB의 데이터를 받아오는 곳
                arrayList.clear();  // 기존 배열리스트가 존재하지 않도록 초기화
                for(DataSnapshot snapshot : dataSnapshot.getChildren()) { //반복문으로 데이터 list를 추출해냄
                    ItemList itemList = snapshot.getValue(ItemList.class); //만들어둔 ItemList객체에 데이터를 담는다.
                    arrayList.add(itemList); // 담은 데이터들을 배열리스트에 넣고 리사이클러뷰로 보낼 준비
                }
                 
               updateListItems();
            }

            ...
        });
  }

  private void updateListItems() {
       adapter.submitList(arrayList);
  }

   @Override
   public void onItemClickedAt(int position) {
        ListItem selectedItem = arrayList.get(position);

        Intent intent =  new Intent(context, IteminformationActivity.class);   //화면 넘겨주기
        intent.putExtra("artName", selectedItem.getArtName());
        context.startActivity(intent);

        Toast.makeText(context, selectedItem.getArtName() +
                            "\n" + selectedItem.getArtist() +
                            "\n" + selectedItem.getPrice() , Toast.LENGTH_SHORT).show();   //해당위치 아이템 클릭시 토스트메시지창 띄우기

   }
}

 

spark (227,470 포인트) 님이 2021년 11월 8일 답변
String itemId = getIntent().getStringExtra("itemid");
이렇게 받아서 파이어베이스 쿼리에서 사용하셔야 할 것 같은데요. 앞서 말씀드렸다시피 정확한 파이어베이스 쿼리는 테스트를 해보지 못하기 때문에 말씀드릴 수가 없구요.
제가 보기에 IteminformationActivity는 리스트형태가 아니라 리사이클러뷰를 사용하시는게 아니라 그냥 아이템 디테일만 보여주면 되는 것 같은데, 아닌가요?
파이어베이스에서  해당데이터들을 가져와서 해당 액티비티에서 어떻게 구현을 할지 감이 안잡혀서요..
리사이클러로는 한번에 바인딩이 가능한데
리스트 화면에 뷰홀더에 보시면 아이템을 바인딩하는 로직이 있는데, 그걸 활용하면 되지 않을까요?

private ImageView et_image;
private TextView et_artName, et_artist, et_price;

@Override
public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
    if (dataSnapshot.getChildren().size() != 1) throw new RuntimeExcetion("조회 결과가 없거나 하나 이상입니다.");
    ItemList itemList;
    for(DataSnapshot snapshot : dataSnapshot.getChildren()) {
         itemList = snapshot.getValue(ItemList.class);
         itemList.setItemid("itemId");
     }

     bindArtwork(itemList);
});

private void bindArtwork(ItemList itemList) {
       Glide.with(this)
                .load(itemList.getImage())
                .into(et_image);
       et_artName.setText(itemList.getArtName());
       et_artist.setText(itemList.getArtist());
       et_price.setText(String.valueOf(itemList.getPrice()));
}
상세디테일 부분도 리사이클러뷰를 이용해서 할순 없을까요?
package com.example.shoppingmall;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;

import com.bumptech.glide.Glide;
import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.database.DataSnapshot;
import com.google.firebase.database.DatabaseError;
import com.google.firebase.database.DatabaseReference;
import com.google.firebase.database.FirebaseDatabase;
import com.google.firebase.database.ValueEventListener;

import java.util.ArrayList;

public class IteminformationActivity extends AppCompatActivity {


    private ArrayList<ItemList> arrayList;
    private FirebaseDatabase database;
    private DatabaseReference databaseReference;



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



        arrayList = new ArrayList<>(); //ItemList객체를 담을 arraylsit(어댑터 쪽으로)

        getIntent().getStringExtra("itemID");




        database = FirebaseDatabase.getInstance(); //파이어베이스 데이터베이스 연동
        databaseReference = database.getReference().child("Itme_01"); //DB 테이블 연결


        databaseReference.addValueEventListener(new ValueEventListener() {   //addListenerForSingleValueEvent() : 콜백함수를  한번만 호출할때 사용 메소드

            private ImageView et_image;
            private TextView et_artName, et_artist, et_price , et_itemID, et_detail;



            @Override
            public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
                ItemList itemlist = new ItemList();
                for (DataSnapshot snapshot : dataSnapshot.getChildren()) {
                    itemlist = snapshot.getValue(ItemList.class);
                    itemlist.setItemid("itemID");
                }
                bindArtwork(itemlist);
            }

            private void bindArtwork(ItemList itemlist) {
                Glide.with(this.et_image)
                        .load(itemlist.getImage())
                        .into(et_image);
                et_artName.setText(itemlist.getArtName());
                et_artist.setText(itemlist.getArtist());
                et_price.setText(String.valueOf(itemlist.getPrice()));
                et_itemID.setText(String.valueOf(itemlist.getItemid()));
                et_detail.setText(itemlist.getDetail());
            }

            @Override
            public void onCancelled(@NonNull DatabaseError error) {
                // DB를 가져오던중 에러 발생시
                Log.e("IteminformationActivity", String.valueOf(error.toException())); //에러문 출력
            }
        });

    }
}

현재 여기까진 된거 같은데 바인딩이 안되어지네요..
0 추천

제가 테스트해 본 소스코드를 올릴게요. 코드가 완전히 정리된 코드는 아니지만, 로직을 보시는데는 별 문제 없으실 거예요. 제 코드에는 포함시키지는 않았지만, 데이터를 처리하는 로직의 분리와 에러핸들링의 경우도 고려하시면 좋을 것 같아요.
Github에 해당 소스를 올려놨으니까, 이메일을 알려주시면 리포를 볼 수 있도록 해드리겠습니다.

import java.io.Serializable;

public class Artwork implements Serializable {
    private String itemId;
    private String artName;
    private String artist;
    private String image;
    private int price;

    public Artwork() {
    }

    public Artwork(String itemId, String artName, String artist, String image, int price) {
        this.itemId = itemId;
        this.artName = artName;
        this.artist = artist;
        this.image = image;
        this.price = price;
    }

    public String getItemId() {
        return itemId;
    }

    public void setItemId(String itemId) {
        this.itemId = itemId;
    }

    public String getArtName() {
        return artName;
    }

    public void setArtName(String artName) {
        this.artName = artName;
    }

    public String getArtist() {
        return artist;
    }

    public void setArtist(String artist) {
        this.artist = artist;
    }

    public String getImage() {
        return image;
    }

    public void setImage(String image) {
        this.image = image;
    }

    public int getPrice() {
        return price;
    }

    public void setPrice(int price) {
        this.price = price;
    }
}

 

 

public class ArtworkListActivity extends AppCompatActivity {

    public final static String ARTWORKS_REF_NAME = "Itemlist";

    private ActivityArtworkListBinding binding;
    private ArtworkAdapter artworksAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        binding = ActivityArtworkListBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());

        viewDidLoad();
        loadArtworks();
    }

    private void viewDidLoad() {
        artworksAdapter = new ArtworkAdapter();
        artworksAdapter.setListener(new ArtworkAdapter.Listener() {
            @Override
            public void onArtworkClicked(Artwork artwork) {
                artworkClicked(artwork);
            }
        });

        binding.artworksRcv.setAdapter(artworksAdapter);
    }

    private void artworkClicked(Artwork artwork) {
        Intent intent = ArtworkActivity.artworkIntent(this, artwork);
        startActivity(intent);
    }

    private void loadArtworks() {
        binding.loadingIndicator.show();

        FirebaseDatabase database = FirebaseDatabase.getInstance();
        database.getReference(ARTWORKS_REF_NAME)
                .addValueEventListener(new ValueEventListener() {
                    @Override
                    public void onDataChange(@NonNull DataSnapshot snapshot) {
                        dataFetched(snapshot);
                    }

                    @Override
                    public void onCancelled(@NonNull DatabaseError error) {

                    }
                });
    }

    private void dataFetched(@NonNull DataSnapshot dataSnapshot) {
        List<Artwork> artworks = new ArrayList<>();
        for (DataSnapshot snapshot : dataSnapshot.getChildren()) {
            Artwork artwork = snapshot.getValue(Artwork.class);
            assert artwork != null;
            artwork.setItemId(snapshot.getKey());
            artworks.add(artwork);
        }

        runOnUiThread(() -> {
            artworksAdapter.submitList(artworks);
            binding.loadingIndicator.hide();
        });
    }

}

 

public class ArtworkAdapter extends RecyclerView.Adapter<ArtworkViewHolder> {
    interface Listener {
        void onArtworkClicked(Artwork artwork);
    }

    private List<Artwork> items = new ArrayList<>();
    private Listener listener;

    public void setListener(Listener listener) {
        this.listener = listener;
    }

    public void submitList(List<Artwork> items) {
        this.items = items;
        notifyDataSetChanged();
    }

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

    @NonNull
    @Override
    public ArtworkViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_artwork, parent, false);
        return new ArtworkViewHolder(itemView, listener);
    }

    @Override
    public void onBindViewHolder(@NonNull ArtworkViewHolder holder, int position) {
        Artwork artwork = getItem(position);
        holder.bind(artwork);
    }

    private Artwork getItem(int position) {
        return items.get(position);
    }
}


public class ArtworkViewHolder extends RecyclerView.ViewHolder {

    private final ItemArtworkBinding binding;
    private ArtworkAdapter.Listener listener;

    public ArtworkViewHolder(@NonNull View itemView, ArtworkAdapter.Listener listener) {
        super(itemView);
        binding = ItemArtworkBinding.bind(itemView);
        this.listener = listener;
    }

    public void bind(Artwork artwork) {
        Glide.with(binding.artworkImage)
                .load(artwork.getImage())
                .into(binding.artworkImage);
        binding.nameText.setText(artwork.getArtName());
        binding.priceText.setText(String.valueOf(artwork.getPrice()));
        itemView.setOnClickListener(v -> {
            listener.onArtworkClicked(artwork);
        });
    }
}

 

public class ArtworkActivity extends AppCompatActivity {

    private static final String EXTRA_ARTWORK = "artwork";

    public static Intent artworkIntent(Context context, Artwork artwork) {
        Intent intent = new Intent(context, ArtworkActivity.class);
        intent.putExtra(EXTRA_ARTWORK, artwork);
        return intent;
    }

    private ActivityArtworkBinding binding;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        binding = ActivityArtworkBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());

        viewDidLoad();
    }

    private void viewDidLoad() {
        Artwork artwork = (Artwork) getIntent().getSerializableExtra(EXTRA_ARTWORK);
        loadArtwork(artwork);
    }

    // 파이어베이스에서 데이터를 다시 가져오지 않고 이전 액티비티에서 전달받은 Artwork 인스턴스만으로 화면에 데이터를 표시하는 것만으로
    // 충분하다고 판단될 경우는 파이어베이스를 호출할 필요가 없음.
    private void loadArtwork(Artwork artwork) {
        FirebaseDatabase database = FirebaseDatabase.getInstance();
        database.getReference(ARTWORKS_REF_NAME)
                .child(artwork.getItemId())
                .get()
                .addOnCompleteListener(new OnCompleteListener<DataSnapshot>() {
                    @Override
                    public void onComplete(@NonNull Task<DataSnapshot> task) {
                        if (!task.isSuccessful()) {
                            artworkLoadFailure(task.getException());
                            return;
                        }

                        artworkLoadSuccess(task);
                    }
                });

    }

    private void artworkLoadSuccess(@NonNull Task<DataSnapshot> task) {
        Artwork artwork = Objects.requireNonNull(task.getResult()).getValue(Artwork.class);
        assert artwork != null;
        bindArtwork(artwork);
    }

    private void bindArtwork(Artwork artwork) {
        Glide.with(binding.artworkImage)
                .load(artwork.getImage())
                .into(binding.artworkImage);
        binding.nameText.setText(artwork.getArtName());
        binding.priceText.setText(String.valueOf(artwork.getPrice()));
    }

    private void artworkLoadFailure(Exception exception) {
        // 에러 처리할 것
    }
}

 

레이아웃 파일과 빌드파일은 별도로 올리지 않겠습니다.

 

spark (227,470 포인트) 님이 2021년 11월 10일 답변
realtime DB의 상품 정보를 cloud Firestore의 컬렉션으로 추가할수도 있나요.?
네. 할 수 있습니다만, Realtime database와 FireStore는 같은 용도이므로 하나만 사용하시는 게 관리측면에서 좋다고 생각합니다. FireStore를 Realtime DB의 상위버전이라고 보시면 됩니다.
넵! 상세창에서 그대로 해당아이템을 장바구니로 넣어줄려면 데이터를 어떻게 넘겨줘야하나요 상세설명창의

 private static final String EXTRA_ITEMLIST = "itemList";


    public static Intent itemListIntent(Context context, ItemList itemList) {
        Intent intent = new Intent(context, IteminformationActivity.class);
        intent.putExtra(EXTRA_ITEMLIST ,  itemList);
        return intent;
    }
    private ActivityIteminformationBinding binding;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        binding = ActivityIteminformationBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());

        viewDidLoad();




    }

    private void viewDidLoad() {
        ItemList itemList = (ItemList) getIntent().getSerializableExtra(EXTRA_ITEMLIST);    //받는 액티비티에서 getSerializableExtra(EXTRA_ITEMLIST)로 받기
        loadItem(itemList);
    }

    private void loadItem(ItemList itemList) {
        FirebaseDatabase database = FirebaseDatabase.getInstance();
        database.getReference(ITEMLIST_REF_NAME)  //ITEMLIST_REF_NAME -> DB의 Itemlist 테이블
                .child(itemList.getItemID())     //하위 itemList의 해당 아이템 id가져오기
                .get()
                .addOnCompleteListener(task -> {      //익셉션 처리
                    if (!task.isSuccessful()) {
                        artworkLoadFailure(task.getException());
                        return;
                    }

                    artworkLoadSuccess(task);   //성공시
                });
    }
    private void artworkLoadSuccess(Task<DataSnapshot> task) {
        ItemList itemList = Objects.requireNonNull(task.getResult()).getValue(ItemList.class);
        assert itemList != null;   //itemList null값 확인
        bindItem(itemList);   //아이템 바인딩
    }

    private void bindItem(ItemList itemList) {    //아이템 바인딩.
        Glide.with(binding.etImage)
                .load(itemList.getImage())
                .into(binding.etImage);
        binding.etArtName.setText(itemList.getArtName());     //해당아이템의 이름을 가져와 setText로 써줌.
        binding.etArtist.setText(itemList.getArtist());
        binding.etPrice.setText(String.valueOf(itemList.getPrice()));    //숫자형은 string으로 변환하여 사용
        binding.etDetail.setText(itemList.getDetail());
        binding.etMaketime.setText(itemList.getMaketime());
        binding.etWay.setText(itemList.getWay());
        binding.etSize.setText(itemList.getSize());

    }

    private void artworkLoadFailure(Exception exception) {
        //에러 출력
    }

이부분을 그대로 넘기나요
장바구니 기능은 그렇게 간단하게 구현하기 힘듧니다. 문제없이 잘 동작하려면 고려해야할 사항이 좀 많을 것 같습니다. 첫번째는 Perisisten Storage(파일이나 DB)를 사용하시거나 최소한 ViewModel + SavedStateHandle 을 사용해서 앱이 백그라운드로 갔을 때 시스템에 의해 죽더라도(실제로 생각보다 자주 생깁니다.) 현재의 데이터를 보관해줄 수 있어야 합니다. 물론 데이터만 누락이 되지 않는다면 파이어베이스를 사용해서 저장하셔도 됩니다. (개인적으로는 로컬 스토리지가 관리측면이나 데이터 정리 등의 면에서 더 나아보이긴 하지만요)
뭐를 선택하시던,
IteminformationActivity에서 장바구니에 넣는 버튼을 누르면, itemList를 장바구니에 넣으시면 되겠죠. 이때 itemList를 통째로 저장하실 수도 있고, itemId만 저장해서 파이어베이스에 있는 itemList와 연동할 수도 있겠죠. 이 부분은 님의 업무 요구사항에 따라 디자인이 갈리는 부분입니다. 요구사항을 적절하게 파악해서 결정하시면 될 것같아요.
추가로 파이이베이스만 가지고 모든 걸 다 처리하실 수 있는지 좀 의문이 드네요. 상황에 따라서 로컬에만 데이터를 저장했다가 구매시에만 서버로 보내는게 맞을 것 같은데, 쇼핑몰 앱들이 일반적으로 어떻게 처리하는지 찾아보세요. 리모트에 저장을 해주면 더 좋긴한데, 백엔드의 도움없이 한다면, 나중에 쓸데없는 데이터가 쌓여서 골치가 아플 것 같은데요. 이것도 제가 해드릴 수없는 님의 상황에 따른 선택이 되어야 합니다.
현재 상태에서 메인 리사이클러뷰에서 상품아이템 검색기능을 구현하고 싶은데 기본적인 소스를 알수 있을까요
...