Search code examples
androidandroid-fragmentsandroid-recyclerviewandroid-viewpager

RecyclerView inside a Fragment conained in a ViewPager is empty


Struggling with this so bad!

I created a viewpager with 2 pages, one being an audio player that is working perfectly, and the second page being the news list. It's a fragment with a recyclerview as a child.

Gradle

dependencies {
    implementation 'com.android.support.constraint:constraint-layout:1.1.1'
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    implementation project(':library')
    implementation 'com.android.support:support-v4:27.1.1'
    implementation 'com.android.support:appcompat-v7:27.1.1'
    implementation 'com.android.support:recyclerview-v7:27.1.1'
    implementation 'com.squareup.picasso:picasso:2.71828'
    implementation 'com.android.volley:volley:1.1.0'
}

ViewPager Adapter

private class mPagerAdapter extends FragmentStatePagerAdapter {

        public mPagerAdapter(FragmentManager fm) {
            super(fm);
        }

        @Override
        public Fragment getItem(int pos) {
            switch(pos) {

                case 0: return PlayerPage.newInstance();
                case 1: return News.newInstance();
                default: return PlayerPage.newInstance();
            }
        }

        @Override
        public int getCount() {
            return 2;
        }
    }

News Fragment

public class News extends Fragment {
    private List<NewsBlueprint> newsList = new ArrayList<>();
    private RecyclerView recyclerView;
    private NewsAdapter newsAdapter;
    private ProgressBar progressBar;

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

        progressBar = (ProgressBar) v.findViewById(R.id.progress_bar);

        recyclerView = (RecyclerView) v.findViewById(R.id.news_recyclerview);
        recyclerView.setLayoutManager(new LinearLayoutManager(getActivity().getBaseContext()));
        recyclerView.setHasFixedSize(true);
        recyclerView.setItemViewCacheSize(View.DRAWING_CACHE_QUALITY_HIGH);

        // Fill with test
        newsList.add(new NewsBlueprint("hi", "hi", "hi", "hi"));

        try {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    newsAdapter = new NewsAdapter(newsList);
                    getActivity().runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            recyclerView.setAdapter(newsAdapter);
                        }
                    });
                }
            }).start();
        } catch (Exception ex) {
            ex.printStackTrace();
        }

        getNews();

        return v;
    }

    public static News newInstance() {
        return new News();
    }

    public void getNews() {
        newsList.clear();
        progressBar.setVisibility(View.VISIBLE);
        String url = "I removed link for privacy on StackOverflow";

        RequestQueue mRequestQueue;
        Cache cache = new DiskBasedCache(getActivity().getCacheDir(), 1024 * 1024); // 1MB cap
        Network network = new BasicNetwork(new HurlStack());
        mRequestQueue = new RequestQueue(cache, network);
        mRequestQueue.start();

        JsonArrayRequest jsonArrayRequest = new JsonArrayRequest(Request.Method.GET, url, null, new Response.Listener<JSONArray>() {
            @Override
            public void onResponse(JSONArray response) {
                try {

                    JSONObject current = null;

                    for (int i = 0; i < response.length(); i++) {

                        current = response.getJSONObject(i);

                        // Get the topStory's title from the volume information
                        String topStoryTitle =  current.getJSONObject("title").getString("rendered");
                        // Get the current topStory's section information
                        // The appropriate section that the story belongs to is contained in the subsection
                        String topStorySection = current.getJSONObject("content").getString("rendered");

                        // Get the date on which the news was published
                        // Extract date information from the JSON response
                        String topStoryDateTime = current.getString("date");

                        // Get the story url
                        String storyUrl = current.getString("link");

                        JSONObject res = current.getJSONObject("_links");
                        JSONArray multimedia = res.getJSONArray("wp:featuredmedia");
                        String href = multimedia.getJSONObject(0).getString("href");

                        newsList.add(new NewsBlueprint(topStoryTitle, topStorySection, topStoryDateTime, href));


                        Toast.makeText(getContext(), "Looping " + topStoryTitle, Toast.LENGTH_SHORT).show();

                    }
                } catch (Exception ex) {
                    ex.printStackTrace();
                }


            }
        }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                Toast.makeText(getContext(), "Make sure you are connected to the internet", Toast.LENGTH_SHORT).show();
            }
        });

        mRequestQueue.add(jsonArrayRequest);
        newsAdapter.notifyDataSetChanged();


        progressBar.setVisibility(View.INVISIBLE);
    }
}

News Adapter

public class NewsAdapter extends RecyclerView.Adapter<NewsAdapter.MyViewHolder>{

    private List<NewsBlueprint> news;

    public class MyViewHolder extends RecyclerView.ViewHolder {
        public TextView title;
        public ImageView thumbnail;

        public MyViewHolder(View view) {
            super(view);

            title = view.findViewById(R.id.title);
            thumbnail = view.findViewById(R.id.thumbnail);
        }
    }

    public NewsAdapter(List<NewsBlueprint> newsList) {
        this.news = newsList;
    }

    @NonNull
    @Override
    public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View itemView = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.news_row, parent, false);

        return new MyViewHolder(itemView);
    }

    @Override
    public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
        NewsBlueprint newsBlueprint = news.get(position);
        holder.title.setText(newsBlueprint.getTitle());
        // Set thumbnail
        //Picasso.get().load(newsBlueprint.getImageURL()).into(holder.thumbnail);
    }

    public int getItemCount() {
        return news.size();
    }

    }

News Blueprint

Is just a class to represent a news item

The result is an empty RecyclerView

I made sure the data returned from the JSON is correct as I displayed the title of a news in a Toast.


Solution

  • newsAdapter.notifyDataSetChanged(); must be called after you've modified the newsList (jsonArrayRequest runs in the background, the function calling it continues directly without waiting for the response), not after you've started getting the news. Moreover, it's not a good idea to modify an array of an adapter from another Thread and it's totally wrong to create an Adapter on a background Thread.