Since the previous experience proved to be quite good, in the case of RecyclerView we decided to implement a wrapper (hereinafter the wrapper) for the adapter. This allows the developer to create his own special and unique data adapter, and we are trying not to interfere with this endeavor. The developer then links the wrapper to the adapter using dependency injection , and in RecyclerView passes the link to the wrapper. Thus, we do not interfere in the logic of the adapter. When displaying a collection, the wrapper calculates where to display the ad unit, and where the source data is displayed. Since the considered part of the library does not carry any elegant solutions and innovative constructions, I would like to elaborate more on the features and differences in the implementation of scrollable native advertising for RecyclerView from ListView . The base adapter class for RecyclerView is RecyclerView.Adapter <~> , and for ListView , the BaseAdapter . If the use of the ViewHolder pattern for the ListView is an action from the category of best-practices , then in the RecyclerView.Adapter <VH extends RecyclerView.ViewHolder> declaration, the ViewHolder type parameter is mandatory. Base class requires redefinition of methods.abstract int getItemCount() returns the total number of items stored by the adapter. In essence, it is similar to the int getCount () method; abstract void onBindViewHolder(VH holder, int position) bindes the view stored in holder to the data collection item with index position . This and the following methods came to "replace" the View getView method (int position, View convertView, ViewGroup parent) from BaseAdapter - earlier, depending on the scan results, convertView to null , or created a new convertView , or bind or the current to the corresponding data; abstract VH onCreateViewHolder(ViewGroup parent, int viewType) creates and returns a viewholder of the desired type viewType . The type of the viewType container should be used in case the elements of your collection need to be displayed in different ways according to a certain attribute. In this case, the method should also be defined. int getItemViewType(int position) returns the container type for the data collection item by index position . The BaseAdapter also needed to determine the total number of container types in the getViewTypeCount method, in RecyclerView.Adapter <~>, this is no longer necessary. In our example, getViewTypeCount returns a wrapper to 3: one type for elements from the original data collection (for the adapter), and two more for advertising with content and application installation advertising (see in the previous article). At the same time, the adapter can define its own logic for displaying container types and the wrapper does not need to know anything about it. So, the source code is better than a thousand words :) public class AdmobRecyclerAdapterWrapper<T, V extends View> extends RecyclerView.Adapter<ViewWrapper<V>> implements AdmobFetcher.AdmobListener { //... @Override public void onBindViewHolder(ViewWrapper<V> viewHolder, int position) { if (viewHolder==null) return; switch (viewHolder.getItemViewType()) { // - case VIEW_TYPE_AD_INSTALL: // viewHolder.getView() (recycling) NativeAppInstallAdView lvi1 = (NativeAppInstallAdView) viewHolder.getView(); // . getItem AdmobFetcher , . NativeAppInstallAd ad1 = (NativeAppInstallAd) getItem(position); // AdViewHelper.bindInstallAdView(lvi1, ad1); break; // - case VIEW_TYPE_AD_CONTENT: NativeContentAdView lvi2 = (NativeContentAdView) viewHolder.getView(); NativeContentAd ad2 = (NativeContentAd) getItem(position); AdViewHelper.bindContentAdView(lvi2, ad2); break; default: // ( ), ( ) int origPos = getOriginalContentPosition(position); // mAdapter.onBindViewHolder(viewHolder, origPos); } } @Override public final ViewWrapper<V> onCreateViewHolder(ViewGroup parent, int viewType) { switch (viewType) { case VIEW_TYPE_AD_INSTALL: case VIEW_TYPE_AD_CONTENT: // viewholder viewType - return new ViewWrapper<V>(onCreateItemView(parent, viewType)); default: // return mAdapter.onCreateViewHolder(parent, viewType); } } // (. ) private V onCreateItemView(ViewGroup parent, int viewType) { switch (viewType) { case VIEW_TYPE_AD_INSTALL: NativeAppInstallAdView lvi1 = getInstallAdView(parent); return (V)lvi1; case VIEW_TYPE_AD_CONTENT: NativeContentAdView lvi2 = getContentAdView(parent); return (V)lvi2; default: return null; } } // , //... } AtomicBoolean lockFetch = new AtomicBoolean(); Before calling loadAd, we check the value of this flag, and if it is set to true — another block is currently being loaded, we exit. Otherwise, set it to true and try to load a block of ads. private synchronized void fetchAd() { Context context = mContext.get(); if (context != null) { if(lockFetch.getAndSet(true)) return; adLoader.loadAd(getAdRequest()); } else { mFetchFailCount++; } } adLoader = new AdLoader.Builder(mContext.get(), admobUnitId) .forAppInstallAd(new NativeAppInstallAd.OnAppInstallAdLoadedListener() { @Override public void onAppInstallAdLoaded(NativeAppInstallAd appInstallAd) { lockFetch.set(false); //... } }) .forContentAd(new NativeContentAd.OnContentAdLoadedListener() { @Override public void onContentAdLoaded(NativeContentAd contentAd) { lockFetch.set(false); //... } }) .withAdListener(new AdListener() { @Override public void onAdFailedToLoad(int errorCode) { lockFetch.set(false); mFetchFailCount++; //... } }).build(); Source: https://habr.com/ru/post/282427/
All Articles