1
votes

I have a fragment that is loaded on a Tab and then loads a RecyclerView and sets an adapter with two ViewHolder's. The data on the screen is displayed on the screen through CardViews.

One card for the top and multiple cards for the rest of the data. A representation of it is as follows:

enter image description here

My first and biggest problem is that it loads fine as you can see on the picture above, but when I try to scroll I get the error:

Application has stopped unexpectedly: Thread[main,5,main] failed: java.lang.NullPointerException: Attempt to invoke virtual method 'boolean android.support.v7.widget.RecyclerView$LayoutManager.canScrollHorizontally()' on a null object reference at android.support.v7.widget.RecyclerView.computeHorizontalScrollOffset(RecyclerView.java:1548) at android.view.View.canScrollHorizontally(View.java:12827) at android.support.v4.view.ViewCompatICS.canScrollHorizontally(ViewCompatICS.java:31) at android.support.v4.view.ViewCompat$ICSViewCompatImpl.canScrollHorizontally(ViewCompat.java:1249) at android.support.v4.view.ViewCompat.canScrollHorizontally(ViewCompat.java:1684) at android.support.v4.view.ViewPager.canScroll(ViewPager.java:2561) at android.support.v4.view.ViewPager.canScroll(ViewPager.java:2552) at android.support.v4.view.ViewPager.canScroll(ViewPager.java:2552) at android.support.v4.view.ViewPager.onInterceptTouchEvent(ViewPager.java:1923) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1961) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2406) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2107) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2406) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2107) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2406) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2107) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2406) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2107) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2406) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2107) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2406) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2107) at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchTouchEvent(PhoneWindow.java:2625) at com.android.internal.policy.impl.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1770) at android.app.Activity.dispatchTouchEvent(Activity.java:2742) at android.support.v7.view.WindowCallbackWrapper.dispatchTouchEvent(WindowCallbackWrapper.java:60) at android.support.v7.view.WindowCallbackWrapper.dispatchTouchEvent(WindowCallbackWrapper.java:60) at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.java:2586) at android.view.View.dispatchPointerEvent(View.java:8675) at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:4129) at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:3995) at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3550) at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3603) at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3569) at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:3686) at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3577) at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:3743) at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3550) at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3603) at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3569) at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3577) at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3550) at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:5813) at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:5787) at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:5758) at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:5903) at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:185) at android.view.InputEventReceiver.nativeConsumeBatchedInputEvents(Native Method) at android.view.InputEventReceiver.consumeBatchedInputEvents(InputEventReceiver.java:176) at android.view.ViewRootImpl.doConsumeBatchedInput(ViewRootImpl.java:5874) at android.view.ViewRootImpl$ConsumeBatchedInputRunnable.run(ViewRootImpl.java:5926) at android.view.Choreographer$CallbackRecord.run(Choreographer.java:767) at android.view.Choreographer.doCallbacks(Choreographer.java:580) at android.view.Choreographer.doFrame(Choreographer.java:548) at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:753) at android.os.Handler.handleCallback(Handler.java:739) at android.os.Handler.dispatchMessage(Handler.java:95) at android.os.Looper.loop(Looper.java:135) at android.app.ActivityThread.main(ActivityThread.java:5294) at java.lang.reflect.Method.invoke(Native Method) at java.lang.reflect.Method.invoke(Method.java:372) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:904) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:699)

The second thing I wanted to ask is whether you think this is the right way to implement this. Should i be using multiple view holders inside a RecyclerView? Will that affect performance?

My code is as follows:

HomeFragment.java

public class HomeFragment extends Fragment{
    private List<Favourite> favourites;
    private RecyclerView rv;
    FavouriteAdapter fa;

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

        // Get recycler view reference
        rv = (RecyclerView) view.findViewById(R.id.favourite_card_rv);

        // Set layout manager
        rv.setLayoutManager(new LinearLayoutManager(getActivity()));

        // some data
        // This only returns a list of objects with some data
        initializeData();

        // Create adapter
        fa = new FavouriteAdapter(getActivity(), favourites);

        // Set adapter
        rv.setAdapter(fa);


        // return
        return view;
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
    }
}

FavouriteAdapter.java

public class FavouriteAdapter extends RecyclerView.Adapter<FavouriteAdapter.MainViewHolder> {

    List<Favourite> favourites;
    Context context;
    final int VIEW_TYPE_TOP = 0;
    final int VIEW_TYPE_FAVOURITES = 1;

    // Constructor
    public FavouriteAdapter(Context context, List<Favourite> favourites) {
        if (favourites == null) {
            throw new IllegalArgumentException(
                    "favourites must not be null");
        }
        this.favourites = favourites;
        this.context = context;
    }

    static class MainViewHolder extends  RecyclerView.ViewHolder {
        CardView cv;
        TextView videoName;
        TextView videoUrl;
        ImageView videoImage;

        public MainViewHolder(View v) {
            super(v);

            cv = (CardView)itemView.findViewById(R.id.cv);
            videoName = (TextView)itemView.findViewById(R.id.video_name);
            videoUrl = (TextView)itemView.findViewById(R.id.video_url);
            videoImage = (ImageView)itemView.findViewById(R.id.video_photo);
        }
    }

    static class TopFavouriteViewHolder extends MainViewHolder{
        TopFavouriteViewHolder(View itemView) {
            super(itemView);
        }
    }

    static class FavouriteViewHolder extends MainViewHolder {
        FavouriteViewHolder(View itemView) {
            super(itemView);
        }
    }


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

    @Override
    public int getItemViewType(int position) {
        return (position == 0? VIEW_TYPE_TOP : VIEW_TYPE_FAVOURITES);
    }

    @Override
    public MainViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
        View view;
        switch(viewType){
            case 0:
                view = LayoutInflater
                        .from(viewGroup.getContext())
                        .inflate(R.layout.top_card, viewGroup, false);
                return new TopFavouriteViewHolder(view);
            case 1:
                view = LayoutInflater
                        .from(viewGroup.getContext())
                        .inflate(R.layout.favourite_card, viewGroup, false);
                return new FavouriteViewHolder(view);
        }
        return null;
    }

    @Override
    public void onBindViewHolder(MainViewHolder holder, int i) {
        holder.videoName.setText(favourites.get(i).name);
        holder.videoUrl.setText(favourites.get(i).url);
        Picasso.with(this.context).load(favourites.get(i).image).into(holder.videoImage);
    }

    @Override
    public void onAttachedToRecyclerView(RecyclerView recyclerView) {
        super.onAttachedToRecyclerView(recyclerView);
    }
}

I've looked at many threads here and tried lots of different combinations, but can't seem to be able to get past this problem.

Any help much appreciated.

Thanks

1
Is it possible you have multiple RecyclerViews in your view hierarchy? Looks like there is one that isn't getting a layout manager set on it.Kevin Coppock
Wow! You're a genius @kcoppock. That was exactly it. In the beginning I though I would need two RecyclerView, but then realised all I needed was a couple of ViewHolders. Totally forgot that RecyclerView there. Thank you so much!Marcos Placona
How did you work that out just from the log?Marcos Placona
I'll add it to an answer. :)Kevin Coppock

1 Answers

3
votes

So it appears you have an extra RecyclerView somewhere in your hierarchy that doesn't have a LayoutManager set on it.

If you look at the top of the stack trace:

Application has stopped unexpectedly: Thread[main,5,main] failed: 
    java.lang.NullPointerException: Attempt to invoke virtual method 
    'boolean android.support.v7.widget.RecyclerView$LayoutManager.canScrollHorizontally()' 
    on a null object reference 
at android.support.v7.widget.RecyclerView.computeHorizontalScrollOffset(RecyclerView.java:1548) 

It indicates that it crashed due to a NullPointerException when calling canScrollHorizontally() on a RecyclerView$LayoutManager instance.