How to get correct item position after native ads are added to recycler view

android
fabric

#1

In my android app I’m trying to implement mopub native ads. Before using these native ads my app was functioning correctly as expected. Now it gives outofbound exceptions and incorrect items when list item is clicked. I’m using recyclerview and using infinite scrolling, it gets item from my api and displays item correctly, but when clicking on item it gives incorrect item.

Here is my recyclerview adapter below.

public class PostRecycleAdapter extends RecyclerView.Adapter<com.example.adapters.PostRecycleAdapter.PostViewHolder> implements Callback<List<Post>> {

    private Context mContext;
    private List<Post> mPosts;
    // Allows to remember the last item shown on screen
    private int lastPosition = -1;
    //    Pagination
    private Pagination mPagination;

    public PostRecycleAdapter(Context context, List<Post> posts) {
        mContext = context;
        mPosts = posts;
    }

    @Override
    public PostViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.list_item, parent, false);

        PostViewHolder viewHolder = new PostViewHolder(view);
        return viewHolder;
    }

    @Override
    public void onBindViewHolder(PostViewHolder holder, int position) {
        holder.bindPost(mPosts.get(position));
        // Here you apply the animation when the view is bound
        setAnimation(holder.mPostName, position);

        //check for last item
        if ((position >= getItemCount() - 1)) {
            // Loading next set of list items on scroll
            if (mPagination != null && !mPagination.getOutOfRange() && mPagination.getNextPage() != null){
                MyApplication.bus.post(new LoadMoreFeed(mPagination));
            }
        }
    }

    @Override
    public int getItemCount() {
        return mPosts == null ? 0 : mPosts.size();
    }

    /**
     * Here is the key method to apply the animation
     */
    private void setAnimation(View viewToAnimate, int position)
    {
        // If the bound view wasn't previously displayed on screen, it's animated
        if (position > lastPosition)
        {
            Animation animation = AnimationUtils.loadAnimation(mContext, android.R.anim.slide_in_left);
            viewToAnimate.startAnimation(animation);
            lastPosition = position;
        }
    }

    public class PostViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {

        public TextView mPostName;
        public ImageView mAnchorImage;

        public PostViewHolder(View itemView) {
            super(itemView);

            mPostName = (TextView) itemView.findViewById(R.id.postName);
            mAnchorImage = (ImageView) itemView.findViewById(R.id.anchorImage);

            itemView.setOnClickListener(this);
        }

        public void bindPost(Post post) {
            mPostName.setText(post.getName());

            String postImage = post.getPostImage();
            Picasso.with(mContext)
                    .load(postImage)
                    .placeholder(R.drawable.avatar_empty)
                    .into(mAnchorImage);
        }

        @Override
        public void onClick(View view) {
            int position = getAdapterPosition(); // gets item position
            Post post = mPosts.get(position);
            int postId = post.getId();
            String postName = post.getName();

            Intent intent = new Intent(mContext, InfoActivity.class);
            intent.putExtra(MyAppConstants.POST_ID, postId);
            intent.putExtra(MyAppConstants.POST_NAME, postName);
            mContext.startActivity(intent);
        }
    }

    @Override
    public void success(List<Post> posts, Response response) {
        if (mPosts == null) {
            mPosts = posts;
        } else {
            mPosts.addAll(posts);
        }

        notifyDataSetChanged();

        List<Header> headerList = response.getHeaders();
        for(Header header : headerList) {
            if ("X-Pagination".equals(header.getName())) {
                Gson gson = new Gson();
                mPagination = gson.fromJson(header.getValue(), Pagination.class);
            }
        }

        MyApplication.bus.post(new FeedLoaded(mPagination));
    }

    @Override
    public void failure(RetrofitError error) {
        Log.d("Call", " : Failed => " + error);
        MyApplication.bus.post(new ErrorLoadingFeed(error));
    }

    public void clearData() {
        if (mPosts != null) {
            mPosts.clear();
            notifyDataSetChanged();
        }
    }

    public List<Post> getPosts() {
        return mPosts;
    }
}

and here is how I’m requesting ads in my app.

@Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.fragment_feed,
                container, false);

        ButterKnife.bind(this, rootView);

        mSwipeRefreshLayout.setOnRefreshListener(mOnRefreshListener);
        mSwipeRefreshLayout.setColorSchemeResources(R.color.cpb_default_color);

        // RecyclerView
        LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity());
        mRecyclerView.setLayoutManager(layoutManager);
        mRecyclerView.addItemDecoration(new DividerItemDecoration(getActivity()));

        adapter = new PostRecycleAdapter(getActivity(), null);

        // Mopub
        // Create an ad renderer and view binder that describe your native ad layout.
        ViewBinder myViewBinder = new ViewBinder.Builder(R.layout.native_ad_list)
                .titleId(R.id.native_title)
                .iconImageId(R.id.native_icon_image)
                .privacyInformationIconImageId(R.id.native_privacy_information_icon_image)
                .callToActionId(R.id.native_cta)
                .build();

        MoPubNativeAdPositioning.MoPubServerPositioning adPositioning = MoPubNativeAdPositioning.serverPositioning();

        MoPubStaticNativeAdRenderer myRenderer = new MoPubStaticNativeAdRenderer(myViewBinder);

        // Pass the recycler Adapter your original adapter.
        myMoPubAdapter = new MoPubRecyclerAdapter(getActivity(), adapter, adPositioning);

        myMoPubAdapter.registerAdRenderer(myRenderer);

        // Set up the recycler view
        mRecyclerView.setAdapter(myMoPubAdapter);

        return rootView;
    }

and onResume

@Override
    public void onResume() {
        super.onResume();

        MyApplication.bus.register(this);

        mSwipeRefreshLayout.post(new Runnable() {
            @Override
            public void run() {
                adapter.clearData();
                mSwipeRefreshLayout.setRefreshing(true);
                getDataGracefullyApi(mLoadPage);
            }
        });

        // Mopub ads

        // Optional targeting parameters
        RequestParameters parameters = new RequestParameters.Builder()
                //.keywords("your target words here")
                .build();

        // Request ads when the user returns to this activity
        myMoPubAdapter.loadAds(getActivity().getString(R.string.mopub_native_ad_unit_id_feed), parameters);
    }

Please help me solve this issue.

Thanks.


#2

Thank you for reaching out. Do you still continue to see this issue with the latest SDK? If so please let us know so we can debug this issue further