Erick Ramirez
Erick Ramirez

Reputation: 167

How to implement a custom ArrayList in AsyncTask?

i am building an Android application that takes an API(The Movie Database), and extracts the JSON array. Upon extraction, the appropriate JSONObject is sent to a model. This model is a class composed of setters and getters to extract the object and assign it to variables which i can use. All of this is done on the AsyncTask. However, i am having a hard time understanding the doInBackground method as it expects me to return something. I have done all of the work on the getMovieJson method and i know i must return something from the doInBackground because it is required to update my custom adapter on the onPostExecute method. This custom adapter is an ArrayAdapter that creates the appropriate views to populate in my gridView.

This is my Model

package com.xxcanizeusxx.erick.moviesnow;

/**
 * This class acts as the model base for our
 * array of JSON Objects that need to be populated.\
 * This class uses getters and setters to achieve its task.
 */

public class Movie {

    private String title;
    private String vote_average;
    private String overview;
    private String release_date;
    private String poster_path;

    public String getTitle(){
        return title;
    }

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

    public String getOverview(){
        return overview;
    }

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

    public String getRelease_date(){
        return release_date;
    }

    public void setRelease_date(String release_date){
        this.release_date = release_date;
    }

    public String getVote_average(){
        return vote_average;
    }

    public void setVote_average(String vote_average){
        this.vote_average = vote_average;
    }


    public String getPoster_path(){
        return poster_path;
    }

    public void setPoster_path(String poster_path){
        this.poster_path = poster_path;
    }


}

This my Fragment

package com.xxcanizeusxx.erick.moviesnow;

import android.net.Uri;
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.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.GridView;

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;

/**
 * Created by Erick on 1/4/2017.
 */

public class MovieFragment extends Fragment {
    //Initialize our array adapter and components.
    private ArrayList<Movie> mMovieData;
    private MovieAdapter mMovieAdapter;

    //Empty constructor
    public MovieFragment() {

    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //Add this line in order for this fragment to handle menu events.
        setHasOptionsMenu(true);
    }

    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        inflater.inflate(R.menu.movie_fragment, menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();
        if (id == R.id.popular_movies) {
            updateMovie();

            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    private void updateMovie() {
        FetchMovieTask fetchMovie = new FetchMovieTask();
        fetchMovie.execute();
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        //Initialize our array adapter
        mMovieData = new ArrayList<>();
        mMovieAdapter = new MovieAdapter(getActivity(), R.layout.grid_movie_item, mMovieData);

        View rootView = inflater.inflate(R.layout.gird_layout, container, false);

        //Get a reference to the gridView and attach the adapter to it
        GridView mGridView = (GridView) rootView.findViewById(R.id.gridView);
        mGridView.setAdapter(mMovieAdapter);
        return rootView;
    }

    @Override
    public void onStart() {
        super.onStart();
        updateMovie();
    }

    public class FetchMovieTask extends AsyncTask<String[], Void, String[]> {
        private final String LOG = FetchMovieTask.class.getSimpleName();



        private  String[] getMovieJson(String movieJsonStr) throws JSONException {
            final String OWM_RESULTS = "results";
            String title;
            String posterPath;




            JSONObject movieJson = new JSONObject(movieJsonStr);
            JSONArray movieJsonArray = movieJson.getJSONArray(OWM_RESULTS);
            Movie movieItem = new Movie();
            //Newly created array  that will house the data in order to check for views on the onPostExecute method
            String[] results = new String[movieJsonStr.length()];
            for (int i = 0; i < movieJsonArray.length(); i++) {
                JSONObject movieObject = movieJsonArray.getJSONObject(i);
                title = movieObject.getString("title");
                posterPath = movieObject.getString("poster_path");
                movieItem.setTitle(title);
                movieItem.setPoster_path(posterPath);
                results[i] = movieObject.getString(posterPath) +" " + movieObject.getString(posterPath);


            }

            mMovieData.add(movieItem);
            return results;


        }


        @Override
        protected String[] doInBackground(String[]... params) {
            //Establish a connection
            HttpURLConnection urlConnection = null;
            BufferedReader bufferedReader = null;

            //Will contain the raw JSON as a string
            String movieJsonStr = null;
            //String as placeholders
            String descriptionHolder = "popularity.desc";

            try {
                //String to hold the Base url
                final String TMDB_BASE_URL = "http://api.themoviedb.org/3/discover/movie?";
                final String APPID = "api_key";
                final String DESC = "sort_by";

                //Build the url
                Uri buildMovieUri = Uri.parse(TMDB_BASE_URL).buildUpon()
                        .appendQueryParameter(DESC, descriptionHolder)
                        .appendQueryParameter(APPID, BuildConfig.TMDP_API_KEY)
                        .build();

                URL url = new URL(buildMovieUri.toString());
                //Create the request to TMDB and open 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;
                }
                bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
                String line;
                while ((line = bufferedReader.readLine()) != null) {
                    //Make debugging easier by adding a new line to the bufffer stream
                    buffer.append(line + "\n");
                    int result = 1;
                }

                if (buffer.length() == 0) {
                    //stream was empty. No point in parsing.
                    return null;
                }
                movieJsonStr = buffer.toString();

            } catch (IOException e) {
                Log.e(LOG, "Error ", e);
                //If code didnt get the movie data, no point in parsing
                return null;
            } finally {
                if (urlConnection != null) {
                    urlConnection.disconnect();
                }
                if (bufferedReader != null) {

                    try {
                        bufferedReader.close();
                    } catch (final IOException e) {
                        Log.e(LOG, "Error closing stream ", e);
                    }
                }
            }
            try {
                return getMovieJson(movieJsonStr);
            } catch (JSONException e) {
                Log.e(LOG, e.getMessage(), e);
                e.printStackTrace();
            }
                return null;
        }


        @Override
        protected void onPostExecute(String[] result){
            if (result != null){
                mMovieAdapter.clear();
                for (String results : result  ){
                    mMovieAdapter.setMovieData(results);
                }
            }
        }



    }

}

This is my Adapter

package com.xxcanizeusxx.erick.moviesnow;

import android.app.Activity;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;

import com.squareup.picasso.Picasso;

import java.util.ArrayList;

/**
 * This class is the MovieAdapter that will take data from the model and
 * display it on the View. Additionally, this adapter will load the picasso base_img_url
 * to populate the griView according to Udacity.
 */

public class MovieAdapter extends ArrayAdapter<Movie> {
    private final String BASE_IMAGE_URL = "http://image.tmdb.org/t/p/w185";
    private Context mContext;
    private int resource;
    private ArrayList<Movie> mMovieData = new ArrayList<Movie>();

    //Constructor matching super
    public MovieAdapter(Context mContext, int resource, ArrayList<Movie> mMovieData) {
        super(mContext, resource, mMovieData);
        this.mContext = mContext;
        this.resource = resource;
        this.mMovieData = mMovieData;

    }

    //This method sets the movieData and refreshes the gridLayout items.
    public void setMovieData(ArrayList<Movie> mMovieData){
        this.mMovieData = mMovieData;
        //Notifies if data state has changed
        notifyDataSetChanged();
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent){
        //Declare the variables to hold the convertView and viewHolder static class
        View cv = convertView;
        ViewHolder viewHolder;

        //If the convertView is null, we don't have a view so, create one.
        if(cv == null){
            //Create the View
            cv = LayoutInflater.from(getContext()).inflate(resource, parent, false);
            //Call the static viewHolder class
            viewHolder = new ViewHolder();
            //Initialize the textView and imageView to load inside the view
            viewHolder.movieTextView = (TextView) cv.findViewById(R.id.movie_title);
            viewHolder.moviePoster = (ImageView) cv.findViewById(R.id.movie_poster);
            cv.setTag(viewHolder);
        }else{
            viewHolder = (ViewHolder) cv.getTag();
        }

        Movie movieItem = mMovieData.get(position);
        //Set the textView holder
        viewHolder.movieTextView.setText(movieItem.getTitle());
        //use picasso to load images to the holder
        Picasso.with(mContext).load(BASE_IMAGE_URL + movieItem.getPoster_path()).fit().into(viewHolder.moviePoster);
        return cv;

    }

    static class ViewHolder{
        TextView movieTextView;
        ImageView moviePoster;

    }
}

This is my Main Activity

package com.xxcanizeusxx.erick.moviesnow;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        if(savedInstanceState == null){
            getSupportFragmentManager().beginTransaction()
                    .add(R.id.container, new MovieFragment())
                    .commit();
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu){
        //Inflate the menu, this adds items to actionbar if present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

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

My girdLayout xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
             android:id="@+id/grid_layout"
              android:orientation="vertical"
              android:layout_width="match_parent"
              android:layout_height="match_parent">
    <GridView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/gridView"
        android:numColumns="2"
        android:gravity="center"
        android:drawSelectorOnTop="true"
        android:verticalSpacing="5dp"
        android:horizontalSpacing="5dp">
    </GridView>

</FrameLayout>

My movieItem xml (To populate the gridLayout)

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:id="@+id/movie_detail_item"
              android:orientation="vertical"
              android:layout_width="wrap_content"
              android:layout_height="wrap_content">
    <ImageView
        android:id="@+id/movie_poster"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:scaleType="fitXY"/>

    <TextView
        android:id="@+id/movie_title"
        android:maxLines="2"
        android:gravity="center"
        android:textSize="14sp"
        android:textAllCaps="true"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

</LinearLayout>

Upvotes: 0

Views: 413

Answers (2)

Sadiq Md Asif
Sadiq Md Asif

Reputation: 902

Change the parameter of your Asynctask first

public class FetchMovieTask extends AsyncTask<Void, String[], String[]> {}

Then change the return type of your doInBackground() to get the results

@Override
protected String[] doInBackground(Void... params) {}

Understand the flow of parameters and return types.

enter image description here

UPDATE:

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

        //Initialize our array adapter
        mMovieData = new ArrayList<>();
        updateView(mMovieData);
        return rootView;
    }


void updateView(ArrayList<>() movieData){
mMovieAdapter = new MovieAdapter(getActivity(), R.layout.grid_movie_item, movieData);

        //Get a reference to the gridView and attach the adapter to it
        GridView mGridView = (GridView) rootView.findViewById(R.id.gridView);
        mGridView.setAdapter(mMovieAdapter);
}

Return ArrayList from your GetMovie & DoInBackground

 @Override
protected ArrayList<Movies> doInBackground(Object... params) {}

and

private ArrayList<Movies> getMovieJson(String movieJsonStr) throws JSONException {
....
mMovieData.add(movieItem);
return mMovieData;
}

call this updateView to update rather setAdapater(..) and postExecute parameters to

@Override
        protected void onPostExecute(ArrayList<Movies> movieData){
            updateView(movieData);
        }

Upvotes: 2

Dibzmania
Dibzmania

Reputation: 1994

First things is you code compiling ? From what I can See your AsyncTask has the following signature -

public class FetchMovieTask extends AsyncTask<String[], Void, Void> {

But You are returning String[] from doInBackground which ideally would give you an error. Anyways, you don't have to return anything from the AsyncTask in your case. Just add this call on onPostExecute - mMovieAdapter.setMovieData(Arrays.asList(result))

Btw, the type result should be String[] and not String as I can see.

Upvotes: 0

Related Questions