Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

Android

Techzim Admin
Techzim Admin
16,378 Points

Please Help me with this RecyclerView

I have created a TabLayout that hosts Fragments, i use the TabLayout with viewPager, which is inside MainActivity. I download data from a news site, which i pass as a Parcelable Array in bundle from the MainActivity, via viewPager and onto the fragments. The Fragments display different categories of the news. I have created methods to sort the data and pass it already sorted to the RecyclerView. The data that i pass to the RecyclerView via Fragment is received correctly in the RecyclerView. I used the debugger and my array elements are all there. My problem is that when the categorized data is inside the RecyclerView, somehow the array size gets multiplied to twice its size. However the extra array elements are null, causing my adapter to crash with a null Pointer Exception when i scroll. When i checked using the debugger the length of the array at getItemCount() method was twice the original array length. I should stress that the problm is not my Sorting method or Fragment or passing of data from one method to another, the problem is in RECYCLERVIEW, which is multiplying my array elements somehow although it has received the right data.

I had thought that it might be that the Fragments are being created multiple times hence they pass the categorized data twice, but that doesn't not explain the null elements in the array.

Here is an example of my business Fragment, together with adapter and sorting class

public class FeedsFragment extends Fragment {

Latest[] Latest;
Parcelable[] mParcelables;

private static Bundle mBundle;

//callback interface for onItemClick listener
public interface OnStorySelected {
    void onStorySelected(int position, String summary, String title, String url, String datePosted);
}

public FeedsFragment() {
    //required empty public constructor
}

//called by viewpager adapter to setup the fragment
public static FeedsFragment newInstance(int position, String name, Bundle bundle){
    FeedsFragment feedsFragment = new FeedsFragment();
     mBundle = bundle ;
    return feedsFragment;
}

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {

    View view = inflater.inflate(R.layout.feeds_fragment, container, false);
    OnStorySelected listener = (OnStorySelected) getActivity();

    //my unCategorized array comes from the hosting activity via bundle
        mParcelables = mBundle.getParcelableArray("news");
    if (mParcelables != null) {
        Latest = Arrays.copyOf(mParcelables, mParcelables.length, Latest[].class);
    }else{
        Latest = null;
    }
    //here i call the sorting class to take care of the Categorizing
    SortingClass.CategorizingMethod(Latest);
   Latest[] testValue = SortingClass.getCategorizedArray(); // my testValue here is correct,
                                                            // 7 array elements, no null array elements here

    //access the recycler view in layout
   RecyclerView mRecyclerView = (RecyclerView) view.findViewById(R.id.recyclerView);
    BusinessAdapter businessAdapter = new BusinessAdapter(testValue, getContext()); //create instance of adapter and pass it the
                                                                                    //categorized array and the context
    mRecyclerView.setAdapter(businessAdapter);                                      //pass adapter to recyclerView
    mRecyclerView.setLayoutManager(new LinearLayoutManager(view.getContext()));     //set layoutManager

    return view;
}

}

public class BusinessAdapter extends RecyclerView.Adapter<BusinessAdapter.BusinessViewHolder> {

private Context mContext;
private Latest[] mCategorized;

public BusinessAdapter(Latest[] latest, Context context) {
    mContext = context;
    mCategorized = latest; // at this point during debugging i find that the values are exactly as they
                            //should be i.e 7 array elements from the Feeds Fragment.
}

@Override
public BusinessViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.news_list_item, parent, false);
    return new BusinessViewHolder(view);
}

@Override
public void onBindViewHolder(BusinessViewHolder holder, int position) {
    holder.bind(mCategorized[position]);
}

@Override
public int getItemCount() {
    return mCategorized.length; // at this point however, the array now has 14 elements, twice the original size
                                //and the other 7 elements are null, they contain nothing whatsoever
                                //and this causes my adapter to crash when i scroll down to those null elements
                                // I can't find a reason why the array elements are expanding to double the size
}

public class BusinessViewHolder extends RecyclerView.ViewHolder {
    TextView mTitle;
    TextView mSummary;
    ImageView mStoryImage;

    public BusinessViewHolder(View itemView) {
        super(itemView);
        mTitle = (TextView) itemView.findViewById(R.id.titleTextView);
        mSummary = (TextView) itemView.findViewById(R.id.summaryTextView);
        mStoryImage = (ImageView) itemView.findViewById(R.id.storyImageView);
    }


    void bind(Latest latest) {
        mTitle.setText(latest.getName());
        mSummary.setText(latest.getDescription());
        Picasso.with(mContext).setIndicatorsEnabled(true);
        String uri = latest.getImage();
        Picasso.with(mContext)
                .load(uri)
                .placeholder(R.drawable.image_placeholder)
                .error(R.drawable.drone)
                .fit()
                .into(mStoryImage);
    }


}

}

public class SortingClass { private static int mCategorizedCounter = 1;

private static Latest[] returnedArray;

public static Latest[] getCategorizedArray() {
    return returnedArray;
}

private static void setCategorizedArray(Latest[] returnedArray) {
    SortingClass.returnedArray = returnedArray;
}

public static void CategorizingMethod(Latest[] latest) {

    //takes all the Latest news and cycles through to find only the Business news
    int[] arrayPositionIndexer = new int[latest.length];
    Latest[] temporaryCategorized = new Latest[latest.length];
    for (int i = 0; i < latest.length; i++) {
        char tag = latest[i].getCategory().charAt(0);
        switch (tag) {
            case 'B':
                arrayPositionIndexer[i] = i;
                temporaryCategorized[i] = latest[i];
                mCategorizedCounter++;
        }
    }
    //takes the Business news and removes any null arrays
        int x = 0;
    Latest[] categorized = new Latest[mCategorizedCounter - 1];
    for (int j = 0; j < latest.length; j++) {
        if (arrayPositionIndexer[j] != 0) {
            categorized[x] = temporaryCategorized[arrayPositionIndexer[j]];
            x++;
        }
    }

    setCategorizedArray(categorized);
}

}

1 Answer

Seth Kroger
Seth Kroger
56,413 Points

The problem is that the SortingClass holds on to the manipulated array and item count as static variables. In particular, mCategorizedCounter is never reset between sorting, so if the your CategorizingMethod is called a second time (say on switching the tab, or even an orientation change) on the same data, the count will be double creating a double-sized result array.

Ideally, the counter shouldn't even be a field, but a local variable since it should start fresh with every call. Also, unless there is some overriding reason to store the results statically it's probably better to return the results directly from CategorizingMethod. (Because objects and arrays are stored by reference, other parts of the app can change the array contents out from under the RecyclerView/Adapter without it knowing, causing issues.)

Techzim Admin
Techzim Admin
16,378 Points

Thanks a lot. Posted this problem on SO and everyone was quick to point out i wasn't taking care of the possibility of there being null array elements in the RecyclerView. My argument that there was never supposed to be any null elements in the first place fell on deaf ears. Thanks again. Your advice worked wonders, program's now running well.