Kotlin Version of One Recycler View Adapter for All*
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.