2

I am trying to make dynamical list using RecyclerView and CardView using tutorial from this site and I am getting this error:

java.lang.NullPointerException: Attempt to invoke interface method 'int java.util.List.size()' on a null object reference
        at kawi15.myapplication.CustomAdapter.getItemCount(CustomAdapter.java:66) 

I run debugger to check that I have results from tmbd API and this is what I received: http://prntscr.com/vdjj1h

This is my Fragment class activity when I want to create that list:


public class FragmentOne extends Fragment {

    private static RecyclerView.Adapter adapter;
    private RecyclerView.LayoutManager layoutManager;
    private static RecyclerView recyclerView;
    private static List<MovieDb> data;
    static View.OnClickListener myOnClickListener;

    public static FragmentOne newInstance() {
        FragmentOne fragment = new FragmentOne();
        return fragment;
    }

    public class MovieTask extends AsyncTask<Void, Void, List<MovieDb>> {
        @Override
        protected List<MovieDb> doInBackground(Void... voids) {
            MovieResultsPage movies = new TmdbApi("f753872c7aa5c000e0f46a4ea6fc49b2").getMovies().getUpcoming("en-US", 1, "US");
            List<MovieDb> listMovies = movies.getResults();

            return listMovies;
        }

        @Override
        protected void onPostExecute(List<MovieDb> movieDb) {
            data = movieDb;
        }
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        MovieTask mt = new MovieTask();
        mt.execute();
        super.onCreate(savedInstanceState);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View returnView = inflater.inflate(R.layout.fragment_one, container, false);
        recyclerView = (RecyclerView) returnView.findViewById(R.id.my_recycler_view);
        recyclerView.setHasFixedSize(true);

        layoutManager = new LinearLayoutManager(getContext());  // ???
        recyclerView.setLayoutManager(layoutManager);
        recyclerView.setItemAnimator(new DefaultItemAnimator());



        adapter = new CustomAdapter(data);
        recyclerView.setAdapter(adapter);



        return returnView;
    }
}

and my Adapter class:

public class CustomAdapter extends RecyclerView.Adapter<CustomAdapter.MyViewHolder> {

    private List<MovieDb> dataSet;

    public static class MyViewHolder extends RecyclerView.ViewHolder {

        TextView textViewName;
        TextView textViewVersion;
        ImageView imageViewIcon;

        public MyViewHolder(View itemView) {
            super(itemView);
            this.textViewName = (TextView) itemView.findViewById(R.id.text1);
            this.textViewVersion = (TextView) itemView.findViewById(R.id.text2);
            this.imageViewIcon = (ImageView) itemView.findViewById(R.id.imageView);
        }
    }

    public CustomAdapter(List<MovieDb> data) {
        this.dataSet = data;
    }

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

        //view.setOnClickListener(MainActivity.myOnClickListener);

        MyViewHolder myViewHolder = new MyViewHolder(view);
        return myViewHolder;
    }

    @Override
    public void onBindViewHolder(final MyViewHolder holder, final int listPosition) {

        TextView textViewName = holder.textViewName;
        TextView textViewVersion = holder.textViewVersion;
        ImageView imageView = holder.imageViewIcon;
        Glide.with(imageView).load("http://image.tmdb.org/t/p/w500" + dataSet.get(listPosition).getPosterPath()).into(imageView);

        textViewName.setText(dataSet.get(listPosition).getOriginalTitle());
        textViewVersion.setText(dataSet.get(listPosition).getReleaseDate());
        //imageView.setImageResource(dataSet.get(listPosition).);
    }

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

I stucked with my code with this and I dont have idea where is mistake.

Zain
  • 37,492
  • 7
  • 60
  • 84
  • Does this answer your question? [What is a NullPointerException, and how do I fix it?](https://stackoverflow.com/questions/218384/what-is-a-nullpointerexception-and-how-do-i-fix-it) – OrangeDog Nov 04 '20 at 19:52
  • No, I know that list is null, but I dont know why and how to fix it. I think its something with Adapter or Fragment, some code in wrong place maybe – Karol Wiśniewski Nov 04 '20 at 19:56
  • I think adapter = new CustomAdapter(data); this data is null. Try rechecking how to get this data correctly. – private static Nov 04 '20 at 20:31
  • Try retrieving your data like this: MovieResultsPage movies = new TmdbApi("f753872c7aa5c000e0f46a4ea6fc49b2").getMovies().getUpcoming("en-US", 1, "US"); List listMovies = movies.getResults(); adapter = new CustomAdapter ( listMovies ); – private static Nov 04 '20 at 20:36

2 Answers2

2

Initially dataSet is null. So using dataSet.size() is throwing NullPointerException. Change getItemCount method in the following way.

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

This is removing error but its still doesnt work because data is null making dataSet null too. I dont know why data is null, I need help with this.

Reason for that could be, you are creating CustomAdapter object in onCreateView method, which is kind of called before you AsyncTask has finished. So, when onCreateView is being called, data is null.

To fix it, You can add a setter method in your CustomAdapter, which will allow you to update dataSet, even after the adapter object is created. Then you will have to update the dataSet in your onPostExecute method & after that use notifyDataSetChanged on the adapter, to reflect the changes.

  • This is removing error but its still doesnt work because `data` is null making `dataSet` null too. I dont know why `data` is null, I need help with this. – Karol Wiśniewski Nov 04 '20 at 20:12
  • 1
    @KarolWiśniewski Reason for that could be, you are creating CustomAdapter object in `onCreateView` method, which is kind of called before you AsyncTask has finished. So, when `onCreateView` is being called, `data` is `null`. – Ekamjit Singh Nov 04 '20 at 20:26
  • How to fix that? – Karol Wiśniewski Nov 04 '20 at 20:28
  • 1
    You can add a setter method in your CustomAdapter, which will allow you to update dataSet, even after the object is created. Then you will have to update the dataSet in your `onPostExecute` method & after that use `notifyDataSetChanged` on the adapter, to reflect the changes. – Ekamjit Singh Nov 04 '20 at 20:39
  • It worked. I have one more question. Can I made this Cards clickable and open dialog onClick? – Karol Wiśniewski Nov 04 '20 at 20:41
  • 1
    Great. As it worked, I'm adding the fix in answer itself. Do mark it as accepted if it solve the scope of problem here. As for clickable thing, I'm not sure about how to do that. Maybe someone can help. – Ekamjit Singh Nov 04 '20 at 20:47
1

At the start dataSet will be null until you assign it a value, and then feed it back with data, so change getItemCount() to below to return 0 data when it's null

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

Ok, there is no error and application is launching but still I dont have list and this is my problem

You need to update the changes to your adapter with notifyDataSetChanged()

    @Override
    protected void onPostExecute(List<MovieDb> movieDb) {
        data = movieDb;
        adapter.notifyDataSetChanged();
    }

Now two possible things, either TmdbApi("...").getMovies() doesn't really return data, in this case you need to debug it and check if it returns data or not.

The other thing, move the code of the AsyncTask execution after creating the RecyclerView instance in onCreateView and Initialize the adapter in onPostExecute();

Here are after the changes:

public class MovieTask extends AsyncTask<Void, Void, List<MovieDb>> {
    @Override
    protected List<MovieDb> doInBackground(Void... voids) {
        MovieResultsPage movies = new TmdbApi("f753872c7aa5c000e0f46a4ea6fc49b2").getMovies().getUpcoming("en-US", 1, "US");
        List<MovieDb> listMovies = movies.getResults();

        return listMovies;
    }

    @Override
    protected void onPostExecute(List<MovieDb> movieDb) {

        data = movieDb;
        adapter = new CustomAdapter(data);
        recyclerView.setAdapter(adapter);
    }
}

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

    recyclerView = (RecyclerView) returnView.findViewById(R.id.my_recycler_view);
    recyclerView.setHasFixedSize(true);

    layoutManager = new LinearLayoutManager(getContext());  // ???
    recyclerView.setLayoutManager(layoutManager);
    recyclerView.setItemAnimator(new DefaultItemAnimator());

    MovieTask mt = new MovieTask();
    mt.execute();

    return returnView;
}
Zain
  • 37,492
  • 7
  • 60
  • 84
  • 1
    It works now, thanks, I want to ask one more question. Can I made this Cards clickable and open dialog onClick? – Karol Wiśniewski Nov 04 '20 at 20:40
  • 1
    Of course you can `setOnClickListener` to the ViewHolder `itemView`, and Show a dialog or a dialog fragment as you want but first give it a try happy coding :) – Zain Nov 04 '20 at 20:42
  • If this answers your questions, please mark it as accepted answer .. thanks – Zain Nov 04 '20 at 20:49
  • Hmm, can I get information from item I clicked? I think about it and I dont know how to do this. I want to get whole item from list. Its possible? – Karol Wiśniewski Nov 10 '20 at 15:33
  • Yes there is a method getAdapterPosition# in the ViewHolder which returns the current position of the item... Just implement the OnClickListener and call it – Zain Nov 10 '20 at 16:05
  • I implement OnClickListener but I still dont understand how it works, can I have chat with u? – Karol Wiśniewski Nov 10 '20 at 16:24
  • Im trying something like this ```@Override public void onClick(View v) { int pos; pos = adapter.getItemId() movieDb = data.get(pos);``` but I dont know how to get adapter position, I cant use this method here. – Karol Wiśniewski Nov 10 '20 at 16:38
  • 1
    Sure plz give me some time i have a severe cold – Zain Nov 10 '20 at 18:21
  • Ok, I make some progress but still have problem, so if u will be ready open chat. – Karol Wiśniewski Nov 10 '20 at 18:39
  • 1
    I achieved what I want with this: https://stackoverflow.com/questions/64773711/how-to-get-item-from-recycler-view/64775468?noredirect=1#comment114530217_64775468 It's finally working, its different what I were trying. https://www.avinsharma.com/android-basics-recyclerview-II/ First I follow this, but have little problem with constructor but its not important now. Still thanks for ur readiness to help. – Karol Wiśniewski Nov 10 '20 at 21:15