Android实现每2篇博客后插入Firebase实时数据库广告
问题背景
我有如下结构的Firebase实时数据库,根节点包含blog_posts和Ads两个子节点:
root ├─ blog_posts │ ├─ post_id_1 │ │ ├─ title: "Blog Post 1" │ │ ├─ content: "Content of Blog Post 1" │ │ ├─ contentType: "text" │ │ └─ contentUrl: "https://example.com/post1" │ ├─ post_id_2 │ │ ├─ title: "Blog Post 2" │ │ ├─ content: "Content of Blog Post 2" │ │ ├─ contentType: "text" │ │ └─ contentUrl: "https://example.com/post2" │ ├─ post_id_3 │ │ ├─ title: "Blog Post 3 " │ │ ├─ content: "Content of Blog Post 3" │ │ ├─ contentType: "text" │ │ └─ contentUrl: "https://example.com/post3" │ └─ post_id_4 │ ├─ title: "Blog Post 4" │ ├─ content: "Content of Blog Post 4" │ ├─ contentType: "text" │ └─ contentUrl: "https://example.com/post4" └─ Ads ├─ ad_id_1 │ ├─ title: "Ad 1" │ ├─ content: "Content of Ad 1" │ ├─ contentType: "image" │ └─ contentUrl: "https://example.com/ad1" └─ ad_id_2 ├─ title: "Ad 2" ├─ content: "Content of Ad 2" ├─ contentType: "image" └─ contentUrl: "https://example.com/ad2"
需求与问题
我要实现每展示2篇博客文章后插入一条广告(比如Ad1在Post2后、Ad2在Post4后、Ad3在Post6后),但现在运行代码后,所有广告都显示在所有博客文章之后。
现有代码
MainActivityViewPost类
import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import omega.jschii.healourlandafrica.R; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; 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; import java.util.List; import androidx.annotation.NonNull; public class MainActivityViewPost extends AppCompatActivity { private RecyclerView recyclerView; private Adapter adapter; private List<Object> items; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main_view_post); recyclerView = findViewById(R.id.recyclerView); LinearLayoutManager layoutManager = new LinearLayoutManager(this); recyclerView.setLayoutManager(layoutManager); items = new ArrayList<>(); DatabaseReference databaseReference = FirebaseDatabase.getInstance().getReference(); databaseReference.child("blog_posts").addListenerForSingleValueEvent(new ValueEventListener() { @Override public void onDataChange(@NonNull DataSnapshot dataSnapshot) { for (DataSnapshot snapshot : dataSnapshot.getChildren()) { BlogPost blogPost = snapshot.getValue(BlogPost.class); items.add(blogPost); } adapter.notifyDataSetChanged(); } @Override public void onCancelled(@NonNull DatabaseError databaseError) { // Handle database error } }); databaseReference.child("Ads").addListenerForSingleValueEvent(new ValueEventListener() { @Override public void onDataChange(@NonNull DataSnapshot dataSnapshot) { for (DataSnapshot snapshot : dataSnapshot.getChildren()) { Ads ad = snapshot.getValue(Ads.class); items.add(ad); } adapter.notifyDataSetChanged(); } @Override public void onCancelled(@NonNull DatabaseError databaseError) { // Handle database error } }); adapter = new Adapter(this, items); recyclerView.setAdapter(adapter); } }
Adapter类
import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; import androidx.annotation.NonNull; import androidx.recyclerview.widget.RecyclerView; import java.util.List; public class Adapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { private static final int VIEW_TYPE_BLOG_POST = 0; private static final int VIEW_TYPE_AD = 1; private List<Object> items; private Context context; // Constructor public Adapter(Context context, List<Object> items) { this.context = context; this.items = items; } @Override public int getItemViewType(int position) { Object item = items.get(position); return item instanceof BlogPost ? VIEW_TYPE_BLOG_POST : VIEW_TYPE_AD; } @NonNull @Override public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { LayoutInflater inflater = LayoutInflater.from(parent.getContext()); if (viewType == VIEW_TYPE_BLOG_POST) { View itemView = inflater.inflate(R.layout.item_blog_post, parent, false); return new BlogPostViewHolder(itemView); } else { View itemView = inflater.inflate(R.layout.item_ad, parent, false); return new AdViewHolder(itemView); } } @Override public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { if (holder instanceof BlogPostViewHolder) { BlogPostViewHolder blogPostViewHolder = (BlogPostViewHolder) holder; BlogPost blogPost = (BlogPost) items.get(position); blogPostViewHolder.bind(blogPost); } else if (holder instanceof AdViewHolder) { AdViewHolder adViewHolder = (AdViewHolder) holder; Ads ad = (Ads) items.get(position); adViewHolder.bind(ad); } } @Override public int getItemCount() { return items.size(); } // ViewHolder classes for blog posts and ads private class BlogPostViewHolder extends RecyclerView.ViewHolder { private TextView titleTextView; BlogPostViewHolder(@NonNull View itemView) { super(itemView); titleTextView = itemView.findViewById(R.id.titleTextView); } void bind(BlogPost blogPost) { titleTextView.setText(blogPost.getTitle()); } } private class AdViewHolder extends RecyclerView.ViewHolder { private TextView titleTextView; AdViewHolder(@NonNull View itemView) { super(itemView); titleTextView = itemView.findViewById(R.id.titleTextView); } void bind(Ads ad) { titleTextView.setText(ad.getTitle()); } } }
解决方案
问题出在你把所有博客先加到列表,再把所有广告追加到后面,自然会出现广告全在末尾的情况。要实现每2篇博客插广告,得先分别存储博客和广告列表,等两个数据都加载完成后,再按规则合并成最终列表。
修改MainActivityViewPost类,具体改动如下:
- 新增两个列表分别存储博客和广告,替换原来直接用
items的方式 - 用计数器跟踪两个数据请求的完成状态,确保都加载完再合并
- 编写合并逻辑:每添加2篇博客就插入一条广告,广告循环使用(如果广告数量不足,重复现有广告)
修改后的MainActivityViewPost代码:
import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import omega.jschii.healourlandafrica.R; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; 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; import java.util.List; import androidx.annotation.NonNull; public class MainActivityViewPost extends AppCompatActivity { private RecyclerView recyclerView; private Adapter adapter; private List<Object> items; // 新增两个列表分别存储博客和广告 private List<BlogPost> blogPosts; private List<Ads> adsList; // 计数器跟踪数据加载完成数 private int dataLoadedCount = 0; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main_view_post); recyclerView = findViewById(R.id.recyclerView); LinearLayoutManager layoutManager = new LinearLayoutManager(this); recyclerView.setLayoutManager(layoutManager); items = new ArrayList<>(); blogPosts = new ArrayList<>(); adsList = new ArrayList<>(); adapter = new Adapter(this, items); recyclerView.setAdapter(adapter); DatabaseReference databaseReference = FirebaseDatabase.getInstance().getReference(); // 加载博客数据 databaseReference.child("blog_posts").addListenerForSingleValueEvent(new ValueEventListener() { @Override public void onDataChange(@NonNull DataSnapshot dataSnapshot) { for (DataSnapshot snapshot : dataSnapshot.getChildren()) { BlogPost blogPost = snapshot.getValue(BlogPost.class); blogPosts.add(blogPost); } dataLoadedCount++; checkAndMergeData(); } @Override public void onCancelled(@NonNull DatabaseError databaseError) { // Handle database error dataLoadedCount++; checkAndMergeData(); } }); // 加载广告数据 databaseReference.child("Ads").addListenerForSingleValueEvent(new ValueEventListener() { @Override public void onDataChange(@NonNull DataSnapshot dataSnapshot) { for (DataSnapshot snapshot : dataSnapshot.getChildren()) { Ads ad = snapshot.getValue(Ads.class); adsList.add(ad); } dataLoadedCount++; checkAndMergeData(); } @Override public void onCancelled(@NonNull DatabaseError databaseError) { // Handle database error dataLoadedCount++; checkAndMergeData(); } }); } // 检查数据是否全部加载完成,完成则合并列表 private void checkAndMergeData() { if (dataLoadedCount != 2) { return; } items.clear(); int adIndex = 0; int adCount = adsList.size(); if (adCount == 0) { // 如果没有广告,直接添加所有博客 items.addAll(blogPosts); adapter.notifyDataSetChanged(); return; } // 按规则合并博客和广告 for (int i = 0; i < blogPosts.size(); i++) { items.add(blogPosts.get(i)); // 每2篇博客后插入广告(注意i从0开始,所以i+1是第N篇) if ((i + 1) % 2 == 0) { items.add(adsList.get(adIndex)); adIndex = (adIndex + 1) % adCount; // 循环使用广告 } } adapter.notifyDataSetChanged(); } }
Adapter类不需要修改,它已经支持两种视图类型的判断和绑定逻辑。
这样修改后,就能实现每2篇博客文章后插入一条广告的效果,广告会循环使用(比如Ad1、Ad2、Ad1、Ad2...这样依次插入)。
内容的提问来源于stack exchange,提问作者F.joel
相关产品推荐
相关产品推荐

