Adding a RecyclerView
Last updated
Last updated
Before we move into the shared element transitions there are a few changes we need to change to our app as we want to reproduce the most common use of the shared element transitions, a RecyclerView with a details screen.
By the end of this step this will be our final app:
First of all we need to change the CardView
in both of our XML layout files to a RecyclerView
.
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.motion.MotionLayout ....>
<!-- Circle ImageView -->
<!-- Main ImageView -->
<android.support.v7.widget.RecyclerView
android:id="@+id/rv_contacts"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginBottom="16dp"
android:elevation="2dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/guideline_horizontal30"
tools:listitem="@layout/li_main" />
<!-- ImageView Paw 1-->
<!-- ImageView Paw 2-->
<!-- ImageView Paw 3-->
<!-- ImageView Paw 4-->
<!-- Second Background View-->
<!-- ImageView Leaf 1 -->
<!-- ImageView Leaf 2 -->
<!-- Guidelines -->
</android.support.constraint.motion.MotionLayout>
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.motion.MotionLayout ....>
<!-- Circle ImageView -->
<!-- ImageView Leaf 1 -->
<!-- ImageView Leaf 2 -->
<android.support.v7.widget.RecyclerView
android:id="@+id/rv_contacts"
android:layout_width="match_parent"
android:layout_height="0dp"
tools:listitem="@layout/li_main"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/guideline_horizontal20"
app:layout_constraintVertical_bias="0.0"
android:elevation="2dp"/>
<!-- Main ImageView -->
<!-- ImageView Paw 1-->
<!-- ImageView Paw 2-->
<!-- ImageView Paw 3-->
<!-- ImageView Paw 4-->
<!-- Second Background View-->
<!-- Guidelines -->
</android.support.constraint.motion.MotionLayout>
Then we need to create the XML layout for our list items.
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout android:layout_height="wrap_content"
android:layout_width="match_parent"
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">
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/iv_avatar"
android:layout_width="45dp"
android:layout_height="45dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:elevation="3dp"
app:layout_constraintBottom_toBottomOf="@+id/cardView"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/cardView"
tools:src="@tools:sample/avatars" />
<android.support.v7.widget.CardView
android:id="@+id/cardView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="28dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:background="@color/cardview_light_background"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/tv_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="29dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:textAppearance="@style/TextAppearance.AppCompat.Subhead"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="@tools:sample/full_names" />
<TextView
android:id="@+id/tv_phone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="@+id/tv_name"
app:layout_constraintTop_toBottomOf="@+id/tv_name"
tools:text="@tools:sample/us_phones" />
</android.support.constraint.ConstraintLayout>
</android.support.v7.widget.CardView>
</android.support.constraint.ConstraintLayout>
We then create a data class for our mock data:
data class Contact (val avatarId: Int?, val name: String, val phone:String)
On our MainActivity.kt
we add the following methods
private val contacts = arrayListOf<Contact>()
private val NUMBER_OF_ITEMS = 8
....
// Create dummy data to populate to our RecyclerView
private fun getData(){
var count =0
while (count < NUMBER_OF_ITEMS) {
contacts.add(Contact(getDrawableForPosition(count+1), "XYZ", "645654654"))
count ++
}
}
/**
We want to have an automatic way to get the images for the avatars.
This will return the resource id for a specific name
The position refers to the item position on the list
*/
private fun getDrawableForPosition(position:Int): Int? {
val name = "avatar_$position"
return resources.getIdentifier(name, "drawable",packageName)
}
We also need to create a RecyclerView
Adapter.
To add Glide in your project add the following to your Gradle: implementation 'com.github.bumptech.glide:glide:4.8.0'
class ContactsAdapter(val context: Context, val items:List<Contact>) :
RecyclerView.Adapter<ContactsAdapter.ViewHolder>() {
class ViewHolder (view: View) : RecyclerView.ViewHolder(view) {
// Holds the TextView that will add each animal to
val avatar = view.iv_avatar!!
val tvName = view.tv_name!!
val tvPhone = view.tv_phone!!
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ContactsAdapter.ViewHolder {
return ViewHolder(LayoutInflater.from(context).inflate(R.layout.li_main, parent, false))
}
override fun getItemCount(): Int {
return items.size
}
override fun onBindViewHolder(holder: ContactsAdapter.ViewHolder, position: Int) {
Glide.with(context).load(items[position].avatarId).into(holder.avatar)
holder.tvName.text = items[position].name
holder.tvPhone.text = items[position].phone
}
}
Finally we populate our RecyclerView
with our dummy data
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main_start)
getData()
rv_contacts.apply {
setHasFixedSize(true)
layoutManager = LinearLayoutManager(context)
adapter = ContactsAdapter(context, contacts)
}
}
And of course we run our app to test that everything is working
I'm going to use for setting the images to our ImageViews. Even though for this project we could use only thesetImageResource
property, I find it a good practice to have a handy way of handling loading and errors (and unless you have the exact ID there's always a slight chance to get a NPE). And while you should also implement those, we don't have time to cover everything in this workshop.