Reputation: 35
I´m trying to build the next android app:
1) Connect to "The movie DB" API and bring a JSON with the top popular movies.
2) Extract from that JSON the links to the posters (little images) of all that popular movies.
3) Use the Picasso library to put the images on a Gridview.
Steps 1 and 2 are working fine. 3 is almost there but I´m having a problem with the async type of the job.
If I hardcode the List on witch the posters should be, the Gridview populates just fine but if I do it through the background job I think that the View is getting created before the data is ready and when the data from the API is ready the View does not get it or does not render it. So, the Activity just look blank.
MainFragment.java
package com.example.android.popmovies;
import android.content.Context;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.GridView;
import android.widget.ImageView;
import com.squareup.picasso.Picasso;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
public class MainFragment extends Fragment {
// the array to populate with data from the API
private List moviesArray= new ArrayList();
public MainFragment() {
// Required empty public constructor
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Connection connection = new Connection();
connection.execute();
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_main, container, false);
GridView gridView = (GridView) rootView.findViewById(R.id.grid_view_id);
gridView.setAdapter(new CustomAdapter(this.getContext(), moviesArray));
return rootView;
}
public class CustomAdapter extends BaseAdapter{
private Context context;
private List items;
public CustomAdapter(Context context, List items){
super();
this.context = context;
this.items = items;
}
@Override
public int getCount() {
return items.size();
}
@Override
public Object getItem(int position) {
return items.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override public View getView(int position, View convertView, ViewGroup parent) {
ImageView img;
if (convertView == null) {
img = new ImageView(context);
img.setPadding(0,5,0,0);
convertView = img;
} else {
img = (ImageView) convertView;
}
Picasso.with(context)
.load((String) items.get(position))
.into(img);
return convertView;
}
}
public class Connection extends AsyncTask<Void, Void, String[]> {
private final String LOG_TAG = Connection.class.getSimpleName();
private String[] getPosterDataFromJson(String moviesJsonStr) throws JSONException {
String[] posters = new String[moviesJsonStr.length()];
// These are the names of the JSON objects that need to be extracted
final String RESULTS = "results";
//
// convertimos el JSON en un array
JSONObject moviesJson = new JSONObject(moviesJsonStr);
JSONArray moviesArray = moviesJson.getJSONArray(RESULTS);
for(int i = 0; i < moviesArray.length(); i++) {
String image;
JSONObject poster = moviesArray.getJSONObject(i);
image = poster.getString("poster_path");
posters[i] = image;
}
return posters;
}
@Override
protected String[] doInBackground(Void... params) {
HttpURLConnection urlConnection = null;
BufferedReader reader = null;
// Will contain the raw JSON response as a string.
String moviesJsonStr;
try {
// Construct the URL
String baseUrl = "http://api.themoviedb.org/3/movie/popular";
String apiKey = "?api_key=" + BuildConfig.POP_MOVIES_API_KEY;
URL url = new URL(baseUrl.concat(apiKey));
// Create the request and open the connection
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setRequestMethod("GET");
urlConnection.connect();
// Read the input stream into a String
InputStream inputStream = urlConnection.getInputStream();
StringBuffer buffer = new StringBuffer();
if (inputStream == null) {
// Nothing to do.
return null;
}
reader = new BufferedReader(new InputStreamReader(inputStream));
String line;
while ((line = reader.readLine()) != null) {
// Since it's JSON, adding a newline isn't necessary (it won't affect parsing)
// But it does make debugging a *lot* easier if you print out the completed
// buffer for debugging.
buffer.append(line + "\n");
}
if (buffer.length() == 0) {
// Stream was empty. No point in parsing.
return null;
}
// Paso todo el JSON a una variable
moviesJsonStr = buffer.toString();
// Extraigo del JSON lo que me interesa y lo guardo en un vector
String[] datos = getPosterDataFromJson(moviesJsonStr);
// Verificamos que haya algo en el vector
//Log.v(LOG_TAG, "Estoy en CONNECTION y el tamaño de DATOS es: " + Integer.toString(datos.length));
for (int i=0; i<datos.length; i++){
while(datos[i] != null){
Log.v(LOG_TAG, datos[i]);
break;
}
}
return datos;
} catch (IOException e) {
Log.e(LOG_TAG, "Error ", e);
return null;
} catch (JSONException e) {
e.printStackTrace();
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
if (reader != null) {
try {
reader.close();
} catch (final IOException e) {
Log.e(LOG_TAG, "Error closing stream", e);
}
}
}
return null;
}
@Override
protected void onPostExecute(String[] result) {
if (result != null) {
for(String posterStrings : result) {
while(posterStrings != null){
String direccion = "http://image.tmdb.org/t/p/" + "w185/" + posterStrings;
Log.v(LOG_TAG, direccion);
moviesArray.add(direccion);
break;
}
}
}
}
}
}
I´m almost convinced that the problem is that the data is arriving after the View is created but I´m not able to "inform" the View "hey, you got some data to take care of".
Thanks in advance and please excuse my english.
Upvotes: 2
Views: 1662
Reputation: 2028
You need to inform your adapter about the updated list of items. Add something like the updateMovies method to your adapter and call it from the onPostExecute. Like that:
CustomAdapter mAdapter;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
...
mAdapter = new CustomAdapter(this.getContext(), moviesArray);
gridView.setAdapter(mAdapter);
...
}
public class CustomAdapter extends BaseAdapter{
...
public void updateMovies(List items) {
mItems = items;
notifyDataSetChanged();
}
...
}
public class Connection extends AsyncTask<Void, Void, String[]> {
...
@Override
protected void onPostExecute(String[] result) {
if (result != null) {
for(String posterStrings : result) {
while(posterStrings != null){
String direccion = "http://image.tmdb.org/t/p/" + "w185/" + posterStrings;
Log.v(LOG_TAG, direccion);
moviesArray.add(direccion);
break;
}
}
mAdapter.updateMovies(moviesArray);
}
}
...
}
Upvotes: 3