skip to Main Content

I have a News app where I have used Retrofit to get data from API. I have shown news with cardView and Recycler view. Now I want to implement the the next step where I want if a user click on a card the whole news should display on a new activity. But to implement this I don’t have the proper idea that how to do this. Here I am providing my MainActivity , the adapter class and the interface for retrofit. I am looking for the help to implement the next step.

MainActivity.kt

class MainActivity : AppCompatActivity() {
    lateinit var adapter: NewsAdapter
   
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        getNews()
    }

    private fun getNews() {
        val news :Call<News> = NewsSeversis.newsIntence.getHeadlines("in",1)
        
        news.enqueue(object :Callback<News>{
            override fun onResponse(call: Call<News>, response: Response<News>) {
                //here response is a News response
                val news:News?=response.body()
                if (news!=null){
                    Log.d("response",news.toString())
                    adapter= NewsAdapter(news.articles)
                    newsList.adapter=adapter
                    newsList.layoutManager=LinearLayoutManager(this@MainActivity) 
                }
            }
            override fun onFailure(call: Call<News>, t: Throwable) {
                Log.d("response","error occur",t)
            }
        })
    } 
}

Adapter class for recycler view

class NewsAdapter (val artical: List<Artical>):RecyclerView.Adapter<NewsAdapter.articalViewHolder>(){
   inner class articalViewHolder(itemView: View):RecyclerView.ViewHolder(itemView)

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): articalViewHolder {
        val view:View=LayoutInflater.from(parent.context).inflate(R.layout.item_layout,parent,false)
        return articalViewHolder(view)
    }

    override fun onBindViewHolder(holder: articalViewHolder, position: Int) {
        val articals:Artical=artical[position]
        holder.itemView.apply {
            newsTitle.text=articals.title
            newsDescription.text=articals.description
            Glide.with(context).load(articals.urlToImage).into(newsImage)

        }
    }

    override fun getItemCount(): Int {
       return artical.size
    }
}

interface class

const val BASE_URL="https://newsapi.org/"
const val API_KEY="b461fea21cc242458825d1be951ebe1c"
 interface NewsInterface{
    
  @GET("v2/top-headlines?apiKey=$API_KEY")//@GET is to tell the it is a get request
    
  fun getHeadlines(@Query("country")country:String, @Query("page") page:Int) : Call<News> 
  
 }
object NewsSeversis {
   
    val newsIntence:NewsInterface
    init {
        val retrofit=Retrofit.Builder()
            .baseUrl(BASE_URL)
            .addConverterFactory(GsonConverterFactory.create())
            .build()
       
        newsIntence=retrofit.create(NewsInterface::class.java)
    }

}

layout for every card view

<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_margin="16dp"
    app:cardUseCompatPadding="true"
    app:cardCornerRadius="8dp"
    app:cardElevation="4dp">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#F1F1F1"
        android:padding="16dp">

        <ImageView
            android:id="@+id/newsImage"
            android:layout_width="match_parent"
            android:layout_height="180dp"
            android:scaleType="centerCrop"
            android:src="@drawable/ic_launcher_background"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <TextView
            android:id="@+id/newsTitle"
            style="@style/TextAppearance.AppCompat.Headline"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:ems="10"
            android:maxLines="2"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            android:layout_marginTop="8dp"
            android:text="Title"
            app:layout_constraintTop_toBottomOf="@+id/newsImage" />

        <TextView
            android:id="@+id/newsDescription"
            style="@style/TextAppearance.AppCompat.Body1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:ems="10"
            android:maxLines="2"
            android:text="Description"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            android:layout_marginTop="8dp"
            app:layout_constraintTop_toBottomOf="@+id/newsTitle" />

    </androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>

main activity layout

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    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/newsList"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

Along with those I have a Artical and a News class with constructor and a Whole new activity for display the whole news on that activity

News class

ata class News(
    val totalResults:Int,
    val articles:List<Artical>
    )

Artical class

data class Artical(
    val author:String,
    val title:String,
    val description:String,
    val url:String,
    val urlToImage:String)

2

Answers


  1. Following are the steps to get callback to activity of an item click from adapter.

    In your NewsAdapter class use a HigherOrder Function to get call back of item click.

    1. First Update the NewsAdapter

    class NewsAdapter (
    val artical: List<Artical>,
    private val onItemClicked: () -> Unit
                    ):RecyclerView.Adapter<NewsAdapter.articalViewHolder>(){
                        // your existing code 
                 override fun onBindViewHolder(holder: articalViewHolder, position: Int) {
                        val articals:Artical=artical[position]
                        holder.itemView.apply {
                            newsTitle.text=articals.title
                            newsDescription.text=articals.description
                            Glide.with(context).load(articals.urlToImage).into(newsImage)
        // ADD THIS LINE 
            setOnClickListener { onItemClicked.invoke() } 
                
                        }
                    }
    

    2. Now update the code in MainActivity where you are creating object of NewsAdapter.

    // EXISTING CODE
    adapter= NewsAdapter(news.articles){
        // CODE TO LAUNCH THE NEW ACTIVITY       
        }
    // EXISTING CODE
    

    Feel free to ask if you have any queries.

    Login or Signup to reply.
  2. To Identify which item was clicked we need to have an interface from which can get us the callback from Adapter to Activity, so create a interface class in Adapter. (In your case you can create it in last second line of NewsAdapter)

    interface AdapterCallback {
        fun onClick(artical: Artical?)
    }
    

    Then we need to take an oject of this interface in Adapter’s Constructor

    class NewsAdapter (val artical: List<Artical>, callback: AdapterCallback): ...
    

    Then we have to use callback object on click of view(s). (In your case do the following in adapter class’s onBindViewHolder)

    holder.itemView.setOnClickListener {
        callback.onClick(articals)
    }
    

    Now we need to listen this callback in our Activity, so we need create object in Activity in the following way

    private val adapterCallback: AdapterCallback =
            object : AdapterCallback {
                override fun onClick(artical: Artical?) {
                    // Code here to navigate to desired Activity by passing required data in the intent (Example below)
                    val intent = Intent(this, DestinationActivity::class.java)
                    intent.putExtra("news_artical", artical)
                    startActivity(intent)
                }
        }
    

    Now as we have object of AdapterCallback in our Activity, we need to pass it to Adapter

    adapter = NewsAdapter(news.articles, adapterCallback)
    

    Note: You need to make Artical Class Parcelable

    @Parcelize
    data class Artical(
        val author: String,
        val title: String,
        val description: String,
        val url: String,
        val urlToImage: String
    ): Parcelable
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search