Kotlin Version of One Recycler View Adapter for All*

Bill Inkoom
5 min readJun 27, 2017

Ever since Kotlin became a first class language for Android, I have not gone a day without hearing, reading or watching something about Kotlin. Not as if I do it intentionally — I just cant avoid it. Its just everywhere.

Ever since Kotlin became a first class language for Android, I have not gone a day without hearing, reading or watching something about Kotlin. Not as if I do it intentionally — I just cant avoid it. Its just everywhere. So I revisited my first story on medium, and well its Java 7(Yeah it is). Then I got a bright idea, why don’t I just Kotlinize() that article for my next story.

Lets begin!

public CardObject(String title, String subtitle, String image, String info){
this.title = title;
this.subtitle = subtitle;
this.image = image;
this.info = info;
}

This is the CardObject Class I created in Java 7. Well the Kotlinized version of this class is

data class CardObject(val title: String, val subtitle: String, val image: String, val info: String) {

}

The Kotlin version is kind of neat. Its short and straightforward. You don’t need to do any setters and getters. Read more about data classes here.

public CardListPackage(String cardType, ArrayList<CardObject> cardObjects, int cardXML) {
this.cardType = cardType; //type of data eg students,courses etc
this.cardObjects = cardObjects; //data for the recycler view
this.cardXML = cardXML; //xml associated to this card type
}

The CardListPackage is also similar

data class CardListPackage(val cardType:String,val cardObjects:List<CardObject>,val cardXML:Int) {
}

Well isn’t this sweet!

You remember our Java 7 recycler view adapter….yeah I do. It was one great class.

public CardObjectAdapter(Context context, CardListPackage cardListPackage,CardBindingListener cardBindingListener, CardTappedListener cardTappedListener) {
this.context = context;
this.cardObjects = cardListPackage.getCardObjects();
this.card_xml = cardListPackage.getCardXML();
this.cardType = cardListPackage.getCardType();

this.cardTappedListener = cardTappedListener;
this.cardBindingListener = cardBindingListener;
}
@Override
public CardObjectViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(card_xml, viewGroup, false);
CardObjectViewHolder cardObjectViewHolder = new CardObjectViewHolder(view,cardType);
return cardObjectViewHolder;
}
@Override
public void onBindViewHolder(final CardObjectViewHolder cardObjectViewHolder, final int position) {

cardObjectViewHolder.cardObject = cardObjects.get(position);
final CardObject cardObject = cardObjects.get(position);
try {
cardObjectViewHolder.title.setText(cardObject.getTitle());
cardObjectViewHolder.subtitle.setText(cardObject.getSubtitle());
cardObjectViewHolder.info.setText(cardObject.getInfo());
} catch (Exception e) {
Log.e("error", e.toString());
}

//
cardBindingListener.onCardBinding(cardObject,cardObjectViewHolder,position);
}
@Override
public int getItemCount() {
return cardObjects.size();
}
public class CardObjectViewHolder extends RecyclerView.ViewHolder {


//simple views
public TextView title, subtitle, info;
public ImageView image;
public CardObject cardObject;
public int position;
//you can put more views here
public View border,parentCard;
public CardObjectViewHolder(final View card,String cardType) {
super(card);

parentCard = card;
title = (TextView) parentCard.findViewById(R.id.title);
image = (ImageView) parentCard.findViewById(R.id.image);
subtitle = (TextView) parentCard.findViewById(R.id.subtitle);
info = (TextView) parentCard.findViewById(R.id.info);

cardObjectPresenter = new CardObjectPresenter(context,cardType,this);

cardObjectPresenter.findCardReferences();

card.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
cardTappedListener.onCardTapped(cardObject);
}
});
}
}

Can Kotlin suprise as this time?

class CardObjectAdapter(val context: Context, cardListPackage: CardListPackage,
val cardBindingListener: (CardObject, CardObjectViewHolder, Int) -> Unit,
val cardTappedListener: (CardObject) -> Unit
) : RecyclerView.Adapter<CardObjectAdapter.CardObjectViewHolder>() {

var cardObjects: List<CardObject>
var card_xml: Int = 0
var lastPosition = -1
var animationXml = 0
var cardType: String


init {
this.cardObjects = cardListPackage.cardObjects
this
.card_xml = cardListPackage.cardXML
this
.cardType = cardListPackage.cardType

}

override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): CardObjectViewHolder {
val view = LayoutInflater.from(viewGroup.context).inflate(card_xml, viewGroup, false)
val cardObjectViewHolder = CardObjectViewHolder(view, cardType)
return cardObjectViewHolder
}

override fun onBindViewHolder(cardObjectViewHolder: CardObjectViewHolder, cardPosition: Int) {

cardObjectViewHolder.cardObject = cardObjects[cardPosition]
val cardObject = cardObjects[cardPosition]
try {
cardObjectViewHolder.title.text = cardObject.title
cardObjectViewHolder.subtitle.text = cardObject.subtitle
cardObjectViewHolder.info.text = cardObject.info
} catch (e: Exception) {
Log.e("error", e.toString())
}

//
cardBindingListener(cardObject, cardObjectViewHolder, cardPosition)

setAnimation(cardObjectViewHolder.container, cardPosition)
}

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

inner class CardObjectViewHolder(var parentCard: View, cardType: String) : RecyclerView.ViewHolder(parentCard) {

//simple views
var title: TextView
var subtitle: TextView
var info: TextView
var container: LinearLayout
var cardObject: CardObject? = null
var image
: ImageView
var cardPosition: Int = 0

//for more complex views
var border: View? = null
var cardObjectPresenter
: CardObjectPresenter


init {
title = parentCard.findViewById(R.id.title) as TextView
image = parentCard.findViewById(R.id.image) as ImageView
subtitle = parentCard.findViewById(R.id.subtitle) as TextView
info = parentCard.findViewById(R.id.info) as TextView
container = parentCard.findViewById(R.id.container) as LinearLayout
cardObjectPresenter = CardObjectPresenter(context, cardType, this)
cardObjectPresenter.findReferences()

parentCard.setOnClickListener { cardTappedListener(cardObject!!) }
}
}
}

A few surprises though.

val cardBindingListener: (CardObject, CardObjectViewHolder, Int) -> Unit,
val cardTappedListener: (CardObject) -> Unit

We replace the listeners with these lambda definitions. So when the constructor of the CardObjectAdapter is called it will expect two lambdas which conform to this signature.

cardObject!!
/*This implies that if cardObject is null, a Null Pointer Exception will be thrown. For further explanation, please go here.
*/

Our CardObjectPresenter Looked liked this

public CardObjectPresenter(Context context, String cardType, CardObjectAdapter.CardObjectViewHolder cardObjectViewHolder) {
this.context = context;
this.cardObjectViewHolder = cardObjectViewHolder;
this.cardType = cardType;
}
//used to find extra references apart from the basic //title,subtitle,image and infopublic void findCardReferences() {
switch (cardType.toLowerCase()) {
case "students":
cardObjectViewHolder.border = cardObjectViewHolder.parentCard.findViewById(R.id.border);
break;
//more cases here ...
}
}
data class CardObjectPresenter(val context:Context,val cardType:String,val cardObjectViewHolder: CardObjectAdapter.CardObjectViewHolder) {

fun findReferences(){
when (cardType) {
"students" -> cardObjectViewHolder.border = cardObjectViewHolder.parentCard.findViewById(R.id.border)
/*"teachers" -> cardObjectViewHolder.border = cardObjectViewHolder.parentCard.findViewById(R.id.border)
more cases here*/
else -> {
print("x is neither 1 nor 2")
}
}
}
}

So in Kotlin the When keyword replaces the switch keyword in Java. Its great so read more on it here.

And how about our java interfaces?

public interface CardBindingListener {
void onCardBinding(CardObject cardObject, CardObjectAdapter.CardObjectViewHolder cardObjectViewHolder,int position);
}
public interface CardTappedListener {
public void onCardTapped(CardObject cardObject);
}

Well they now look like this …

// Its empty because we don't need interfaces to do this 
// we used the lambda's to achieve that!

So finally how do we use this. In the first story, this is how we did it

final int cardSize = cardObjects.size();
CardListPackage cardListPackage = new CardListPackage("students",cardObjects,R.layout.student_card);
cardObjectAdapter = new CardObjectAdapter(context, cardListPackage, new CardBindingListener() {
@Override
public void onCardBinding(CardObject cardObject, CardObjectAdapter.CardObjectViewHolder cardObjectViewHolder, int position) {
if (position == cardSize - 1) {//the extra view for the student card cardObjectViewHolder.border.setBackgroundColor(context.getResources().getColor(R.color.white));
}
Picasso.with(context).load(cardObject.getImage()).error(R.drawable.app_icon_gray).into(cardObjectViewHolder.image);
}
}, new CardTappedListener() {
@Override
public void onCardTapped(CardObject cardObject) {
Intent intent = new Intent(context, StudentProfile.class);
intent.putExtra("card_id", cardObject.getId());
intent.putExtras(cardObject.getCardBundle());
context.startActivity(intent);
}
});

In Kotlin, this is how I used it

val card_objects = findViewById(R.id.card_objects) as RecyclerView;
card_objects.setHasFixedSize(true);
card_objects.layoutManager = LinearLayoutManager(context)

val cardObjects:List<CardObject> = List(10){index -> CardObject("Teacher $index","Teachers Name","http://image.com","More info about teacher")}
val
cardListPackage = CardListPackage("simple",cardObjects,R.layout.simple_list);

card_objects.adapter = CardObjectAdapter(context,cardListPackage,cardBindingListener = {
cardObject,cardViewHolder,position ->

}
,cardTappedListener = {cardObject ->
val
intent = Intent(context,MainActivity::class.java)
intent.putExtra("card_id", cardObject.title)
context.startActivity(intent)
})

Wheeew! we are done now. I hope this has been helpful. Kotlin is a great language. Its fun, modern and flexible. If you are doing native android with Java then learning Kotlin will be fun and refreshing. Give a try today. After all more knowledge never hurts. Have a great day.

--

--

Bill Inkoom

Software Engineer with great love for Mathematics especially Abstract Mathematics.