-1

I am using Retrofit library to parse the Json reponse of TMDB API of now Playing movies Category and want to display the results in RecyclerView.

Th raw Json which I am getting from the API is

{
  "page": 1,
  "results": [
    {
      "poster_path": "\/tWqifoYuwLETmmasnGHO7xBjEtt.jpg",
      "adult": false,
      "overview": "A live-action adaptation of Disney's version of the classic 'Beauty and the Beast' tale of a cursed prince and a beautiful young woman who helps him break the spell.",
      "release_date": "2017-03-16",
      "genre_ids": [
        14,
        10749
      ],
      "id": 321612,
      "original_title": "Beauty and the Beast",
      "original_language": "en",
      "title": "Beauty and the Beast",
      "backdrop_path": "\/6aUWe0GSl69wMTSWWexsorMIvwU.jpg",
      "popularity": 147.254531,
      "vote_count": 1938,
      "video": false,
      "vote_average": 6.9
    },
    {
      "poster_path": "\/unPB1iyEeTBcKiLg8W083rlViFH.jpg",
      "adult": false,
      "overview": "A story about how a new baby's arrival impacts a family, told from the point of view of a delightfully unreliable narrator, a wildly imaginative 7 year old named Tim.",
      "release_date": "2017-03-23",
      "genre_ids": [
        16,
        35,
        10751
      ],
      "id": 295693,
      "original_title": "The Boss Baby",
      "original_language": "en",
      "title": "The Boss Baby",
      "backdrop_path": "\/bTFeSwh07oX99ofpDI4O2WkiFJ.jpg",
      "popularity": 138.856227,
      "vote_count": 649,
      "video": false,
      "vote_average": 5.7
    },

The model class which I have created is Movie.java

package com.example.vikas.movie.model;

/**
 * Created by vikas on 22/4/17
 */
import com.google.gson.annotations.SerializedName;

import java.util.ArrayList;
import java.util.List;
public class Movie
{
    @SerializedName("poster_path")
    private String posterPath;
    @SerializedName("adult")
    private boolean adult;
    @SerializedName("overview")
    private String overview;
    @SerializedName("realease_date")
    private String releaseDate;
    @SerializedName("genre_ids")
    private List<Integer> genreIds=new ArrayList<Integer>();
    @SerializedName("id")
    private int id;
    @SerializedName("original_title")
    private String originalTitle;
    @SerializedName("original_language")
    private String original_language;
    @SerializedName("title")
    private String title;
    @SerializedName("backdrop_path")
    private String backdrop_path;
    @SerializedName("popularity")
    private Double popularity;
    @SerializedName("vote_count")
    private int vote_count;
    @SerializedName("vedeo")
    private boolean vedeo;
    @SerializedName("vote_average")
    private Double vote_average;


    public Movie(String posterPath, boolean adult, String overview, String releaseDate, List<Integer> genreIds, Integer id,
                 String originalTitle, String original_language, String title, String backdrop_path, Double popularity,
                 Integer vote_count, Boolean vedeo, Double vote_average) {
        this.posterPath = posterPath;
        this.adult = adult;
        this.overview = overview;
        this.releaseDate = releaseDate;
        this.genreIds = genreIds;
        this.id = id;
        this.originalTitle = originalTitle;
        this.original_language = original_language;
        this.title = title;
        this.backdrop_path = backdrop_path;
        this.popularity = popularity;
        this.vote_count = vote_count;
        this.vedeo = vedeo;
        this.vote_average = vote_average;
    }

    public String getPosterPath() {
        return posterPath;
    }

    public void setPosterPath(String posterPath) {
        this.posterPath = posterPath;
    }

    public boolean isAdult() {
        return adult;
    }

    public void setAdult(boolean adult) {
        this.adult = adult;
    }

    public String getOverview() {
        return overview;
    }

    public void setOverview(String overview) {
        this.overview = overview;
    }

    public String getReleaseDate() {
        return releaseDate;
    }

    public void setReleaseDate(String releaseDate) {
        this.releaseDate = releaseDate;
    }

    public List<Integer> getGenreIds() {
        return genreIds;
    }

    public void setGenreIds(List<Integer> genreIds) {
        this.genreIds = genreIds;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getOriginalTitle() {
        return originalTitle;
    }

    public void setOriginalTitle(String originalTitle) {
        this.originalTitle = originalTitle;
    }

    public String getOriginalLanguage() {
        return original_language;
    }

    public void setOriginalLanguage(String original_language) {
        this.original_language = original_language;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getBackdropPath() {
        return backdrop_path;
    }

    public void setBackdropPath(String backdropPath) {
        this.backdrop_path = backdropPath;
    }

    public Double getPopularity() {
        return popularity;
    }

    public void setPopularity(Double popularity) {
        this.popularity = popularity;
    }

    public Integer getvote_count() {
        return vote_count;
    }

    public void setVoteCount(Integer voteCount) {
        this.vote_count = voteCount;
    }

    public Boolean getVedeo() {
        return vedeo;
    }

    public void setVedeo(Boolean vedeo) {
        this.vedeo = vedeo;
    }

    public Double getvote_average() {
        return vote_average;
    }

    public void setVoteAverage(Double vote_average) {
        this.vote_average = vote_average;
    }
}

MovieResponse.java

package com.example.vikas.movie.model;

/**
 * Created by vikas on 22/4/17.
 */
import com.google.gson.annotations.SerializedName;
import java.util.List;
public class MoviesResponse
{
    @SerializedName("page")
    private int page;
    @SerializedName("results")
    private List<Movie>results;
    @SerializedName("total_results")
    private int totalResults;
    @SerializedName("total_pages")
    private int totalPages;
    public int getPage() {
        return page;
    }

    public void setPage(int page) {
        this.page = page;
    }

    public List<Movie> getResults() {
        return results;
    }

    public void setResults(List<Movie> results) {
        this.results = results;
    }

    public int getTotalResults() {
        return totalResults;
    }

    public void setTotalResults(int totalResults) {
        this.totalResults = totalResults;
    }

    public int getTotalPages() {
        return totalPages;
    }

    public void setTotalPages(int totalPages) {
        this.totalPages = totalPages;
    }




}

Now making the API call so below are the classes which I have written to generate URL and to create an instance of retrofit.These classes I am using to generate the URL and making a call to the API.

ApiClient.java package com.example.vikas.movie.rest;

/**
 * Created by vikas on 22/4/17.
 */
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
public class ApiClient
{
    public static final String URL="http://api.themoviedb.org/3/";
    private static Retrofit retrofit=null;

    public static Retrofit getClient()
    {
        if(retrofit==null)
        {
            retrofit= new Retrofit.Builder()//netsed class of Retrofit used to create a new instance of retrofit class
                    .baseUrl(URL)//loaded the base URL which will be used later for all api calls
                    .addConverterFactory(GsonConverterFactory.create())//added converter factory for
                    // serialization and desserialization of objects

                    .build();//created the retrofit instance with configured values

        }
        return retrofit;
    }

}

ApiInterface.java

package com.example.vikas.movie.rest;

/**
 * Created by vikas on 23/4/17.
 */
import com.example.vikas.movie.model.MoviesResponse;
import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Path;
import retrofit2.http.Query;
// to define endpoints of a URL we will use special retrofit annotations. we have used Get annotation in
// to get information from the API and the parameters os this method are @Query,@Path,
public interface ApiInterface
{
    @GET("movie/now-playing")
    Call<MoviesResponse> getNowPlayingMovies(@Query("api_key") String apikey);
    //return value is alwayays a parameterized call<T> oject whose paremeter is generic so we can have
    @GET("movie/{id}")
    Call<MoviesResponse> getMovieDetails(@Path("id") int id, @Query("api_key") String apiKey);
}


// so using this we will generate the following URL
//http://api.themoviedb.org/3/movie/now-playing?api_key=Xxxx

and now I am displaying the result in RecyclerView so Dapter class for that is

MovieAdapter.java

package com.example.vikas.movie.adapter;

/**
 * Created by vikas on 23/4/17.
 */
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;

import com.bumptech.glide.Glide;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.example.vikas.movie.R;
import com.example.vikas.movie.helper.CircleTransformation;
import com.example.vikas.movie.model.Movie;

import java.util.List;
public class MovieAdapter extends RecyclerView.Adapter<MovieAdapter.MovieViewHolder> {

    private List<Movie> movies;
    private int rowLayout;
    private Context context;


    public static class MovieViewHolder extends RecyclerView.ViewHolder {
       RelativeLayout moviesLayout;
        TextView movieTitle;
        TextView data;
        TextView movieDescription;
        TextView rating;


        public MovieViewHolder(View v) {
            super(v);
            moviesLayout = (RelativeLayout) v.findViewById(R.id.movie_layout);
            movieTitle = (TextView) v.findViewById(R.id.title);
            data = (TextView) v.findViewById(R.id.subtitle);
            movieDescription = (TextView) v.findViewById(R.id.description);
            rating = (TextView) v.findViewById(R.id.rating);
        }
    }

    public MovieAdapter(List<Movie> movies, int rowLayout, Context context) {
        this.movies = movies;
        this.rowLayout = rowLayout;
        this.context = context;
    }

    @Override
    public MovieAdapter.MovieViewHolder onCreateViewHolder(ViewGroup parent,
                                                            int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(rowLayout, parent, false);
        return new MovieViewHolder(view);
    }


    @Override
    public void onBindViewHolder(MovieViewHolder holder, final int position) {
        holder.movieTitle.setText(movies.get(position).getTitle());
        holder.data.setText(movies.get(position).getReleaseDate());
        holder.movieDescription.setText(movies.get(position).getOverview());
        holder.rating.setText(movies.get(position).getvote_average().toString());
    }

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

Now the MainActivity class in which I am making a call to the API and fetching the results and giving the results to adapter

MainActivity.java

package com.example.vikas.movie;

import android.support.design.widget.CoordinatorLayout;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;


import com.example.vikas.movie.adapter.MovieAdapter;
import com.example.vikas.movie.model.Movie;
import com.example.vikas.movie.model.MoviesResponse;
import com.example.vikas.movie.rest.ApiClient;
import com.example.vikas.movie.rest.ApiInterface;

import java.util.ArrayList;
import java.util.List;

import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

public class MainActivity extends AppCompatActivity
{

    private static final String TAG=MainActivity.class.getSimpleName();
    //get which got from web API
    private final static String API_KEY="Xxxxxx";
    private CoordinatorLayout coordinatorLayout;
    private MovieAdapter movieAdapter;
    private RecyclerView recyclerView;
    private List<Movie> movieList = new ArrayList<>();

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        coordinatorLayout=(CoordinatorLayout)findViewById(R.id.coordinate_layout);

        if(API_KEY.isEmpty())
        {
            Snackbar snackbar = Snackbar
                    .make(coordinatorLayout, "Please Enter The key", Snackbar.LENGTH_LONG);

            snackbar.show();
            return;
        }
        recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
        recyclerView.setLayoutManager(new LinearLayoutManager(getApplicationContext()));
        recyclerView.setItemAnimator(new DefaultItemAnimator());
        recyclerView.setAdapter(new MovieAdapter(movieList, R.layout.recyclerview_item_movie, getApplicationContext()));


        ApiInterface apiInterface= ApiClient.getClient().create(ApiInterface.class);
        Call<MoviesResponse> call= apiInterface.getNowPlayingMovies(API_KEY);
        call.enqueue(new Callback<MoviesResponse>()
        {
            @Override
            public void onResponse(Call<MoviesResponse> call, Response<MoviesResponse> response)
            {
                Log.d(TAG, "Number of movies received: " + movieList.size());
                int statusCode=response.code();
                 movieList= response.body().getResults();
                 movieAdapter.notifyDataSetChanged();


            }

            @Override
            public void onFailure(Call<MoviesResponse> call, Throwable t)
            {
                Snackbar snackbar = Snackbar
                        .make(coordinatorLayout, "Please Check your Internet Connection", Snackbar.LENGTH_LONG);

                snackbar.show();
                Log.e(TAG, t.toString());

            }
        });

    }
}

But when I am running this code I am getting this error logcat

04-23 23:32:32.529 30506-30506/com.example.vikas.movie E/AndroidRuntime: FATAL EXCEPTION: main
                                                                     Process: com.example.vikas.movie, PID: 30506
                                                                     java.lang.NullPointerException: Attempt to invoke virtual method 'void com.example.vikas.movie.adapter.MovieAdapter.notifyDataSetChanged()' on a null object reference
                                                                         at com.example.vikas.movie.MainActivity$1.onResponse(MainActivity.java:69)
                                                                         at retrofit2.ExecutorCallAdapterFactory$ExecutorCallbackCall$1$1.run(ExecutorCallAdapterFactory.java:68)
                                                                         at android.os.Handler.handleCallback(Handler.java:739)
                                                                         at android.os.Handler.dispatchMessage(Handler.java:95)
                                                                         at android.os.Looper.loop(Looper.java:148)
                                                                         at android.app.ActivityThread.main(ActivityThread.java:5417)
                                                                         at java.lang.reflect.Method.invoke(Native Method)
                                                                         at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
                                                                         at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)

So what I should do now to run this code and fetch the results to successfully display the results so that it does not return null

This question is different as First I was not able not giving correct URL

Vikas
  • 49
  • 9

2 Answers2

0

First your url is wrong. It should be

 http://api.themoviedb.org/3/movie/now_playing?api_key=ddfffa28b4ace50cacf67274370469a1 

Notice the end point movie/now_playing instead of movie/now-playing

This is documented at https://developers.themoviedb.org/3/movies/get-now-playing

Also you can get the list size after you get the response and initialize list

First this

movieList= response.body().getResults();

then

Log.d(TAG, "Number of movies received: " + movieList.size());

Edit1 :

You have this

 recyclerView.setAdapter(new MovieAdapter(movieList, R.layout.recyclerview_item_movie, getApplicationContext()));

You never initialize movieAdapter and when you call notifyDataSetChanged(), movieAdapter is null.

Change it to

 movieAdapter = new MovieAdapter(movieList, R.layout.recyclerview_item_movie, getApplicationContext());

Then

recyclerView.setAdapter(movieAdapter);

Note get getApplicationContext() might not be useful is most cases related to ui. Prefer activity context.

Edit 2: You also need to add items to the same list and then call notifyDataSetChanged() as suggested in the below comment.

Raghunandan
  • 132,755
  • 26
  • 225
  • 256
  • Wait for some time I am trying your answer – Vikas Apr 23 '17 at 17:52
  • you can copy paste the url in browser to check if its working. no need to run it on android... – Raghunandan Apr 23 '17 at 17:53
  • I have tried your answer now it is showing movie size 20 but getting this logcat error and app crashes – Vikas Apr 23 '17 at 17:59
  • update your post with new logcat. it is not related to retrofit since you can see the list size. Its a different question altogether – Raghunandan Apr 23 '17 at 18:00
  • FATAL EXCEPTION: main Process: com.example.vikas.movie, PID: 27956 java.lang.NullPointerException: Attempt to invoke virtual method 'void com.example.vikas.movie.adapter.MovieAdapter.notifyDataSetChanged()' on a null object reference – Vikas Apr 23 '17 at 18:01
  • I have updated my post with the new logcat please have a look – Vikas Apr 23 '17 at 18:05
  • @Vikas its an NPE should be easily to debug. pls check the edit – Raghunandan Apr 23 '17 at 18:05
  • Don't use `=` to get the list in the results... Use clear and addAll... Otherwise, notifyDataSetChanged does nothing – OneCricketeer Apr 23 '17 at 18:10
  • @cricket_007 yup missed that since list is already initialized addAll should be there. – Raghunandan Apr 23 '17 at 18:12
  • Thanks a lot but I have changed something according to your solution it was not giving NPE but UI was not updated so I have done this – Vikas Apr 23 '17 at 18:14
  • public void onResponse(Call call, Response response) { int statusCode=response.code(); movieList= response.body().getResults(); Log.d(TAG, "Number of movies received: " + movieList.size()); recyclerView.setAdapter(new MovieAdapter(movieList, R.layout.recyclerview_item_movie, getApplicationContext())); // movieAdapter.notifyDataSetChanged(); } – Vikas Apr 23 '17 at 18:14
  • @Vikas you can initialize adapter in onCreate. You only update your list that is used for adapter and refresh adapter using notifyDataSetChanged(). You can download a tutorial or google for a recyclerview sample – Raghunandan Apr 23 '17 at 18:16
  • Thanks a lot you saved my day and can you please give me some guidance how can I become a good android developer – Vikas Apr 23 '17 at 18:16
0

Replace

recyclerView.setAdapter(new MovieAdapter(movieList, R.layout.recyclerview_item_movie, getApplicationContext()));

With

movieAdapter = new MovieAdapter(movieList, R.layout.recyclerview_item_movie, this);
recyclerView.setAdapter(movieAdapter);

And replace

movieList= response.body().getResults();
movieAdapter.notifyDataSetChanged();

With this. Otherwise notifyDataSetChanged does nothing because you have a completely different list and would need to recreate the adapter

movieList.clear();
movieList.addAll(response.body().getResults()); 
movieAdapter.notifyDataSetChanged();
OneCricketeer
  • 179,855
  • 19
  • 132
  • 245