Open In App

How to Build a Stock Market News Android App using Retrofit?

Last Updated : 22 Mar, 2023
Improve
Improve
Like Article
Like
Save
Share
Report

Building a stock market news app can be a great way to stay informed about the latest developments in the financial world. This article will explore how to build a simple news app using Kotlin and Retrofit. A sample video is given below to get an idea about what we are going to do in this article.

Step-by-Step Implementation:

Step 1: First, let’s start by setting up a new project in Android Studio. Make sure to select the “Empty Activity” template and choose Kotlin as the language. Once the project is set up, we can start adding the necessary dependencies to our app.

implementation 'com.squareup.retrofit2:retrofit:2.9.0'

Step 2: In this example, we are using the GET method to retrieve the top headlines from a news API. We are passing in the country and API key as query parameters.

Kotlin




interface FinanceApi  {
    @GET("news/all")
    fun getAllDetails(@Query("exchanges") exchanges: String , @Query("api_token")  api_token : String) : Call<News>
}


Java




public interface FinanceApi {
    @GET("news/all")
    Call<News> getAllDetails(@Query("exchanges") String exchanges, @Query("api_token") String api_token);
}


Step 3: Now that we have defined our endpoint, we can use Retrofit to create an instance of our NewsAPI interface.

Kotlin




object RetrofitInstance  {
    val api : FinanceApi by  lazy {
        Retrofit.Builder()
            .baseUrl("https://api.marketaux.com/v1/")
            .addConverterFactory(GsonConverterFactory.create())
            .build()
            .create(FinanceApi::class.java)
    }
}


Java




public class RetrofitInstance {
    private static FinanceApi apiInstance;
 
    public static synchronized FinanceApi getApi() {
        if (apiInstance == null) {
            Retrofit retrofit = new Retrofit.Builder()
                    .baseUrl("https://api.marketaux.com/v1/")
                    .addConverterFactory(GsonConverterFactory.create())
                    .build();
            apiInstance = retrofit.create(FinanceApi.class);
        }
        return apiInstance;
    }
}


Step 4: Generate Data Classes using the json-kotlin converter plugin

Kotlin




data class News(
    val `data`: List<Data>,
    val meta: Meta
)
data class Data(
    val description: String,
    val entities: List<Entity>,
    val image_url: String,
    val keywords: String,
    val language: String,
    val published_at: String,
    val relevance_score: Any,
    val similar: List<Similar>,
    val snippet: String,
    val source: String,
    val title: String,
    val url: String,
    val uuid: String
)


Java




import java.util.List;
 
public class News {
    private List<Data> data;
    private Meta meta;
     
    public List<Data> getData() {
        return data;
    }
     
    public void setData(List<Data> data) {
        this.data = data;
    }
     
    public Meta getMeta() {
        return meta;
    }
     
    public void setMeta(Meta meta) {
        this.meta = meta;
    }
}
 
public class Data {
    private String description;
    private List<Entity> entities;
    private String image_url;
    private String keywords;
    private String language;
    private String published_at;
    private Object relevance_score;
    private List<Similar> similar;
    private String snippet;
    private String source;
    private String title;
    private String url;
    private String uuid;
     
    public String getDescription() {
        return description;
    }
     
    public void setDescription(String description) {
        this.description = description;
    }
     
    public List<Entity> getEntities() {
        return entities;
    }
     
    public void setEntities(List<Entity> entities) {
        this.entities = entities;
    }
     
    public String getImage_url() {
        return image_url;
    }
     
    public void setImage_url(String image_url) {
        this.image_url = image_url;
    }
     
    public String getKeywords() {
        return keywords;
    }
     
    public void setKeywords(String keywords) {
        this.keywords = keywords;
    }
     
    public String getLanguage() {
        return language;
    }
     
    public void setLanguage(String language) {
        this.language = language;
    }
     
    public String getPublished_at() {
        return published_at;
    }
     
    public void setPublished_at(String published_at) {
        this.published_at = published_at;
    }
     
    public Object getRelevance_score() {
        return relevance_score;
    }
     
    public void setRelevance_score(Object relevance_score) {
        this.relevance_score = relevance_score;
    }
     
    public List<Similar> getSimilar() {
        return similar;
    }
     
    public void setSimilar(List<Similar> similar) {
        this.similar = similar;
    }
     
    public String getSnippet() {
        return snippet;
    }
     
    public void setSnippet(String snippet) {
        this.snippet = snippet;
    }
     
    public String getSource() {
        return source;
    }
     
    public void setSource(String source) {
        this.source = source;
    }
     
    public String getTitle() {
        return title;
    }
     
    public void setTitle(String title) {
        this.title = title;
    }
     
    public String getUrl() {
        return url;
    }
     
    public void setUrl(String url) {
        this.url = url;
    }
     
    public String getUuid() {
        return uuid;
    }
     
    public void setUuid(String uuid) {
        this.uuid = uuid;
    }
}
 
public class Entity {
    // Entity fields and methods
}
 
public class Meta {
    // Meta fields and methods
}
 
public class Similar {
    // Similar fields and methods
}


Design UI using XML

Step 5: activity_main.xml file

XML




<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">
 
    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:layout_editor_absoluteX="1dp" />
   
</androidx.constraintlayout.widget.ConstraintLayout>


news_layout.xml

XML




<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="300dp"
    android:layout_margin="12dp"
    android:elevation="2dp"
    app:cardCornerRadius="15dp">
   
    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
 
    <ImageView
        android:id="@+id/imageView_news"
        android:layout_width="wrap_content"
        android:layout_height="180dp"
        android:scaleType="centerCrop"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        tools:srcCompat="@tools:sample/backgrounds/scenic" />
 
    <TextView
        android:id="@+id/textView_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:layout_marginLeft="16dp"
        android:text="Sachetitistion of cigaretts?"
        android:textSize="20dp"
        android:textColor="@color/black"
        android:textStyle="bold"
        android:layout_marginTop="5dp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/imageView_news"/>
       
        <TextView
            android:id="@+id/textView_description"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="16dp"
            android:layout_marginLeft="16dp"
            android:text="An explainer on how sachet cigarettes incentivize smoking"
            android:textSize="20dp"
            android:layout_marginTop="8dp"
            android:textColor="@color/black"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@id/textView_title"/>
 
    </androidx.constraintlayout.widget.ConstraintLayout>
 
</androidx.cardview.widget.CardView>


activity_view_news

XML




<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".ViewNews">
 
    <WebView
        android:id="@+id/webView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
   
</androidx.constraintlayout.widget.ConstraintLayout>


Step 6: Create an Adapter class For RecyclerView. In this, we will be setting up an on click listener so that we can open news URL in a new activity.

Finance Adapter

Kotlin




package com.sangyan.financeapp
 
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.sangyan.financeapp.databinding.NewsLayoutBinding
 
class FinanceAdapter : RecyclerView.Adapter<FinanceAdapter.ViewHolder>() {
 
// A list of Data objects that will
// be displayed in the RecyclerView
var list = ArrayList<Data>()
 
// A function to update the list of Data objects
fun setData(list : List<Data>){
    // Shuffling the list of Data objects
    list.shuffled()
    // Assigning the new list to the class variable
    this.list = list as ArrayList<Data>
    // Notifying the adapter that
    // the data has been updated
    notifyDataSetChanged()
}
 
// The ViewHolder class holds the view of
// a single item in the RecyclerView
class ViewHolder(val binding : NewsLayoutBinding)  : RecyclerView.ViewHolder(binding.root){}
 
// The onCreateViewHolder function inflates the layout
// of the item view and returns a ViewHolder object
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
    // Inflating the layout of the item view using the NewsLayoutBinding
    val binding = NewsLayoutBinding.inflate(LayoutInflater.from(parent.context), parent, false)
    // Returning a ViewHolder object
    // with the inflated layout
    return ViewHolder(binding)
}
 
// The onBindViewHolder function binds the Data object
// at a particular position to the view in the ViewHolder
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
    // Getting the Data object at the current position
    val data = list[position]
    // Setting the title of the news article
    holder.binding.newsTitle.text = data.title
     
    // Setting the description of the news article
    holder.binding.newsDescription.text = data.description
     
    // Setting the source of the news article
    holder.binding.newsSource.text = data.source.name
     
    // Setting the time when the news article was published
    holder.binding.newsTime.text = data.publishedAt
 
    // Loading the image of the news article using Glide library
    Glide.with(holder.itemView)
            .load(data.urlToImage)
            .into(holder.binding.newsImage)
 
    // Setting an onClickListener on the item view of the RecyclerView
    holder.itemView.setOnClickListener {
        // Creating an intent to open the URL
        // of the news article in a browser
        val intent = Intent(Intent.ACTION_VIEW)
        intent.data = Uri.parse(data.url)
        // Starting the activity to open the URL in a browser
        holder.itemView.context.startActivity(intent)
    }
}
 
// The getItemCount function returns the
// number of items in the RecyclerView
override fun getItemCount(): Int {
    return list.size
}
 
}


Java




package com.sangyan.financeapp;
 
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import com.sangyan.financeapp.databinding.NewsLayoutBinding;
import java.util.ArrayList;
import java.util.List;
 
public class FinanceAdapter
    extends RecyclerView
                .Adapter<FinanceAdapter.ViewHolder> {
    // A list of Data objects that will
    // be displayed in the RecyclerView
    private ArrayList<Data> list = new ArrayList<>();
 
    // A function to update the list of Data objects
    public void setData(List<Data> list)
    {
        // Shuffling the list of Data objects
        java.util.Collections.shuffle(list);
        // Assigning the new list to the class variable
        this.list = new ArrayList<>(list);
        // Notifying the adapter that
        // the data has been updated
        notifyDataSetChanged();
    }
 
    // The ViewHolder class holds the view of
    // a single item in the RecyclerView
    public static class ViewHolder
        extends RecyclerView.ViewHolder {
        NewsLayoutBinding binding;
 
        public ViewHolder(NewsLayoutBinding binding)
        {
            super(binding.getRoot());
            this.binding = binding;
        }
    }
 
    // The onCreateViewHolder function inflates the layout
    // of the item view and returns a ViewHolder object
    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent,
                                         int viewType)
    {
        // Inflating the layout of the item view using the
        // NewsLayoutBinding
        NewsLayoutBinding binding
            = NewsLayoutBinding.inflate(
                LayoutInflater.from(parent.getContext()),
                parent, false);
        // Returning a ViewHolder object
        // with the inflated layout
        return new ViewHolder(binding);
    }
 
    // The onBindViewHolder function binds the Data object
    // at a particular position to the view in the
    // ViewHolder
    @Override
    public void onBindViewHolder(ViewHolder holder,
                                 int position)
    {
        // Getting the Data object at the current position
        Data data = list.get(position);
        // Setting the title of the news article
        holder.binding.newsTitle.setText(data.getTitle());
 
        // Setting the description of the news article
        holder.binding.newsDescription.setText(
            data.getDescription());
 
        // Setting the source of the news article
        holder.binding.newsSource.setText(
            data.getSource().getName());
 
        // Setting the time when the news article was
        // published
        holder.binding.newsTime.setText(
            data.getPublishedAt());
 
        // Loading the image of the news article using Glide
        // library
        Glide.with(holder.itemView)
            .load(data.getUrlToImage())
            .into(holder.binding.newsImage);
 
        // Setting an onClickListener on the item view of
        // the RecyclerView
        holder.itemView.setOnClickListener(
            new View.OnClickListener() {
                @Override public void onClick(View v)
                {
                    // Creating an intent to open the URL
                    // of the news article in a browser
                    Intent intent
                        = new Intent(Intent.ACTION_VIEW);
                    intent.setData(
                        Uri.parse(data.getUrl()));
                    // Starting the activity to open the URL
                    // in a browser
                    holder.itemView.getContext()
                        .startActivity(intent);
                }
            });
    }
 
    // The getItemCount function returns the
    // number of items in the RecyclerView
    @Override public int getItemCount()
    {
        return list.size();
    }
}


Step 7: Write code for the main activity which represents the UI. In MainActivity we will be creating an intent to pass data in the new ViewNews activity

Kotlin




package com.sangyan.financeapp
 
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import androidx.recyclerview.widget.LinearLayoutManager
import com.sangyan.financeapp.databinding.ActivityMainBinding
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
 
class MainActivity : AppCompatActivity() {
     
// Initializing variables
private lateinit var binding : ActivityMainBinding
private lateinit var myAdapter : FinanceAdapter
 
// A companion object that holds a
// constant for the URL of the news article
companion object {
    const val URL = "News URL"
}
 
// The onCreate function is called when the activity is created
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
 
    // Inflating the layout of the activity
    // using the ActivityMainBinding
    binding = ActivityMainBinding.inflate(layoutInflater)
    setContentView(binding.root)
 
    // Initializing the adapter
    // for the RecyclerView
    myAdapter = FinanceAdapter()
 
    // Setting the layout manager and adapter for the RecyclerView
    binding.recyclerView.layoutManager = LinearLayoutManager(applicationContext)
    binding.recyclerView.adapter = myAdapter
 
    // Setting a click listener on the items in the RecyclerView
    myAdapter.setNewsItemClickListener(object  : FinanceAdapter.SetNewsItemClickListener{
        override fun setNewsItemClickListener(data: Data) {
            // Creating an intent to open the URL of
            // the news article in a new activity
            val intent = Intent(applicationContext,ViewNews::class.java)
            intent.putExtra(URL,data.url)
            // Starting the new activity
            startActivity(intent)
        }
 
    })
 
    // Making a network request to get the news data
    RetrofitInstance.api.getAllDetails("BSE","ixxnnaiVNGyR2StAS2Ny51vRubP7VMlONNJ7ZSwl").enqueue(object  : Callback<News>{
       override fun onResponse(call: Call<News>, response: Response<News>) {
            // Checking if the response body is not null
            if (response.body()!=null){
                // Updating the data in the RecyclerView adapter
                myAdapter.setData(response.body()!!.data)
 
                // Logging the entities of the news data
                for (i in response.body()!!.data){
                    Log.d("TAG", i.entities.toString())
                }
            }
           else{
               // Returning if the response body is null
               return
            }
 
       }
 
       // Handling the case when the network request fails
       override fun onFailure(call: Call<News>, t: Throwable) {
           Log.d("TAG",t.message.toString() )
       }
 
   })
}
 
}


Java




package com.sangyan.financeapp;
 
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import com.sangyan.financeapp.databinding.ActivityMainBinding;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
 
public class MainActivity extends AppCompatActivity {
    // Initializing variables
    private ActivityMainBinding binding;
    private FinanceAdapter myAdapter;
 
    // A companion object that holds a
    // constant for the URL of the news article
    public static final String URL = "News URL";
 
    // The onCreate function is called when the activity is
    // created
    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
 
        // Inflating the layout of the activity
        // using the ActivityMainBinding
        binding = ActivityMainBinding.inflate(
            getLayoutInflater());
        setContentView(binding.getRoot());
 
        // Initializing the adapter
        // for the RecyclerView
        myAdapter = new FinanceAdapter();
 
        // Setting the layout manager and adapter for the
        // RecyclerView
        binding.recyclerView.setLayoutManager(
            new LinearLayoutManager(
                getApplicationContext()));
        binding.recyclerView.setAdapter(myAdapter);
 
        // Setting a click listener on the items in the
        // RecyclerView
        myAdapter.setNewsItemClickListener(
            new FinanceAdapter.SetNewsItemClickListener() {
                @Override
                public void setNewsItemClickListener(
                    Data data)
                {
                    // Creating an intent to open the URL of
                    // the news article in a new activity
                    Intent intent = new Intent(
                        getApplicationContext(),
                        ViewNews.class);
                    intent.putExtra(URL, data.getUrl());
                    // Starting the new activity
                    startActivity(intent);
                }
            });
 
        // Making a network request to get the news data
        RetrofitInstance.getApi()
            .getAllDetails(
                "BSE",
                "ixxnnaiVNGyR2StAS2Ny51vRubP7VMlONNJ7ZSwl")
            .enqueue(new Callback<News>() {
                @Override
                public void onResponse(
                    Call<News> call,
                    Response<News> response)
                {
                    // Checking if the response body is not
                    // null
                    if (response.body() != null) {
                        // Updating the data in the
                        // RecyclerView adapter
                        myAdapter.setData(
                            response.body().getData());
 
                        // Logging the entities of the news
                        // data
                        for (Data i :
                             response.body().getData()) {
                            Log.d(
                                "TAG",
                                i.getEntities().toString());
                        }
                    }
                    else {
                        // Returning if the response body is
                        // null
                        return;
                    }
                }
 
                // Handling the case when the network
                // request fails
                @Override
                public void onFailure(Call<News> call,
                                      Throwable t)
                {
                    Log.d("TAG", t.getMessage().toString());
                }
            });
    }
}


Step 8: Write code for ViewNews activity in which we will display news in WebView.

Kotlin




package com.sangyan.financeapp
 
import android.os.Binder
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import com.sangyan.financeapp.databinding.ActivityViewNewsBinding
 
class ViewNews : AppCompatActivity() {
    // Initializing variables
    private lateinit var newsUrl : String
    private lateinit var binding:  ActivityViewNewsBinding
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
         
        // Inflating the layout of the activity
       // using the ActivityViewNewsBinding
        binding = ActivityViewNewsBinding.inflate(layoutInflater)
        setContentView(binding.root)
 
        // Getting the news URL from the intent sent by MainActivity
        newsUrl = intent.getStringExtra(MainActivity.URL).toString()
 
        // Loading the news article in the webview
        binding.webView.loadUrl(newsUrl)
    }
}


Java




package com.sangyan.financeapp;
 
import android.content.Intent;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import com.sangyan.financeapp.databinding.ActivityViewNewsBinding;
 
public class ViewNews extends AppCompatActivity {
    // Initializing variables
    private String newsUrl;
    private ActivityViewNewsBinding binding;
    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
 
        // Inflating the layout of the activity
        // using the ActivityViewNewsBinding
        binding = ActivityViewNewsBinding.inflate(
            getLayoutInflater());
        setContentView(binding.getRoot());
 
        // Getting the news URL from the intent sent by
        // MainActivity
        newsUrl = getIntent().getStringExtra(
            MainActivity.Companion.getURL());
 
        // Loading the news article in the webview
        binding.webView.loadUrl(newsUrl);
    }
}


Output:



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads