Android Studio中无法加载Firebase实时数据库所有数据到RecyclerView
解决Firebase实时数据库多用户广告数据加载问题
问题说明
在Android Studio中使用Firebase实时数据库时,Advertisements节点下按用户ID分文件夹存储广告数据,但现有LoadAdvertisements方法只能加载指定用户ID文件夹下的广告,无法展示所有用户的广告内容,RecyclerView本身功能正常。
数据库结构
{ "Advertisements": { "Fg65Rt43Fhjn5js6hGJ": [ { "advertisement_price": "50e", "description": "propale skije", "thumbnailURL": "https://firebasestorage.googleapis.com/v0/b/skisell-c295d.appspot.com/o/PostImages%2FZxw5nuwqXRfWJmlN93i9xBVq2ml1%2F0%2Fthumbnail?alt=media&token=19f41472-cb3e-49fb-8100", "title": "cao" }, { "advertisement_price": "40e", "description": "nesto", "thumbnailURL": "https://firebasestorage.googleapis.com/v0/b/skisell-c295d.appspot.com/o/PostImages%2FZxw5nuwqXRfWJmlN93i9xBVq2ml1%2F0%2Fthumbnail?alt=media&token=19f41472-cb3e-49fb-8100", "title": "isto nesto" } ], "Xd45GhtR569Ds12Xdfiu6": [ { "advertisement_price": "150e", "description": "blah", "thumbnailURL": "https://firebasestorage.googleapis.com/v0/b/skisell-c295d.appspot.com/o/PostImages%2FZxw5nuwqXRfWJmlN93i9xBVq2ml1%2F0%2Fthumbnail?alt=media&token=19f41472-cb3e-49fb-8100", "title": "nesto" } ], "Zxw5nuwqXRfWJmlN93i9xBVq2ml1": [ { "advertisement_price": "100e", "description": "elan", "thumbnailURL": "https://firebasestorage.googleapis.com/v0/b/skisell-c295d.appspot.com/o/PostImages%2FZxw5nuwqXRfWJmlN93i9xBVq2ml1%2F0%2Fthumbnail?alt=media&token=19f41472-cb3e-49fb-8100", "title": "skije" } ] }, "Users": { "Zxw5nuwqXRfWJmlN93i9xBVq2ml1": { "city": "Kraljevo", "name": "Milutin", "profileImage": "https://firebasestorage.googleapis.com/v0/b/skisell-c295d.appspot.com/o/ProfileImages%2FZxw5nuwqXRfWJmlN93i9xBVq2ml1?alt=media&token=761b55d9-1470", "status": "offline", "surname": "Nedeljkovic", "username": "micocomi" } } }
当前代码
//this is the declaration in the OnCreate method //DatabaseReference AdvertisementsRef; //AdvertisementsRef = FirebaseDatabase.getInstance().getReference().child("Advertisements"); private void LoadAdvertisements() { ValueEventListener eventListener = new ValueEventListener() { @Override public void onDataChange(@NonNull DataSnapshot snapshot) { options = new FirebaseRecyclerOptions.Builder<Advertisements>().setQuery(AdvertisementsRef.child("Fg65Rt43Fhjn5js6hGJ"), Advertisements.class).build(); adapter = new FirebaseRecyclerAdapter<Advertisements, MyViewHolder>(options) { @Override protected void onBindViewHolder(@NonNull MyViewHolder holder, int position, @NonNull Advertisements model) { holder.AdTitle.setText(model.getTitle()); holder.AdPrice.setText(model.getAdvertisement_price()); Picasso.get().load(model.getThumbnailURL()).into(holder.thumbnail1); } @NonNull @Override public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_design, parent, false); return new MyViewHolder(view); } }; adapter.startListening(); recyclerView.setAdapter(adapter); } @Override public void onCancelled(@NonNull DatabaseError error) { Toast.makeText(MainActivity.this, "Oops, something went wrong", Toast.LENGTH_SHORT).show(); } }; AdvertisementsRef.addListenerForSingleValueEvent(eventListener); }
解决方案
方案一:遍历现有节点收集所有广告数据(无需修改数据库结构)
现有代码的问题是直接指定了单个用户ID节点,需要遍历所有用户ID节点,将每个节点下的广告数据收集到统一列表中,再用普通RecyclerView Adapter展示:
- 定义全局的广告列表和普通Adapter:
private List<Advertisements> allAdvertisements = new ArrayList<>(); private AdvertisementAdapter advertisementAdapter;
- 在
onCreate方法中初始化Adapter并绑定到RecyclerView:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 初始化RecyclerView(假设已完成布局绑定) recyclerView = findViewById(R.id.recycler_view); recyclerView.setLayoutManager(new LinearLayoutManager(this)); // 初始化Adapter advertisementAdapter = new AdvertisementAdapter(allAdvertisements); recyclerView.setAdapter(advertisementAdapter); // 初始化数据库引用 AdvertisementsRef = FirebaseDatabase.getInstance().getReference().child("Advertisements"); // 加载广告 LoadAdvertisements(); }
- 修改
LoadAdvertisements方法遍历所有节点:
private void LoadAdvertisements() { ValueEventListener eventListener = new ValueEventListener() { @Override public void onDataChange(@NonNull DataSnapshot snapshot) { allAdvertisements.clear(); // 清空旧数据避免重复 // 遍历每个用户ID节点 for (DataSnapshot userSnapshot : snapshot.getChildren()) { // 遍历该用户下的所有广告 for (DataSnapshot adSnapshot : userSnapshot.getChildren()) { Advertisements ad = adSnapshot.getValue(Advertisements.class); if (ad != null) { allAdvertisements.add(ad); } } } advertisementAdapter.notifyDataSetChanged(); // 更新RecyclerView } @Override public void onCancelled(@NonNull DatabaseError error) { Toast.makeText(MainActivity.this, "Oops, something went wrong", Toast.LENGTH_SHORT).show(); } }; // 用addValueEventListener可实时监听数据变化,若只需加载一次可保留addListenerForSingleValueEvent AdvertisementsRef.addValueEventListener(eventListener); }
- 实现普通的RecyclerView Adapter:
class AdvertisementAdapter extends RecyclerView.Adapter<MyViewHolder> { private List<Advertisements> adList; public AdvertisementAdapter(List<Advertisements> adList) { this.adList = adList; } @NonNull @Override public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_design, parent, false); return new MyViewHolder(view); } @Override public void onBindViewHolder(@NonNull MyViewHolder holder, int position) { Advertisements model = adList.get(position); holder.AdTitle.setText(model.getTitle()); holder.AdPrice.setText(model.getAdvertisement_price()); Picasso.get().load(model.getThumbnailURL()).into(holder.thumbnail1); } @Override public int getItemCount() { return adList.size(); } }
方案二:调整数据库结构(推荐,更符合Firebase最佳实践)
Firebase实时数据库中不推荐使用数组存储列表数据(并发修改易冲突、查询灵活性差),建议将广告直接存储在Advertisements节点下,用push()生成的唯一键标识每个广告,并添加userId字段关联用户:
调整后的数据库结构
{ "Advertisements": { "-NxYzaKdF789abc": { "userId": "Fg65Rt43Fhjn5js6hGJ", "advertisement_price": "50e", "description": "propale skije", "thumbnailURL": "https://firebasestorage.googleapis.com/v0/b/skisell-c295d.appspot.com/o/PostImages%2FZxw5nuwqXRfWJmlN93i9xBVq2ml1%2F0%2Fthumbnail?alt=media&token=19f41472-cb3e-49fb-8100", "title": "cao" }, "-NxYzbLmN123def": { "userId": "Fg65Rt43Fhjn5js6hGJ", "advertisement_price": "40e", "description": "nesto", "thumbnailURL": "https://firebasestorage.googleapis.com/v0/b/skisell-c295d.appspot.com/o/PostImages%2FZxw5nuwqXRfWJmlN93i9xBVq2ml1%2F0%2Fthumbnail?alt=media&token=19f41472-cb3e-49fb-8100", "title": "isto nesto" }, "-NxYzcOpQ456ghi": { "userId": "Xd45GhtR569Ds12Xdfiu6", "advertisement_price": "150e", "description": "blah", "thumbnailURL": "https://firebasestorage.googleapis.com/v0/b/skisell-c295d.appspot.com/o/PostImages%2FZxw5nuwqXRfWJmlN93i9xBVq2ml1%2F0%2Fthumbnail?alt=media&token=19f41472-cb3e-49fb-8100", "title": "nesto" } } }
简化后的加载代码
此时可以直接用FirebaseRecyclerAdapter查询所有广告:
private void LoadAdvertisements() { FirebaseRecyclerOptions<Advertisements> options = new FirebaseRecyclerOptions.Builder<Advertisements>() .setQuery(AdvertisementsRef, Advertisements.class) .build(); FirebaseRecyclerAdapter<Advertisements, MyViewHolder> adapter = new FirebaseRecyclerAdapter<Advertisements, MyViewHolder>(options) { @Override protected void onBindViewHolder(@NonNull MyViewHolder holder, int position, @NonNull Advertisements model) { holder.AdTitle.setText(model.getTitle()); holder.AdPrice.setText(model.getAdvertisement_price()); Picasso.get().load(model.getThumbnailURL()).into(holder.thumbnail1); } @NonNull @Override public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_design, parent, false); return new MyViewHolder(view); } }; recyclerView.setAdapter(adapter); adapter.startListening(); }
内容的提问来源于stack exchange,提问作者Milutin Nedeljkovic
相关产品推荐
相关产品推荐

