0

I'm working with Android Studio and I have a problem with my simple project, which uses an API. I'm trying to get images and text from TVMazeAPI http://api.tvmaze.com with the help of retrofit. The idea is in implementation of the search functionality through the SearchView. You type in some title and the app gives some results from http://api.tvmaze.com/search/shows?q=girls Images and text aren't shown and app crashes when I'm trying to make search for some show

The debugger shows this: FATAL EXCEPTION: main Process: com.kate_lebedeva.searchtheshow, PID: 3815 java.lang.NullPointerException: Attempt to invoke interface method 'int java.util.List.size()' on a null object reference at com.kate_lebedeva.searchtheshow.MovieAdapter.getItemCount(MovieAdapter.java:49)

the error is related to this lines in Adapter:

@Override
    public int getItemCount() {

        if(movieList != null){
            return movieListFiltered.size();
        } else {
            return 0;
        }
    }

I can't handle this problem. I'm completely new in this sphere. Please give me an advice.

app gradle

dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    implementation 'com.android.support:appcompat-v7:28.0.0'
    implementation 'com.android.support.constraint:constraint-layout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
    implementation 'com.android.support:design:28.0.0'
    implementation 'com.android.support:cardview-v7:28.0.0'
    implementation 'com.android.support:recyclerview-v7:28.0.0'
    implementation 'com.squareup.retrofit2:retrofit:2.3.0'
    implementation 'com.squareup.retrofit2:converter-gson:2.3.0'
    implementation 'com.github.bumptech.glide:glide:4.3.1'
    implementation 'com.android.support:support-v4:28.0.0'
}

android Manifest

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.kate_lebedeva.searchtheshow">
    <uses-permission android:name="android.permission.INTERNET"></uses-permission>

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:tools="http://schemas.android.com/tools"
    tools:context=".MainActivity"
    android:orientation="vertical">

    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme">

    </android.support.design.widget.AppBarLayout>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@android:color/white">

        <android.support.v7.widget.RecyclerView
            android:id="@+id/recyclerview"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:padding="5dp">
        </android.support.v7.widget.RecyclerView>
    </LinearLayout>

</LinearLayout>

item.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_height="match_parent"
    android:layout_width="wrap_content"
  >

    <android.support.v7.widget.CardView
        android:layout_width="match_parent"
        android:layout_height="160dp"
        android:orientation="horizontal"
        android:padding="8dp">

        <ImageView
            android:id="@+id/image"
            android:layout_width="100dp"
            android:layout_height="match_parent"
            android:layout_marginRight="8dp"
            android:scaleType="fitXY" />

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">

            <TextView
                android:id="@+id/title"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Title"
                android:textSize="16sp"
                android:textStyle="bold"
                />

            <TextView
                android:id="@+id/summary"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="4dp"
                android:ellipsize="end"
                android:maxLines="4"
                android:text="summary"
                />
    </LinearLayout>
    </android.support.v7.widget.CardView>
</LinearLayout>

ApiInterface.java:

import retrofit2.Call;
import retrofit2.http.GET;

public interface ApiInterface {
    @GET("search/shows?q=girls")
    Call <List<Movie>> getMovies();
}

TvMazeApiClient.java

import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

public class TvMazeApiClient {

        public static String BASE_URL ="http://api.tvmaze.com/";
        private static Retrofit retrofit;
        public static Retrofit getClient(){
            if(retrofit == null){
                retrofit = new Retrofit.Builder()
                        .baseUrl(BASE_URL)
                        .addConverterFactory(GsonConverterFactory.create())
                        .build();
            }
            return retrofit;
        }

}

Main Activity:

 public class MainActivity extends AppCompatActivity {
    private SearchView searchView;
    private RecyclerView recyclerView;
    private MovieAdapter movieAdapter;
    private List<Movie> movieList;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        recyclerView = (RecyclerView) findViewById(R.id.recyclerview);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        movieAdapter = new MovieAdapter();
        recyclerView.setAdapter(movieAdapter);

        movieList = new ArrayList<>();
        ApiInterface apiService = TvMazeApiClient.getClient().create(ApiInterface.class);
        Call<List<Movie>> call = apiService.getMovies();

        call.enqueue(new Callback<List<Movie>>() {
            @Override
            public void onResponse(Call<List<Movie>> call, Response<List<Movie>> response) {
                if(movieList != null) {
                movieList = response.body();
                Log.d("TAG", "Response = " + movieList);
                movieAdapter.setMovieList(getApplicationContext(), movieList);
            }
            else{
                Toast.makeText(MainActivity.this,"error", Toast.LENGTH_SHORT).show();
            }
            }

            @Override
            public void onFailure(Call<List<Movie>> call, Throwable t) {
                Log.d("TAG","Response = "+t.toString());
                Toast.makeText(MainActivity.this,t.getLocalizedMessage(), Toast.LENGTH_SHORT).show();
            }
        });
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.options_menu, menu);

        SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
        searchView = (SearchView) menu.findItem(R.id.action_search)
                .getActionView();
        searchView.setSearchableInfo(searchManager
                .getSearchableInfo(getComponentName()));
        searchView.setMaxWidth(Integer.MAX_VALUE);

        searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
            @Override
            public boolean onQueryTextSubmit(String query) {
                movieAdapter.getFilter().filter(query);
                return false;
            }

            @Override
            public boolean onQueryTextChange(String query) {
                movieAdapter.getFilter().filter(query);
                return false;
            }
        });
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();
        if (id == R.id.action_search) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }

    @Override
    public void onBackPressed() {
        if (!searchView.isIconified()) {
            searchView.setIconified(true);
            return;
        }
        super.onBackPressed();
    }}

Adapter:

public class MovieAdapter extends RecyclerView.Adapter<MovieAdapter.MyViewHolder> implements Filterable {

    private List<Movie> movieList;
    private List<Movie> movieListFiltered;
    private Context context;

    public void setMovieList(Context context,final List<Movie> movieList){
        this.context = context;
        this.movieList = movieList;
        this.movieListFiltered = movieList;
    }

    @Override
    public MovieAdapter.MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item,parent,false);
        return new MyViewHolder(view);
    }

    @Override
    public void onBindViewHolder(MovieAdapter.MyViewHolder holder, int position) {
        holder.title.setText(movieListFiltered.get(position).getTitle());
        Glide.with(context).load(movieList.get(position).getImageUrl()).apply(RequestOptions.centerCropTransform()).into(holder.image);
    }

    @Override
    public int getItemCount() {

        if(movieList != null){
            return movieListFiltered.size();
        } else {
            return 0;
        }
    }

    @Override
    public Filter getFilter() {
        return new Filter() {
            @Override
            protected FilterResults performFiltering(CharSequence charSequence) {
                String charString = charSequence.toString();
                if (charString.isEmpty()) {
                    movieListFiltered = movieList;
                } else {
                    List<Movie> filteredList = new ArrayList<>();
                    for (Movie movie : movieList) {
                        if (movie.getTitle().toLowerCase().contains(charString.toLowerCase())) {
                            filteredList.add(movie);
                        }
                    }
                    movieListFiltered = filteredList;
                }

                FilterResults filterResults = new FilterResults();
                filterResults.values = movieListFiltered;
                return filterResults;
            }

            @Override
            protected void publishResults(CharSequence charSequence, FilterResults filterResults) {
                movieListFiltered = (ArrayList<Movie>) filterResults.values;

                notifyDataSetChanged();
            }
        };
    }

    public class MyViewHolder extends RecyclerView.ViewHolder {

        TextView title;
        ImageView image;

        public MyViewHolder(View view) {
            super(view);
            title = (TextView) view.findViewById(R.id.title);
            image = (ImageView)view.findViewById(R.id.image);
        }
    }
}

Movie.java

public class Movie {

    private String title;

    private String imageUrl;

    public Movie(String title, String imageUrl) {
        this.title = title;
        this.imageUrl = imageUrl;
    }

    public String getTitle() {
        return title;
    }

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

    public String getImageUrl() {
        return imageUrl;
    }

    public void setImageUrl(String imageUrl) {
        this.imageUrl = imageUrl;
    }
}

1 Answers1

0

You should add check for movieListFiltered

if(movieList != null && movieListFiltered!=null){
   return movieListFiltered.size();
}
P. Suvajac
  • 29
  • 4
  • thanks a lot app doesn't crash anymore but still doesn't works properly images and text still aren't displayed and i can't completely understand what's the problem – Kate Lebedeva Oct 01 '18 at 13:48
  • Your Movie object isn't structured properly so GSON can't map API response into it. Try to log response to see how data is structured and check [this post](https://stackoverflow.com/questions/32942661/how-can-retrofit-2-0-parse-nested-json-object) to see how you should nest your fields. – P. Suvajac Oct 01 '18 at 13:56
  • thanks a lot again :D I don't know what I would do without you – Kate Lebedeva Oct 01 '18 at 14:05