Android Recyclerview is not new to us but Kotlin programming Language is. In fact, we’ve already discussed in our previous article about RecyclerView and CardView Implementation creating a very interesting project “SongBoard” where we’ve created recyclerview and cardview’s implementation and had seen different implementations for Linear, Grid, Staggered Grid and Horizontal implementations of RecyclerView Android.
In this tutorial of Android RecyclerView using Kotlin, we will be focusing on how to create a RecyclerView and declaring RecyclerAdapter using Kotlin Programming language by creating another special project called “News Board”.
NewsBoard is currently being developed by coderefer using the current popular technologies like Data Binding, Kotlin Programming Language, MVVM design Pattern, ViewModel, etc. The link to that project was given at the bottom of this article.
[adinserter block=”3″]
Android RecyclerView and its WhereAbouts:
Although we have discussed about RecyclerView and its whereabouts in our Previous article, in brief, Recyclerview in Android is the advanced version of Listview to efficiently recycle the views and reuse them.
Lets start implementing RecyclerView in our NewsBoard Project.
Implementation of RecyclerView in our Project:
1. Adding required Dependencies:
Firstly, we need to import RecyclerView in order to use it. Here we will also be importing CardView to effectively show the news articles in NewsBoard. To import RecyclerView and CardView, we need to add the following dependencies:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
dependencies { | |
… | |
//adding a recyclerview | |
compile "com.android.support:recyclerview-v7:26.1.0" | |
//adding a cardview | |
compile "com.android.support:cardview-v7:26.1.0" | |
… | |
} |
[adinserter block=”3″]
2. Adding RecyclerView to our Layout file:
Now that we have our required dependencies, we will be adding RecyclerView to our Project by adding the following code to our Layout File in which we would like to add the list of News:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
… | |
<android.support.v7.widget.RecyclerView | |
android:id="@+id/recyclerView" | |
android:layout_width="match_parent" | |
android:layout_height="match_parent" | |
android:scrollbars="vertical"/> | |
… |
3. Designing List Item to display each news item:
Now that we’ve added the recyclerview to XML file, let us design a layout for news item, which we would be using to populate our RecyclerView.
Here is the xml code for the News item used in NewsBoard App:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?> | |
<layout | |
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" | |
tools:context="com.coderefer.newsboard.MainActivity" | |
> | |
<android.support.v7.widget.CardView | |
android:layout_width="match_parent" | |
android:layout_height="wrap_content" | |
android:layout_margin="10dp" | |
app:cardCornerRadius="4dp" | |
app:cardElevation="4dp"> | |
<android.support.constraint.ConstraintLayout | |
android:layout_width="match_parent" | |
android:layout_height="match_parent" | |
android:orientation="vertical" | |
app:layout_constraintBottom_toBottomOf="parent" | |
app:layout_constraintLeft_toLeftOf="parent" | |
app:layout_constraintRight_toRightOf="parent" | |
app:layout_constraintTop_toTopOf="parent"> | |
<ImageView | |
android:id="@+id/iv_cover" | |
android:layout_width="match_parent" | |
android:layout_height="180dp" | |
android:scaleType="centerCrop" | |
app:layout_constraintHorizontal_bias="1.0" | |
app:layout_constraintLeft_toLeftOf="parent" | |
app:layout_constraintRight_toRightOf="parent" | |
app:layout_constraintTop_toTopOf="parent" | |
app:srcCompat="@drawable/img_not_found"/> | |
<TextView | |
android:id="@+id/tv_source" | |
android:layout_width="wrap_content" | |
android:layout_height="wrap_content" | |
android:text="TechVersus" | |
app:layout_constraintBottom_toBottomOf="@+id/iv_source" | |
app:layout_constraintLeft_toRightOf="@+id/iv_source" | |
app:layout_constraintTop_toTopOf="@+id/iv_source"/> | |
<android.support.v7.widget.AppCompatImageView | |
android:id="@+id/imageView3" | |
android:layout_width="wrap_content" | |
android:layout_height="wrap_content" | |
android:layout_marginBottom="8dp" | |
android:layout_marginLeft="8dp" | |
android:layout_marginRight="8dp" | |
android:layout_marginStart="32dp" | |
android:layout_marginTop="8dp" | |
app:layout_constraintBottom_toBottomOf="@+id/iv_source" | |
app:layout_constraintHorizontal_bias="0.66" | |
app:layout_constraintLeft_toLeftOf="parent" | |
app:layout_constraintRight_toRightOf="parent" | |
app:layout_constraintTop_toTopOf="@+id/iv_source" | |
app:srcCompat="@drawable/time"/> | |
<TextView | |
android:id="@+id/tv_time" | |
android:layout_width="wrap_content" | |
android:layout_height="wrap_content" | |
android:layout_marginLeft="8dp" | |
android:layout_marginStart="8dp" | |
android:text="3 days ago" | |
app:layout_constraintBaseline_toBaselineOf="@+id/tv_source" | |
app:layout_constraintLeft_toRightOf="@+id/imageView3"/> | |
<ImageView | |
android:id="@+id/iv_source" | |
android:layout_width="wrap_content" | |
android:layout_height="wrap_content" | |
android:layout_marginBottom="8dp" | |
android:layout_marginEnd="16dp" | |
android:layout_marginLeft="8dp" | |
android:layout_marginRight="16dp" | |
android:layout_marginStart="8dp" | |
android:layout_marginTop="8dp" | |
android:padding="5dp" | |
app:layout_constraintBottom_toBottomOf="parent" | |
app:layout_constraintHorizontal_bias="0.0" | |
app:layout_constraintLeft_toLeftOf="parent" | |
app:layout_constraintRight_toRightOf="parent" | |
app:layout_constraintTop_toBottomOf="@+id/iv_cover" | |
app:layout_constraintVertical_bias="0.0" | |
app:srcCompat="@drawable/globe"/> | |
<TextView | |
android:id="@+id/tv_heading" | |
android:layout_width="0dp" | |
android:layout_height="wrap_content" | |
android:background="@drawable/black_transparent" | |
android:gravity="start" | |
android:padding="5dp" | |
android:text="IPhone 7 Launched in India" | |
android:textAlignment="textStart" | |
android:textColor="@android:color/white" | |
android:textSize="15sp" | |
app:layout_constraintBottom_toBottomOf="@+id/iv_cover" | |
app:layout_constraintLeft_toLeftOf="@+id/iv_cover" | |
app:layout_constraintRight_toRightOf="@+id/iv_cover"/> | |
</android.support.constraint.ConstraintLayout> | |
</android.support.v7.widget.CardView> | |
</layout> |
[adinserter block=”3″]
If you’ve Noticed, we are using Android Data Binding in the above xml file, whose implementation is explained in our previous article. We’ll only focus on Android Recycler View implementation for this article.
All the icons used above will be available in the project whose link is given at the bottom of this article.
4. Creating a Model:
Next we would be creating a Model class to model each and every news item that we would be displaying in our NewsBoard.
Here is the simplified form of Model class in Kotlin:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.util.Date | |
data class News( | |
val id:Int=0, | |
val news_title:String?=null, | |
var news_detail: String? = null, | |
var news_image_url: String? = null, | |
var news_url: String? = null, | |
var news_source: String? = null, | |
var pub_date: Date? = null | |
) |
We also used a data class in Kotlin. Data classes helps compiler to automatically derive the functions such as toString(), equals(), hashCode() and copy().
You can also see that we haven’t declared getters and setters.
In Kotlin, getters and setters are optional and are auto-generated if you do not declare them in your program.
[adinserter block=”3″]
5. Adding an Adapter:
Next, we will be adding an adapter class. In brief, adapter class acts as a bridge to bind the data to the recyclerview.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.coderefer.newsboard | |
import android.support.v7.widget.RecyclerView | |
import android.util.Log | |
import android.view.View | |
import android.view.ViewGroup | |
import com.squareup.picasso.Picasso | |
import kotlinx.android.synthetic.main.news_item.view.* | |
/** | |
* Created by vamsitallapudi on 16/01/18. | |
*/ | |
class NewsRecyclerAdapter(private val news: ArrayList<News>): RecyclerView.Adapter<NewsRecyclerAdapter.NewsHolder>() { | |
override fun getItemCount(): Int { | |
return news.size | |
} | |
override fun onBindViewHolder(holder: NewsRecyclerAdapter.NewsHolder, position: Int) { | |
val itemNews = news[position] | |
holder.bindNews(itemNews) | |
} | |
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NewsRecyclerAdapter.NewsHolder{ | |
val inflatedView = parent.inflate(R.layout.news_item) | |
return NewsHolder(inflatedView) | |
} | |
class NewsHolder(v:View) : RecyclerView.ViewHolder(v) , View.OnClickListener { | |
private var view : View = v | |
private var news : News? = null | |
init { | |
v.setOnClickListener {this} | |
} | |
override fun onClick(v: View?) { | |
Log.d("RecyclerView", "CLICK!") | |
} | |
fun bindNews(news: News) { | |
this.news = news | |
Picasso.with(view.context).load(news.news_image_url).into(view.iv_cover) | |
view.tv_heading.text = news.news_title | |
view.tv_source.text = news.news_source | |
} | |
} | |
} |
In the above code, at line 25, you might have noticed that i’ve used inflate() on ViewGroup. That is an Extension Function written to extend the existing class and to write our own code. Extension Functions are powerful feature in Kotlin. In fact, Google has just announced Android ktx which provides bunch of extensions useful for Android Development as a library. Here is the code from the file where we would be declaring all the required extension functions for our project.
[adinserter block=”3″]
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
fun ViewGroup.inflate(@LayoutRes layoutRes: Int, attachToRoot: Boolean = false): View { | |
return LayoutInflater.from(context).inflate(layoutRes,this,attachToRoot) | |
} |
6. Modifying our Activity / Fragment/s code to show RecyclerView:
The last step is to add the following code to your activity / fragment accordingly to show the data in our recyclerview:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.coderefer.newsboard | |
import android.databinding.DataBindingUtil | |
import android.support.v7.app.AppCompatActivity | |
import android.os.Bundle | |
import android.support.v7.widget.LinearLayoutManager | |
import android.util.Log | |
import com.coderefer.newsboard.databinding.ActivityMainBinding | |
import kotlinx.android.synthetic.main.activity_main.* | |
import org.json.JSONArray | |
import org.json.JSONObject | |
import java.io.InputStream | |
class MainActivity : AppCompatActivity() { | |
var mBinding: ActivityMainBinding? = null | |
private val TAG : String = "MainActivity" | |
private lateinit var linearLayoutManager:LinearLayoutManager | |
private lateinit var mAdapter: NewsRecyclerAdapter | |
override fun onCreate(savedInstanceState: Bundle?) { | |
super.onCreate(savedInstanceState) | |
mBinding = DataBindingUtil.setContentView(this,R.layout.activity_main) | |
linearLayoutManager = LinearLayoutManager(this) | |
val jsonString:String = readJsonFromKotlinFile() | |
val newsList : ArrayList<News> = parseJsonStringToNewsList(jsonString) | |
// can also be declared as kotlin by default supports data binding | |
// recyclerView.layoutManager = linearLayoutManager | |
// using android's data binding library here | |
mBinding?.recyclerView?.layoutManager = linearLayoutManager | |
mAdapter = NewsRecyclerAdapter(newsList) | |
recyclerView.adapter = mAdapter | |
} | |
private fun parseJsonStringToNewsList(jsonString: String): ArrayList<News> { | |
val newsList :ArrayList<News> = ArrayList<News>(0) | |
val newsArray = JSONArray(jsonString) | |
var i = 0 | |
var numIterations = newsArray.length() | |
while(i < numIterations){ | |
val newsObject:JSONObject = newsArray.getJSONObject(i) | |
val news = News() | |
news.news_title = newsObject.getString("news_title") | |
news.news_image_url = newsObject.getString("news_image_url") | |
news.news_source = newsObject.getString("news_source") | |
news.news_detail = newsObject.getString("news_detail") | |
news.news_url = newsObject.getString("news_url") | |
news.id = newsObject.getInt("id") | |
newsList.add(news) | |
i++ | |
} | |
return newsList | |
} | |
private fun readJsonFromKotlinFile() :String{ | |
var inputString = "" | |
try { | |
val inputStream:InputStream = assets.open("news_data_file.json") | |
inputString = inputStream.bufferedReader().use{it.readText()} | |
Log.d(TAG,inputString) | |
} catch (e:Exception){ | |
Log.d(TAG, e.toString()) | |
} | |
return inputString | |
} | |
} |
[adinserter block=”3″]
If you are using fragment, make sure you’ve added your code in onCreateView() method instead.
If you’ve observed the above code, i am reading the json file from local and parsing it to fill the Android RecyclerView. We’ve discussed on how to read a local file using Kotlin in our previous article.
This completes our tutorial on Android RecyclerView using Kotlin where we’ve used Data binding, Extension Functions and filling recyclerview by parsing the data from local json file.
Complete code:
The link for the complete code is given below:
https://github.com/vamsitallapudi/NewsBoard/tree/recyclerview-implementation