News API Integration In Android App Java

News API Integration In Android App Java

📅 April 17, 2025 · ✍️ Al Saeed
Android Java Source Code

In today’s mobile-first world, delivering real-time content quickly and smoothly is crucial for user engagement. In this tutorial on News API Integration In Android App Java, you’ll learn how to fetch and display news articles in an Android app using Retrofit, RecyclerView, and ProgressBar, while implementing an infinite scroll feature — all written in Java.

By the end of this guide, you will have a working news app that fetches the latest articles based on a search query and loads more content as the user scrolls down.


🚀 Key Components We’ll Use

  • RecyclerView — To display news articles in a scrollable list.
  • ProgressBar — To show loading status while fetching data.
  • Retrofit — For efficient and safe API calls.
  • Infinite Scrolling — Load more news automatically when the user reaches the end.
  • Handler — To simulate slight loading delays during pagination.

🔧 Step-by-Step Implementation

1. Setting Up RecyclerView

First, we initialize our RecyclerView and set up a custom NewsAdapter to bind news articles dynamically.


private void setupRecyclerView() {
    recyclerView.setLayoutManager(new LinearLayoutManager(this));
    adapter = new NewsAdapter(this, listArticles, article -> {
        NewsDialogFragment dialogFragment = NewsDialogFragment.newInstance(article);
        dialogFragment.show(getSupportFragmentManager(), "NewsDialogFragment");
    });
    recyclerView.setAdapter(adapter);
}

Here, each news item is clickable and opens a DialogFragment to show article details.


2. Fetching News Using Retrofit

We use Retrofit to fetch news data asynchronously from an API. Here’s the main method:

🔔 Note: Make sure to replace API_KEY with your actual News API key to successfully fetch the news data.
You can get your API key from NewsAPI.org.

private void fetchNews(String query, int limit, int offset) {
    NewsApiService apiService = ApiClient.getRetrofitInstance().create(NewsApiService.class);

    Call<ArticlesResponse> call = apiService.getArticles(query, limit, offset, API_KEY);
    call.enqueue(new Callback<ArticlesResponse>() {
        @Override
        public void onResponse(@NonNull Call<ArticlesResponse> call, @NonNull Response<ArticlesResponse> response) {
            isLoading = false;
            adapter.removeLoadingFooter();
            if (response.isSuccessful() && response.body() != null) {
                List<ArticlesResponse.Article> newArticles = response.body().getArticles();
                if (newArticles != null && !newArticles.isEmpty()) {
                    int oldSize = listArticles.size();
                    listArticles.addAll(newArticles);
                    adapter.notifyItemRangeInserted(oldSize, newArticles.size());
                }
            }
            webProgress.setVisibility(View.GONE);
        }

        @Override
        public void onFailure(@NonNull Call<ArticlesResponse> call, @NonNull Throwable t) {
            webProgress.setVisibility(View.GONE);
            Toast.makeText(MainActivity.this, "Error: " + t.getMessage(), Toast.LENGTH_SHORT).show();
        }
    });
}

3. Adding Infinite Scroll (Load More News)

To enable infinite scrolling, we attach a scroll listener to the RecyclerView. When the user scrolls near the bottom, we load more news automatically.


recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
    @Override
    public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
        super.onScrolled(recyclerView, dx, dy);

        LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
        int totalItemCount = Objects.requireNonNull(layoutManager).getItemCount();
        int lastVisibleItem = layoutManager.findLastVisibleItemPosition();

        if (!isLoading && lastVisibleItem == totalItemCount - 10) {
            isLoading = true;
            adapter.addLoadingFooter();

            new Handler().postDelayed(() -> {
                currentOffset += PAGE_SIZE;
                fetchNews(QUERY, PAGE_SIZE, currentOffset);
            }, 1000); // simulate network delay
        }
    }
});

✅ This ensures a seamless user experience without manual refresh!


📱 Full MainActivity Java Code

For your reference, here’s the complete MainActivity.java:


public class MainActivity extends AppCompatActivity {
    private ProgressBar webProgress;
    private RecyclerView recyclerView;
    private NewsAdapter adapter;
    private List listArticles = new ArrayList<>();
    private boolean isLoading = false;
    private int currentOffset = 0;
    private final int PAGE_SIZE = 20;
    private final String QUERY = "bitcoin"; // or anything you use

    private static final String API_KEY = "API_KEY";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        webProgress = findViewById(R.id.web_progress);
        webProgress.setVisibility(VISIBLE);

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

        fetchNews(QUERY, PAGE_SIZE, currentOffset);
    }


    private void fetchNews(String query, int limit, int offset) {
        NewsApiService apiService = ApiClient.getRetrofitInstance().create(NewsApiService.class);

        Call call = apiService.getArticles(query, limit, offset, API_KEY);
        call.enqueue(new Callback() {
            @Override
            public void onResponse(@NonNull Call call, @NonNull Response response) {

                isLoading = false;
                adapter.removeLoadingFooter();

                if (response.isSuccessful() && response.body() != null) {
                    List newArticles = response.body().getArticles();

                    if (newArticles != null && !newArticles.isEmpty()) {
                        int oldSize = listArticles.size();
                        listArticles.addAll(newArticles);

                        adapter.notifyItemRangeInserted(oldSize, newArticles.size());
                    }

                    NewsLog.INSTANCE.d("onResponse", "News Loaded");

                }else {

                    NewsLog.INSTANCE.d("onResponse", "Failed to load news");

                }

                webProgress.setVisibility(View.GONE);

            }

            @Override
            public void onFailure(@NonNull Call call, @NonNull Throwable t) {
                webProgress.setVisibility(View.GONE);
                Toast.makeText(MainActivity.this, "Error: " + t.getMessage(), Toast.LENGTH_SHORT).show();
                NewsLog.INSTANCE.d("onFailure", t.getMessage());
            }
        });
    }


    private void setupRecyclerView(){

        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        adapter = new NewsAdapter(this, listArticles, article -> {
            // listen news click here, to open the news
            NewsDialogFragment dialogFragment = NewsDialogFragment.newInstance(article);
            dialogFragment.show(getSupportFragmentManager(), "NewsDialogFragment");

        });
        recyclerView.setAdapter(adapter);

        recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);

                LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
                int totalItemCount = Objects.requireNonNull(layoutManager).getItemCount();
                int lastVisibleItem = layoutManager.findLastVisibleItemPosition();

                if (!isLoading && lastVisibleItem == totalItemCount - 10) {
                    isLoading = true;
                    adapter.addLoadingFooter();

                    new Handler().postDelayed(() -> {
                        currentOffset += PAGE_SIZE;
                        fetchNews(QUERY, PAGE_SIZE, currentOffset);
                    }, 1000); // simulate delay

                }
            }
        });

    }

}

📱 Full NewsAdapter Java Code

For your reference, here’s the complete NewsAdapter.java:


public class NewsAdapter extends RecyclerView.Adapter {

    private static final int VIEW_TYPE_ITEM = 0;
    private static final int VIEW_TYPE_LOADING = 1;
    private List articles;
    private Context context;
    private boolean isLoadingAdded = false;
    private onNewsClickListener listener;

    public NewsAdapter(Context context, List articles, onNewsClickListener listener) {
        this.context = context;
        this.articles = articles;
        this.listener = listener;
    }

    @Override
    public int getItemViewType(int position) {
        return (position == articles.size() && isLoadingAdded) ? VIEW_TYPE_LOADING : VIEW_TYPE_ITEM;
    }

    @Override
    public int getItemCount() {
        return articles.size() + (isLoadingAdded ? 1 : 0);
    }

    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        if (viewType == VIEW_TYPE_ITEM) {
            View view = LayoutInflater.from(context).inflate(R.layout.item_article, parent, false);
            return new ArticleViewHolder(view);
        } else {
            View view = LayoutInflater.from(context).inflate(R.layout.item_loading, parent, false);
            return new LoadingViewHolder(view);
        }
    }

    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
        if (getItemViewType(position) == VIEW_TYPE_ITEM) {
            ArticleViewHolder viewHolder = (ArticleViewHolder) holder;
            ArticlesResponse.Article article = articles.get(position);
            viewHolder.title.setText(article.getTitle());
            viewHolder.description.setText(article.getDescription());
            // Add image loading if needed

            Glide.with(context)
                    .load(article.getFeedImage())  // Image URL or resource
                    .apply(new RequestOptions().placeholder(R.drawable.place_holder)
                          //  .error(R.drawable.error_image)
                    )
                    .into(((ArticleViewHolder) holder).imageView);  // The ImageView where the image will be loaded

            holder.itemView.setOnClickListener(v -> {
                int position1 = holder.getAdapterPosition();
                if(listener != null && position1 != RecyclerView.NO_POSITION){
                    listener.onClick(article);
                }
            });

        }
    }

    public void addLoadingFooter() {
        isLoadingAdded = true;
        notifyItemInserted(articles.size());
    }

    public void removeLoadingFooter() {
        if (isLoadingAdded) {
            isLoadingAdded = false;
            notifyItemRemoved(articles.size());
        }
    }

    static class ArticleViewHolder extends RecyclerView.ViewHolder {
        TextView title, description;
        ImageView imageView;

        public ArticleViewHolder(View itemView) {
            super(itemView);
            title = itemView.findViewById(R.id.article_title);
            description = itemView.findViewById(R.id.article_description);
            imageView = itemView.findViewById(R.id.iv_news);
        }
    }

    static class LoadingViewHolder extends RecyclerView.ViewHolder {
        public LoadingViewHolder(View itemView) {
            super(itemView);
        }
    }
}



Download Source Code

🎯 Final Thoughts

By combining Retrofit, RecyclerView, and ProgressBar, we’ve built a highly efficient, real-time news app that supports infinite scrolling in Android using Java.

This pattern is extremely useful for creating news apps, social media feeds, blogs, and any app that requires real-time content loading.

Pro Tip: Always handle error cases smartly (like no internet, API limit errors) to enhance user experience even further.