Embedding ads within Recyclerview
Asked Answered
C

6

50

I am trying to upgrade my app from listview to recyclerview. When I was using listview I had embedded ads within the listview using this tutorial.

I am not able to add it within recyclerview similarly. Any views on how this is to be done in a Recyclerview?

Currently in my listview the code is as below for loading ads:

    if ((position % k) == 0) {
      if (convertView instanceof AdView) {
        return convertView;
      } else {
        // Create a new AdView
        AdView adView = new AdView(activity, AdSize.BANNER,
                                   ADMOB_ID);

        float density = activity.getResources().getDisplayMetrics().density;
        int height = Math.round(AdSize.BANNER.getHeight() * density);
        AbsListView.LayoutParams params = new AbsListView.LayoutParams(
            AbsListView.LayoutParams.FILL_PARENT,
            height);
        adView.setLayoutParams(params);

        adView.loadAd(new AdRequest());
        return adView;
      }
    } else {
      return delegate.getView(position - (int) Math.ceil(position / k) - 1,
          convertView, parent);
    }

This is how it should look:

ListView Items

Update: Refer this video from google, it gives the complete explanation

Carsoncarstensz answered 15/1, 2015 at 17:28 Comment(4)
any code to show? how is it looking right now?Adductor
Added existing code snippet..Carsoncarstensz
For people looking for this solution... there's now an implementation example of Cigogne answer... I think is very usefull... I'm pasting it here because it might be usefull for people lookin on this questionPencil
The video is not found! Pls fix itCourtship
M
49

In your adapter, you first need to override getItemViewType, for example:

@Override
public int getItemViewType(int position) 
{
    if (position % 5 == 0)
        return AD_TYPE; 
    return CONTENT_TYPE;
}

Then in onCreateViewHolder, inflate a different view according to the type. Something like this:

@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) 
{
    View v = null;

    if (viewType == AD_TYPE)
    {
        v = new AdView(activity, AdSize.BANNER, ADMOB_ID);
        float density = activity.getResources().getDisplayMetrics().density;
        int height = Math.round(AdSize.BANNER.getHeight() * density);
        AbsListView.LayoutParams params = new AbsListView.LayoutParams(AbsListView.LayoutParams.FILL_PARENT,height);
        v.setLayoutParams(params);

     AdRequest adRequest = new AdRequest.Builder().build();
        if (adRequest != null && v != null){
            v.loadAd(adRequest);
         }
    }
    else 
        v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.list_item_layout, viewGroup, false);

    RecyclerView.ViewHolder viewHolder = new RecyclerView.ViewHolder(v);
    return viewHolder;
}
Mansfield answered 15/1, 2015 at 20:46 Comment(13)
As per new guidelines we have to use play services and the code should be within layout. How to place the adview using new play services guidelines. developers.google.com/mobile-ads-sdk/docs/dfp/android/…Carsoncarstensz
I need to insert an horizontal scroll view in place of ad view. Can u please provide some code for that.Swinford
( position % 5 == 0 ) to change view type will skip the actual item at that position right ?Lesser
No, it simply assigns a different view type every 5 positions. It's up to onCreateViewHolder to decide what to do with that view typeAptitude
@Lesser is right. In this example, either your data has to be augmented to inject ads every 5 positions or you will skip over those items.Kink
Don't forget to change getItemCount() accordingly: 'return items_count + (items_count % 5)'Unconformable
@Unconformable it should be item_count / 5 instead "%"Reggie
Just one question in my mind, is it alright to display admob ad inside a recycler view? I mean, After every 5 or 10 item interval. Does it break any admob policy ?Testa
@CigogneEveillée I'm getting ViewHolder is abstract: cannot be instantiated. How can this be done now that it's been updated?Glucosuria
This actually skips items. I tested it and it does. Plus you can get IndexOutOfBoundsException at getItemViewType()Preface
See @Unconformable 's comment above "Don't forget to change getItemCount() accordingly: 'return items_count + (items_count % 5)'"Aptitude
whats different between this way and native adTriploid
In this approach, if the position is zero then the first ad visible and it is not good UI/UX approach.Skidmore
A
38

How to add AD list items and don't skip content items and don't insert null items to content list?

private static final int LIST_AD_DELTA = 3;
private static final int CONTENT = 0;
private static final int AD = 1;

@Override
public int getItemViewType(int position) {
    if (position > 0 && position % LIST_AD_DELTA == 0) {
        return AD;
    }
    return CONTENT;
}

@Override
public BaseRecyclerHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    if (viewType == CONTENT) {
        return new ContentRecyclerHolder(parent, layoutId) {
            @Override
            protected void onCardClick(CardView card) {
                fragmentManager.showPagerFragmentWithTransition(card.getContext(), getRealPosition(getAdapterPosition()));
            }
        };
    } else {
        return new AdRecyclerHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_ad, parent, false));
    }
}

@Override
public int getItemCount() {
    int additionalContent = 0;
    if (data.size() > 0 && LIST_AD_DELTA > 0 && data.size() > LIST_AD_DELTA) {
        additionalContent = data.size() / LIST_AD_DELTA;
    }
    return data.size() + additionalContent;
}

@Override
public void onBindViewHolder(BaseRecyclerHolder baseHolder, int position) {
    if (getItemViewType(position) == CONTENT) {
        ContentRecyclerHolder holder = (ContentRecyclerHolder) baseHolder;
        Content content = data.get(getRealPosition(position));
    } else {
        AdRecyclerHolder holder = (AdRecyclerHolder) baseHolder;
            AdRequest adRequest = new AdRequest.Builder().build();
            if (adRequest != null && holder.adView != null){
                holder.adView.loadAd(adRequest);
             }
    }
}

private int getRealPosition(int position) {
    if (LIST_AD_DELTA == 0) {
        return position;
    } else {
        return position - position / LIST_AD_DELTA;
    }
}
Avrom answered 15/11, 2016 at 11:11 Comment(14)
It works awesome, for future readers i suggest replacing BaseRecyclerHolder for RecyclerView.ViewHolderHerculean
@MartinDeSimone BaseRecyclerHolder is actually extends RecyclerView.ViewHolder in my projectAvrom
This skips over some content. For example, if LIST_AD_DELTA = 3 and you wish to display 8 content items, your last row will be an ad because (position: 9 % 3 = 0). So, just change the formula to get additionalContent in getItemCount() to: additionalContent = (data.size() + (data.size() / LIST_AD_DELTA)) / LIST_AD_DELTA;Aday
Getting IndexOutOfBounds exception in getItemViewTypePreface
@RichardMcFriendOluwamuyiwa make sure you are using getItemViewType from my example (there are no logic for such exception) or list items count is > 0Avrom
It's happening and it also shows multiple ads in sequence.Preface
I will post a question and send the link if you are willing to help outPreface
Fixed. I now get an extra adview at the bottomPreface
this skips some row :( p.s: thanks @AftabHussain for updated formulae. It doesnt skip after using thatRedletter
@ShahidKamal, Aftab Hussain please edit my answer, if you've tested your changesAvrom
@AftabHussain I have a recyclerview where i display all songs found on a device, so the items aren't constant and can change, so last item is always an ad if songs are added. Do you now how to fix this?Festoonery
@Vince Check out Zeeshan's answerAday
Can someone tell me what is ContentRecyclerHolder?Oshinski
@liva ContentRecyclerHolder and AdRecyclerHolder extend BaseRecyclerHolder. They are ViewHolderAvrom
H
22

In your ArrayList, add null values to positions you want to show ads

    for(int i=0;i<arr.size();i++){
       if(i%5==0)
       {
         alist.add(null);
       }
       alist.add(arr.get(i));
    }

Then in getItemViewType(),

  @Override
public int getItemViewType(int position) {
   if(qlist.get(position)==null)
       return AD_TYPE;
   return CONTENT_TYPE;
}

Then,

@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    View v = null;
    ViewHolder vh=null;
    if (viewType == AD_TYPE)
    {
        v=inflater.inflate(R.layout.adview_item, parent, false);
        vh=new AdviewHolder(v);

    }
    else {
        v = inflater.inflate(R.layout.cardview_items, parent, false);
        vh = new ContentViewHolder(v);
    }
    return vh;
}

This will not skip any items but will insert ads in to required positions.

Hydnocarpate answered 25/6, 2016 at 10:21 Comment(4)
what about if your using gson to parse jsonLoudspeaker
In every answer , i was getting wrong position for item . This one helped me +1. @kinsleykajiva after gettting json from server you can make new instance of class and add this filter value in itClothesline
@kinsleykajiva i've added my answer above regarding retrofit gson.Clothesline
@Hydnocarpate can you think of how to implement this with AsyncListDiffer because I got java.lang.IndexOutOfBoundsException:Mewl
C
4

I'm using retrofit with gson converter and need to display ads after every 5 items . By using above all answer,I was getting wrong position for item . So I've insert custom value to every 5th element in json response .like this

call.enqueue(new Callback<List<DataModel>>() {
            @Override
            public void onResponse(Call<List<DataModel>> call, Response<List<DataModel>> response) {

                if (response.body() != null) {

                    List<DataModel> list_Data = response.body();
                    List<DataModel> list_ad_Data = new ArrayList<>();

                        for (int i = 0; i < list_Data.size(); i++) {
                            if (i %5 == 0 ){
                                list_ad_Data.add(null);
                            }
                                list_ad_Data.add(list_Data.get(i));
                        }

                        adapter = new HomeAdapter(getActivity(), list_ad_Data);

In my Adapter. I've changed viewtype according to

@Override
    public int getItemViewType(int position) {
        if (list_data.get(position) == null) {
            return AD;
        }
        return CONTENT;

    }

changing viewtype is already mentioned in above answers. Hope it helps to future readers.

Clothesline answered 15/1, 2019 at 9:30 Comment(0)
E
0

It's easier to load the ads via databinding like this:

@BindingAdapter({"bind:loadAds"})
public static void loadAds(AdView adView, boolean load) {
    AdRequest adRequest = new AdRequest.Builder().build();
    adView.loadAd(adRequest);
}

and then in xml add this like to adView

        app:loadAds='@{true}'

like this:

  <com.google.android.gms.ads.AdView
        android:id="@+id/adView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        ads:adSize="BANNER"
        ads:adUnitId="ca-app-pub-3940256099942544/6300978111"
        app:loadAds='@{true}'
        />
Evin answered 29/12, 2018 at 18:0 Comment(2)
This will create a new add Request(builder) everytime the user tries to load an add.Chrissa
@AqsaShahid i think load more ads is better bc user more probably click and earn more money.Evin
I
0

I solve issue by just changing one line in onBindViewHolder()

@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, @SuppressLint("RecyclerView") int position) {
    if(holder.getItemViewType()==POST_TYPE) {
        PostHolder postHolder=(PostHolder) holder;
        int bindingPosition=position - position / LIST_AD_DELTA;
        postHolder.textView.setText(List_Item.get(bindingPosition).getBtnText());
        postHolder.cardView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent(context, DetailActivity.class);
                intent.putExtra("file", "file:///android_asset/" + List_Item.get(position).getHtmlFile());
                context.startActivity(intent);
            }
        });
    }else if(holder.getItemViewType()==AD_TYPE){

        
    }
Inhaul answered 23/5, 2022 at 7:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.