4

I am new to android and trying to update the gridlayout in popular movies popular movies app of udacity nanodegree project.

I am downloading the data using AsyncTask and parsing the data, updating the recyclerview and setting adapter in onPostExecute.

This is my AsyncTask class

private class getJson extends AsyncTask<String, Void, String>{

    @Override
    protected String doInBackground(String... strings) {
        String result = "";
        URL url;
        HttpURLConnection httpURLConnection = null;


        try {
            url = new URL(strings[0]);

            httpURLConnection = (HttpURLConnection) url.openConnection();

            InputStream in = httpURLConnection.getInputStream();

            InputStreamReader reader = new InputStreamReader(in);

            int data = reader.read();

            while ( data != -1 ){
                  char current  = (char) data;

                  result += current;

                  data = reader.read();
            }

        } catch (java.io.IOException e) {
            e.printStackTrace();
        }

        return result;
    }

    @Override
    protected void onPostExecute(String s) {
        super.onPostExecute(s);

        jsonString = s;

        Log.i("result", jsonString);

        JSONObject jsonObject = null;

        if(!list.isEmpty())
          list.clear();
        try {

            jsonObject = new JSONObject(jsonString);

            JSONArray jsonArray = jsonObject.getJSONArray("results");

            for( int i = 0; i < jsonArray.length() && i < 10; i++ ){

                JSONObject currObject = jsonArray.getJSONObject(i);

                String posterurl = preUrl + currObject.getString("backdrop_path");

                Log.i("poster", posterurl);

                String url =  preUrl + currObject.getString("poster_path");

                String overview = currObject.getString("overview");

                String title = currObject.getString("original_title");

                String release_date = (currObject.getString("release_date"));

                String votes = (currObject.getString("vote_average"));

                list.add(new MovieDetails(url,overview, title, release_date, votes, posterurl));
            }

        } catch (JSONException e) {
            e.printStackTrace();
        }



    }
}

This is my adapter class

    public class MainRecyclerViewAdapter extends RecyclerView.Adapter<MainRecyclerViewAdapter.ViewHolder> {

    ArrayList<MovieDetails> data;
    Context context;

    public MainRecyclerViewAdapter(Context context, ArrayList<MovieDetails> data ){
         this.data = data;
         this.context = context;
    }
    @Override
    public MainRecyclerViewAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

        View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.grid_item, parent, false);

        MainRecyclerViewAdapter.ViewHolder viewHolder = new MainRecyclerViewAdapter.ViewHolder(v);

        return viewHolder;
    }

    @Override
    public void onBindViewHolder(MainRecyclerViewAdapter.ViewHolder holder, int position) {

        Picasso.with(context).load(data.get(position).getImageUrl()).into(holder.movie_thumbnail);
        //holder.movie_thumbnail.setImageResource(R.mipmap.ic_launcher);


    }

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

    public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{

        ImageView movie_thumbnail;

        private Context context;

        @Override
        public void onClick(View view) {
            Toast.makeText(context,data.get(getPosition()).getTitle(),Toast.LENGTH_LONG).show();
            Intent intent = new Intent(context, DetailsActivity.class);
            Bundle bundle = new Bundle();
            bundle.putSerializable("list", (Serializable) data);
            intent.putExtras(bundle);
            intent.putExtra("index", getAdapterPosition());

            context.startActivity(intent);
        }

        public ViewHolder(View itemView) {
            super(itemView);
            context = itemView.getContext();
            itemView.setOnClickListener(this);
            movie_thumbnail = (ImageView) itemView.findViewById(R.id.movies_item_thumbnail);
        }
    }
}

This is how I am handling sorting based on voting or popularity

@Override
public boolean onOptionsItemSelected(MenuItem item) {

    getJson gJson = new getJson();
    switch (item.getItemId()){
        case R.id.rating :
            gJson.execute(top_rated);
            return true;
        case R.id.popularity : gJson.execute(popular);
            return true;
        default: return super.onOptionsItemSelected(item);

    }

}

onCreate

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    Toolbar myToolbar = (Toolbar) findViewById(R.id.main_toolbar);
    setSupportActionBar(myToolbar);

    getJson getJson = new getJson();
    getJson.execute(popular);

    recyclerView = (RecyclerView)findViewById(R.id.main_recyclerview);

    recyclerView.setHasFixedSize(true);
    layoutManager = new GridLayoutManager(MainActivity.this,2);
    recyclerView.setLayoutManager(layoutManager);

    adapter = new MainRecyclerViewAdapter(MainActivity.this, list);
    recyclerView.setAdapter(adapter);

}

I figured out to use notifydatasetchanged(), but I am not able to figure where to use that.

Also please tell me better way to handle the sorting.

This is what I did :

  1. I moved the instantiating of adapter and setting of adapter of to onCreate
  2. calling notfiydatasetchanged() in onPostexeute()

  3. Everytime I call the AsyncTask, my arraylist is being populated with new elements and so the adapter is filling all images. I am list.clear() before parsing the json in onPostexecute()

potter13
  • 69
  • 7
  • See this http://stackoverflow.com/questions/3669325/notifydatasetchanged-example – Mable John Dec 23 '16 at 17:15
  • I don't really understand your question. You are already calling `adapter.notifyDataSetChanged();` in your code. It's there in `onPostExecute`. Please can you make me understand. – X09 Dec 23 '16 at 17:27
  • But I am using recyclerview, this answer talks about arrayadapter and methods of arrayadapter. – potter13 Dec 23 '16 at 17:28
  • @Ozuf I am sorry I didn't mention this in question, I used notifydatasetchanged() seeing other answers, but it didn't worked for me. I will update the question. – potter13 Dec 23 '16 at 17:30
  • @potter13 i can give you an answer but that does not help you to improve you knowledge as the way when you read and learn by yourself, watch this video its bit old but i really recommend this for you https://www.youtube.com/watch?v=wDBM6wVEO70 and go through so answers there are plenty of them already exists on this topic if you want ill comment few of them – Charuක Dec 23 '16 at 17:35
  • @Charuka Thank u, I'll go through it – potter13 Dec 23 '16 at 17:47
  • @potter13 posted an answer for you :P – Charuක Dec 23 '16 at 19:15

5 Answers5

1

For http-requests I would advise to use retrofit. The adapter describe the sorting function on the options you want, call it and in the end notifydatasetchanged();

for example

void sort(String sortBy) {
    // your sort
    notifydatasetchanged();
}

When you want to use the sort simply call a method of sorting the adapter, set data into it.

Charuක
  • 12,953
  • 5
  • 50
  • 88
ViLeon
  • 91
  • 5
  • Thank you, this app need to be submitted in two parts. I will use retrofit for the part-2 submission of this app, I thought I could concentrate on recyclerview, intents and all the basic stuff in part-1. – potter13 Dec 24 '16 at 03:18
1

Set the adapter in the onCreate of the activity.

    adapter = new MainRecyclerViewAdapter(MainActivity.this, list);
    recyclerView.setAdapter(adapter);

Use a field to store your list.

Call the notifydatasetchanged() in the onPostExecute() of the AsyncTask (like you are doing), but don't reassign the adapter there.

Cory Roy
  • 5,379
  • 2
  • 28
  • 47
  • I am setting the adapter in onCreate of the activity and calling notifydatasetchanged() in PostExecute() but no use, when I re run the app or chaging the sort previous images are not clearing – potter13 Dec 23 '16 at 17:43
  • Did you move the adapter instantiation out of the post-execute? – Cory Roy Dec 23 '16 at 18:10
1

Just like Cory Roy suggested, try this:

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        recyclerView = (RecyclerView)findViewById(R.id.main_recyclerview);
        layoutManager = new GridLayoutManager(MainActivity.this, 2);
        recyclerView.setLayoutManager(layoutManager);

        adapter = new MainRecyclerViewAdapter(MainActivity.this, list);
        recyclerView.setAdapter(adapter);
    }

   private class getJson extends AsyncTask<String, Void, String>{

    @Override
    protected String doInBackground(String... strings) {
        String result = "";
        URL url;
        HttpURLConnection httpURLConnection = null;

        try {
            url = new URL(strings[0]);

            httpURLConnection = (HttpURLConnection) url.openConnection();

            InputStream in = httpURLConnection.getInputStream();

            InputStreamReader reader = new InputStreamReader(in);

            int data = reader.read();

            while ( data != -1 ){
                  char current  = (char) data;

                  result += current;

                  data = reader.read();
            }

        } catch (java.io.IOException e) {
            e.printStackTrace();
        }

        return result;
    }

    @Override
    protected void onPostExecute(String s) {
        super.onPostExecute(s);

        jsonString = s;

        Log.i("result", jsonString);

        JSONObject jsonObject = null;
        try {

            jsonObject = new JSONObject(jsonString);

            JSONArray jsonArray = jsonObject.getJSONArray("results");

            for( int i = 0; i < jsonArray.length() && i < 10; i++ ){

                JSONObject currObject = jsonArray.getJSONObject(i);

                String posterurl = preUrl + currObject.getString("backdrop_path");

                Log.i("poster", posterurl);

                String url =  preUrl + currObject.getString("poster_path");

                String overview = currObject.getString("overview");

                String title = currObject.getString("original_title");

                String release_date = (currObject.getString("release_date"));

                String votes = (currObject.getString("vote_average"));

                list.add(new MovieDetails(url,overview, title, release_date, votes, posterurl));
            }

        } catch (JSONException e) {
            e.printStackTrace();
        }

        adapter.notifyDataSetChanged();

    }
}

Please try it and let me know if it works.

Community
  • 1
  • 1
X09
  • 3,827
  • 10
  • 47
  • 92
  • Yes I tried this, but when I am using the sort by rating, previous images are not clearing and both sets of images are loading. – potter13 Dec 23 '16 at 17:54
1

You are use an AsyncTask to download movies from remote server. Once the download and parsing is over you create the adapter and set it to the RecyclerView

As per your implementation no need to call notifyDataSetChanged() on postExecute()

The above approach has one disadvantage that you create a new adapter instance every time the AsyncTask execute.

Try something below

 Activity {

    ArrayList<MovieDetails> data;
    MainRecyclerViewAdapter adapter

    onCreate() {

        recyclerView = (RecyclerView)findViewById(R.id.main_recyclerview);

        recyclerView.setHasFixedSize(true);
        layoutManager = new GridLayoutManager(MainActivity.this,2);
        recyclerView.setLayoutManager(layoutManager);


        adapter = new MainRecyclerViewAdapter(MainActivity.this);
        recyclerView.setAdapter(adapter);
    }


    AsyncTask {
        /.....

        onPostExecute{

            // Download completed
            adapter.notifyDataSetChanged()
        }
    }

    MainRecyclerViewAdapter {

        //...
        ViewHolder {
            //...
        }
    }
}
Charuක
  • 12,953
  • 5
  • 50
  • 88
Mable John
  • 4,518
  • 3
  • 22
  • 35
  • I made changes as suggested, but of no use. The previous images are not clearing when sort by rating – potter13 Dec 23 '16 at 17:59
1

Here is all what you need to do ,

look at my example

These are global vars

    private List<Movie> movieList = new ArrayList<>();
    private RecyclerView recyclerView;
    private MoviesAdapter mAdapter; 

let's go to onCreate

1.initialize recycle view

recyclerView = (RecyclerView) findViewById(R.id.recycler_view);

2.create new instance of my List

mAdapter = new MoviesAdapter(movieList);

3.set up my recycle view

        RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getApplicationContext());
        recyclerView.setLayoutManager(mLayoutManager);
        recyclerView.setItemAnimator(new DefaultItemAnimator());

4.set the adapter to my recycleView

recyclerView.setAdapter(mAdapter);

5.call the method to load the data.

setMyData();

let's go to setMyData method

1.get data from some way .. may be sever

2.add them to my List

movieList.add(movie); // i have not added the lines where i add data to my movie list think it has 10 elements now 

3.after i add all data i perform a

mAdapter.notifyDataSetChanged();

so every time there is a change i can call setMyData(); it sets data as needed.

How about that logic?

purposely didn't add the Adapter class , if you need that as well let me know

Hint: your async task should do what setMyData() method does? now see. Okay you need to update your data for the second time!!! Go through your async task, layoutManager = new GridLayoutManager(MainActivity.this,2);

ah ha you call this line again and again when you call your async task... people suggest you to notifyDataSet in onPostExecute,(it works but..)think, new object also gets created tan ta tan!! call your asyncTask 10 times .. now compare where i created my object, does that keep increasing .. now you will see what went wrong!

p.s my LayoutManager instance is not in my setMyData() method but in my onCreate

Charuක
  • 12,953
  • 5
  • 50
  • 88
  • Thanks for all the help, I updated the solution. Only thing is I haven't created `setMyData()` method, I moved the code to onCreate as suggested and calling `notifydatasetchanged()` in `onPostexecute()`, but the main problem is the arraylist getting populated everytime. So I am calling `list.clear()` before parsing and adding data – potter13 Dec 24 '16 at 03:12