用RecyclerView实现Admob Native Advanced,但无法获得准确的View Types。

问题描述 投票:0回答:1

我已经知道不同类型视图的RecyclerView是如何工作的,但这次我试图将本地高级Admob广告添加到我的RecyclerView中。我按照以下步骤进行Youtube教程 但在应用程序崩溃后,有一个错误打印到我的logcat。

日志猫

 java.lang.ClassCastException: com.google.android.gms.internal.ads.zzaeh cannot be cast to mgb.com.sdalyricsplus.Database.Entities.SongsEntity
        at mgb.com.sdalyricsplus.newAdapters.DisplayItemAdapter.onBindViewHolder(DisplayItemAdapter.java:98)
        at androidx.recyclerview.widget.RecyclerView$Adapter.onBindViewHolder(RecyclerView.java:7065)
        at androidx.recyclerview.widget.RecyclerView$Adapter.bindViewHolder(RecyclerView.java:7107)
        at androidx.recyclerview.widget.RecyclerView$Recycler.tryBindViewHolderByDeadline(RecyclerView.java:6012)
        at androidx.recyclerview.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline(RecyclerView.java:6279)
        at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:6118)
        at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:6114)
        at androidx.recyclerview.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:2303)
        at androidx.recyclerview.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1627)
        at androidx.recyclerview.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1587)

我复习了很多次教程,希望我漏掉了一些导致错误的东西,但似乎我按照教程正确地进行了操作。

以下是我的代码

DisplayItemActivity

public class DisplayItemActivity extends AppCompatActivity{

public static final int NUMBER_OF_AD = 5;
AdLoader adLoader;
FastScrollRecyclerView recyclerView;
Global global;
RoomViewModel model;
List<Object> recyclerViewItems = new ArrayList<>();
List<UnifiedNativeAd> nativeAds = new ArrayList<>();

DisplayItemAdapter adapter;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_songs);
        MobileAds.initialize(this,"ca-app-pub-2493911630710964~1147957926");
        recyclerView = findViewById(R.id.recycler_view);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        recyclerView.setHasFixedSize(true);
        global = (Global)getApplication();
        model = ViewModelProviders.of(this).get(RoomViewModel.class);
        adapter = new DisplayItemAdapter(getLayoutInflater());
        recyclerView.setAdapter(adapter);
        recyclerViewItems.addAll(model.selectAll());

        loadNativeAds();

    }

    private void loadNativeAds() {
        AdLoader.Builder builder = new AdLoader.Builder(this, getResources().getString(R.string.native_advance));

        adLoader = builder.forUnifiedNativeAd(new UnifiedNativeAd.OnUnifiedNativeAdLoadedListener() {
            @Override
            public void onUnifiedNativeAdLoaded(UnifiedNativeAd unifiedNativeAd) {
                nativeAds.add(unifiedNativeAd);
                if (!adLoader.isLoading()) {
                    insertAdToList();
                }
            }
        }).withAdListener(new AdListener() {
            @Override
            public void onAdFailedToLoad(int i) {
                super.onAdFailedToLoad(i);
                if (!adLoader.isLoading()) {
                    insertAdToList();
                }
            }
        }).build();

        adLoader.loadAds(new AdRequest.Builder().build(), NUMBER_OF_AD);
    }

    private void insertAdToList() {
        int offset = recyclerViewItems.size() / (nativeAds.size() + 1);
        int index = 0;

        for (UnifiedNativeAd ad : nativeAds) {
            recyclerViewItems.add(index,ad);
            index = index + offset;
        }

       adapter.setList(recyclerViewItems);
    }

}

而我的适配器

DisplayItemAdapter

public class DisplayItemAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    private final int MENU_ITEM_VIEW_TYPE = 0;
    private final int UNIFIED_NATIVE_AD_VIEW_TYPE = 1;
    private List<Object> recyclerViewItems = new ArrayList<>();
    private Global global;
    private String searchTxt = "";
    private final String newline = System.getProperty("line.separator");
    private ClickSongItemListener clickSongItemListener;
    private ChangeFavoriteListener changeFavoriteListener;
    private LayoutInflater layoutInflater;

    public DisplayItemAdapter(LayoutInflater layoutInflater) {
        this.layoutInflater = layoutInflater;

    }

    public void setList(List<Object> list) {
        this.recyclerViewItems = list;
        notifyDataSetChanged();
    }

    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        global = (Global) ((Activity)parent.getContext()).getApplication();
        switch (viewType) {
            case UNIFIED_NATIVE_AD_VIEW_TYPE:
                View adView = LayoutInflater.from(parent.getContext()).inflate(R.layout.native_ad_view,parent,false);
                return new UnifiedNativeAdViewHolder(adView);
            case MENU_ITEM_VIEW_TYPE :
                default:
                    View songitem = LayoutInflater.from(parent.getContext()).inflate(R.layout.gospel_song_item,parent,false);
                    return new SongItemViewHolder(songitem);
        }
    }

    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
        int viewType = getItemViewType(position);
        switch (viewType) {
            case UNIFIED_NATIVE_AD_VIEW_TYPE :
                UnifiedNativeAdViewHolder nativeAdViewHolder = (UnifiedNativeAdViewHolder)holder;

                UnifiedNativeAd unifiedNativeAd = (UnifiedNativeAd) recyclerViewItems.get(position);
//                    poulateNativeAdView(unifiedNativeAd,((UnifiedNativeAdViewHolder)holder).getAdView());
                FrameLayout frameLayout =
                        nativeAdViewHolder.view.findViewById(R.id.ad_frame_placement);
                UnifiedNativeAdView adView = (UnifiedNativeAdView) layoutInflater
                        .inflate(R.layout.ad_unified, null);
                populateUnifiedNativeAdView(unifiedNativeAd, adView);
                frameLayout.removeAllViews();
                frameLayout.addView(adView);
                break;
            case MENU_ITEM_VIEW_TYPE :
                default:
                SongItemViewHolder songItemViewHolder = (SongItemViewHolder)holder;
                setSongViews(songItemViewHolder, (SongsEntity)recyclerViewItems.get(position));

        }
    }

    private void populateUnifiedNativeAdView(UnifiedNativeAd nativeAd, UnifiedNativeAdView adView) {
        // Set the media view.
        adView.setMediaView((MediaView) adView.findViewById(R.id.ad_media));

        // Set other ad assets.
        adView.setHeadlineView(adView.findViewById(R.id.ad_headline));
        adView.setBodyView(adView.findViewById(R.id.ad_body));
        adView.setCallToActionView(adView.findViewById(R.id.ad_call_to_action));
        adView.setIconView(adView.findViewById(R.id.ad_app_icon));
        adView.setPriceView(adView.findViewById(R.id.ad_price));
        adView.setStarRatingView(adView.findViewById(R.id.ad_stars));
        adView.setStoreView(adView.findViewById(R.id.ad_store));
        adView.setAdvertiserView(adView.findViewById(R.id.ad_advertiser));

        // The headline and mediaContent are guaranteed to be in every UnifiedNativeAd.
        ((TextView) adView.getHeadlineView()).setText(nativeAd.getHeadline());
        adView.getMediaView().setMediaContent(nativeAd.getMediaContent());

        // These assets aren't guaranteed to be in every UnifiedNativeAd, so it's important to
        // check before trying to display them.
        if (nativeAd.getBody() == null) {
            adView.getBodyView().setVisibility(View.INVISIBLE);
        } else {
            adView.getBodyView().setVisibility(View.VISIBLE);
            ((TextView) adView.getBodyView()).setText(nativeAd.getBody());
        }

        if (nativeAd.getCallToAction() == null) {
            adView.getCallToActionView().setVisibility(View.INVISIBLE);
        } else {
            adView.getCallToActionView().setVisibility(View.VISIBLE);
            ((Button) adView.getCallToActionView()).setText(nativeAd.getCallToAction());
        }

        if (nativeAd.getIcon() == null) {
            adView.getIconView().setVisibility(View.GONE);
        } else {
            ((ImageView) adView.getIconView()).setImageDrawable(
                    nativeAd.getIcon().getDrawable());
            adView.getIconView().setVisibility(View.VISIBLE);
        }

        if (nativeAd.getPrice() == null) {
            adView.getPriceView().setVisibility(View.INVISIBLE);
        } else {
            adView.getPriceView().setVisibility(View.VISIBLE);
            ((TextView) adView.getPriceView()).setText(nativeAd.getPrice());
        }

        if (nativeAd.getStore() == null) {
            adView.getStoreView().setVisibility(View.INVISIBLE);
        } else {
            adView.getStoreView().setVisibility(View.VISIBLE);
            ((TextView) adView.getStoreView()).setText(nativeAd.getStore());
        }

        if (nativeAd.getStarRating() == null) {
            adView.getStarRatingView().setVisibility(View.INVISIBLE);
        } else {
            ((RatingBar) adView.getStarRatingView())
                    .setRating(nativeAd.getStarRating().floatValue());
            adView.getStarRatingView().setVisibility(View.VISIBLE);
        }

        if (nativeAd.getAdvertiser() == null) {
            adView.getAdvertiserView().setVisibility(View.INVISIBLE);
        } else {
            ((TextView) adView.getAdvertiserView()).setText(nativeAd.getAdvertiser());
            adView.getAdvertiserView().setVisibility(View.VISIBLE);
        }

        // This method tells the Google Mobile Ads SDK that you have finished populating your
        // native ad view with this native ad.
        adView.setNativeAd(nativeAd);

        // Get the video controller for the ad. One will always be provided, even if the ad doesn't
        // have a video asset.
        VideoController vc = nativeAd.getVideoController();

        // Updates the UI to say whether or not this ad has a video asset.
        if (vc.hasVideoContent()) {
//            videoStatus.setText(String.format(Locale.getDefault(),
//                    "Video status: Ad contains a %.2f:1 video asset.",
//                    vc.getAspectRatio()));

            // Create a new VideoLifecycleCallbacks object and pass it to the VideoController. The
            // VideoController will call methods on this object when events occur in the video
            // lifecycle.
            vc.setVideoLifecycleCallbacks(new VideoController.VideoLifecycleCallbacks() {
                @Override
                public void onVideoEnd() {
                    // Publishers should allow native ads to complete video playback before
                    // refreshing or replacing them with another ad in the same UI location.
                    super.onVideoEnd();
                }
            });
        } else {
        }
    }

    private void setSongViews(SongItemViewHolder viewHolder, SongsEntity note) {
        Context context = viewHolder.itemView.getContext();
        if (note.getMedia_extension().equals("audio")) {
            Glide.with(context)
                    .load(R.drawable.music_icon)
                    .thumbnail(00.1f)
                    .into(viewHolder.iv_thumbnail);
        }else if (note.getGenre().toLowerCase().contains("karaoke")) {
            File file = new File(context.getExternalFilesDir(null)+"/.file"+note.getId());
            if (file.exists()) {
                Glide.with(context)
                        .setDefaultRequestOptions(new RequestOptions().placeholder(R.drawable.karaoke_icon))
                        .load(file)
                        .thumbnail(00.1f)
                        .into(viewHolder.iv_thumbnail);
            }else {
                Glide.with(context)
                        .setDefaultRequestOptions(new RequestOptions().placeholder(R.drawable.karaoke_icon))
                        .load(note.getMedia_url())
                        .thumbnail(00.1f)
                        .into(viewHolder.iv_thumbnail);
            }

        }else {
            Glide.with(context)
                    .load(R.drawable.lyrics_icon)
                    .thumbnail(00.1f)
                    .into(viewHolder.iv_thumbnail);
        }




        viewHolder.title.setText(global.capitalize(note.getTitle()));
        viewHolder.artist.setText(global.capitalize(note.getArtist()));
        viewHolder.category.setText(note.getGenre());
        viewHolder.favorite.setOnCheckedChangeListener(null);
        viewHolder.favorite.setChecked(note.getFavorites());
        viewHolder.views.setText(note.getFavorite_counter() <2 ? note.getFavorite_counter()+" heart" : note.getFavorite_counter()+" hearts");

        String MY_ID = "JUntYdabhUh5XtMhfCIXXwNbsdW2";
        if (!note.getUploader_id().equals(MY_ID))
            Glide.with(context)
                    .setDefaultRequestOptions(new RequestOptions().placeholder(R.mipmap.sda_logo).diskCacheStrategy(DiskCacheStrategy.ALL))
                    .load(note.getUploader_photo_url())
                    .into(viewHolder.user_logo);
        else
            Glide.with(context)
                    .load(R.mipmap.sda_logo)
                    .into(viewHolder.user_logo);

        String lyrics = note.getLyrics().toLowerCase();
        String searchFilter = searchTxt.toLowerCase();

        if (searchTxt.isEmpty()) {
            viewHolder.phrase_end.setText(note.getLyrics());
            viewHolder.phrase.setText("");
        } else
        if (lyrics.contains(searchFilter) && (lyrics.indexOf(searchFilter)) + searchTxt.length() <= lyrics.length()) {
            viewHolder.phrase.setText(searchTxt);

            String filter = note.getLyrics().substring(lyrics.indexOf(searchFilter) + searchFilter.length());
            assert newline != null;
            viewHolder.phrase_end.setText(filter.replaceAll(newline, " "));
        }else {
            viewHolder.phrase_end.setText(note.getLyrics());
            viewHolder.phrase.setText("");
        }


        viewHolder.favorite.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                if (changeFavoriteListener != null) {
                    changeFavoriteListener.onChange(buttonView.getContext(),note,isChecked);
                }
            }
        });
    }

    private void poulateNativeAdView(UnifiedNativeAd unifiedNativeAd, UnifiedNativeAdView adView) {
        ((TextView)adView.getHeadlineView()).setText(unifiedNativeAd.getHeadline());
        ((TextView)adView.getBodyView()).setText(unifiedNativeAd.getBody());
        ((TextView)adView.getCallToActionView()).setText(unifiedNativeAd.getCallToAction());

        NativeAd.Image icon = unifiedNativeAd.getIcon();

        if (icon == null) {
            adView.getIconView().setVisibility(View.INVISIBLE);
        }else {
            ((ImageView)adView.getIconView()).setImageDrawable(icon.getDrawable());
            adView.getIconView().setVisibility(View.VISIBLE);
        }

        if (unifiedNativeAd.getPrice() == null) {
            adView.getPriceView().setVisibility(View.INVISIBLE);
        }else {
            adView.getPriceView().setVisibility(View.VISIBLE);
            ((TextView)adView.getPriceView()).setText(unifiedNativeAd.getPrice());
        }

        if (unifiedNativeAd.getStore() == null) {
            adView.getStoreView().setVisibility(View.INVISIBLE);
        }else {
            adView.getStoreView().setVisibility(View.VISIBLE);
            ((TextView)adView.getStoreView()).setText(unifiedNativeAd.getStore());
        }

        if (unifiedNativeAd.getStarRating() == null) {
            adView.getStarRatingView().setVisibility(View.INVISIBLE);
        }else {
            adView.getStarRatingView().setVisibility(View.VISIBLE);
            ((RatingBar)adView.getStarRatingView()).setRating(unifiedNativeAd.getStarRating().floatValue());
        }

        if (unifiedNativeAd.getAdvertiser() == null) {
            adView.getAdvertiserView().setVisibility(View.INVISIBLE);
        }else {
            adView.getAdvertiserView().setVisibility(View.VISIBLE);
            ((TextView)adView.getAdvertiserView()).setText(unifiedNativeAd.getAdvertiser());
        }

        adView.setNativeAd(unifiedNativeAd);


    }

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


    public class SongItemViewHolder extends RecyclerView.ViewHolder {
        TextView title, phrase,phrase_end;
        TextView artist;
        TextView category;
        ToggleButton favorite;
        LinearLayout layoutWrapper;
        LinearLayout phrase_layout;
        TextView views;
        CircleImageView user_logo;
        ImageView iv_thumbnail;

        public SongItemViewHolder(@NonNull View itemView) {
            super(itemView);
            title = itemView.findViewById(R.id.text_view_title);
            iv_thumbnail = itemView.findViewById(R.id.iv_thumbnail);
            artist = itemView.findViewById(R.id.text_view_artist);
            phrase = itemView.findViewById(R.id.phrase);
            phrase_end = itemView.findViewById(R.id.phrase_end);
            category = itemView.findViewById(R.id.text_view_category);
            favorite = itemView.findViewById(R.id.toggleButton_favorite);
            layoutWrapper = itemView.findViewById(R.id.layout_wrapper);
            phrase_layout = itemView.findViewById(R.id.phrase_layout);
            views = itemView.findViewById(R.id.tv_view_status);
            user_logo = itemView.findViewById(R.id.user_logo);
        }
    }

    private class UnifiedNativeAdViewHolder extends RecyclerView.ViewHolder {

        private View view;

        public UnifiedNativeAdViewHolder(View view) {
            super(view);
            this.view = view;
        }
    }

}

日志上说 com.google.android.gms.internal.ads.zzaeh cannot be cast to mgb.com.sdalyricsplus.Database.Entities.SongsEntity 并将错误指向了viewbindholder。

 case MENU_ITEM_VIEW_TYPE :
                default:
                SongItemViewHolder songItemViewHolder = (SongItemViewHolder)holder;
                setSongViews(songItemViewHolder, (SongsEntity)recyclerViewItems.get(position)); // this line

我怀疑是视图类型。也许是视图类型没有正确分配。

我也试过这个代码,但错误仍然存在。

 @Override
    public int getItemViewType(int position) {
        if (position % DisplayItemActivity.NUMBER_OF_AD == 0) {
            return UNIFIED_NATIVE_AD_VIEW_TYPE;
        }else {
            return MENU_ITEM_VIEW_TYPE;
        }
    }

谁能帮我找到错误的原因?

java android android-recyclerview admob
1个回答
0
投票

你需要合并你的 SongItemUnifiedNativeAd 成为1个单一的数据源。

制作一个新的对象类名 SongAdsData 合并你的 Song 对象和 Ads 从Admob变成1个单一的对象,如下图。

    public class SongAdsData {

        public int getType() {
            return type;
        }

        public UnifiedNativeAd getAds() {
            return ads;
        }

        public Post getSong() {
            return Song;
        }

        public int type; // 1 is ads and 2 is songs
        public UnifiedNativeAd ads;
        public Song song; // here is ur Song object as usual

    }

在你的适配器中,修改如下。

public class SongAdapter  extends RecyclerView.Adapter<RecyclerView.ViewHolder> {  

  private static final int UNIFIED_ADS_VIEW  = 1;

  private static final int SONG_ITEM_VIEW = 2;

  private List<SongAdsData> songAdsDataList;

  //here your onBindViewHolder,here your refer to item of `songAdsDataList`

  // here your onCreateViewHolder

  public void setSongAdsDataList(List<SongAdsData> songAdsDataForRecyclerView) {
    this.songAdsDataList = songAdsDataForRecyclerView;
    notifyDataSetChanged();
  }

  // Here is the function that insert the ads to RecyclerView
  public void insertAdToRecyclerView(List<UnifiedNativeAds> nativeAds) {
    int offset = songAdsDataList.size() / (nativeAds.size() + 1);
    int index = 0;

    for (UnifiedNativeAd ad : nativeAds) {
        SongAdsData adsData = new SongAdsData();
        adsData.song = null;
        adsData.ads = ad;
        adsData.type = 1; //1 for ads,2 for song

        songAdsDataList.add(index,adsData);
        index = index + offset;
    }

     notifyDataSetChanged();
  }

  @Override
  public int getItemViewType (int position) {

      if(songAdsDataList.get(position).getType() == 1){ // So here you compare the type of the object it the position 
        return UNIFIED_ADS;
      }else{
        return SONG_ITEM_VIEW;
      }
  }

  @Override
  public int getItemCount() {
    if(songAdsDataList != null){
        return songAdsDataList.size();
    }else{
        return 0;
    }
  }
}

最后在你的 DisplayItemActivity

我不知道你是如何获得SongItem的(你的数据从服务器或其他地方),但这个想法是转换你的 SongItem 变成 SongAdsData 我们在步骤一中创建的。

像这样的例子。

private void displaySongFromYourServer(List<Songs> songs) {

     List<SongAdsData> songAdsDataList = new ArrayList<>();

     for(Songs song : songs){
        SongAdsData data = new SongAdsData();
        data.ads = null;
        data.song = song;
        data.type = 2;

        songAdsDataList.add(data);
      }

     songAdapter.setSongAdsDataList(songAdsDataList);
}

那么现在,你的 Song 项目将成为 SongAdsData. 同时,你的 UnifiedNativeAd 对象也需要改造成为 SongAdsData所以我们要把里面的东西都搬走 insertAdToList 变成 SongAdapter(见 insertAdToRecyclerView 在上面的歌曲适配器中),所以它可以引用相同的。List 的回收者视图。

因此,在你的 insertAdToListDisplayItemActivity 应该变成这样。

private void insertAdToList() {
    songAdapter.insertAdsToRecyclerView(nativeAds); //called to the function inside songAdapter.
}

希望你能明白我的意思


0
投票

你需要覆盖 getItemViewType(int position) 来检查你的对象的实例,以便返回正确的视图类型。

 @Override
public int getItemViewType(int position) {
    if (this.recyclerViewItems.get(position) instanceof UnifiedNativeAd) {
        return UNIFIED_NATIVE_AD_VIEW_TYPE;
    }else {
        return MENU_ITEM_VIEW_TYPE;
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.