We’ll once again take a break from the cross-platform Xamarin content and look at an example of using the latest Kotlin language from Jetbrains with our native Android applications. In this post, we’ll look at an implementation of a TabLayout
with a ViewPager
using Kotlin!
I also apologize for the lack of useful highlighting of the Kotlin code in this post. Since it is a new language, WordPress doesn’t support it as well for code snippets…
The source code for this example can be found on my GitHub here:
https://github.com/SuavePirate/KotlinPuppies.
The Layout
This example will use a RecyclerView
for the content of each Fragment
. So we need to define layouts for our Puppy
, Fragment
, and our entire Activity
that houses the TabLayout
and Fragments
.
Our puppy item will contain a CardView
that has an image and text to contain a picture of the puppy and the pup’s name!
puppy_item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="8dp"
app:cardElevation="8dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="16dp"
android:orientation="vertical">
<ImageView
android:id="@+id/puppyImageView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:srcCompat="@mipmap/ic_launcher" />
<TextView
android:id="@+id/puppyTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAlignment="center"
android:text="Puppy Name" />
</LinearLayout>
</android.support.v7.widget.CardView>
</LinearLayout>
Now let’s look at our Fragment
layout that will contain a RecylerView
that houses each collection of puppies.
puppy_fragment.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.suavepirate.kotlinpuppies.MainActivity$PlaceholderFragment">
<android.support.v7.widget.RecyclerView
android:id="@+id/puppyRecyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
tools:listitem="@layout/puppy_item" />
</RelativeLayout>
Now let’s wrap it all together with our main layout:
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout 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:id="@+id/main_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context="com.suavepirate.kotlinpuppies.MainActivity">
<android.support.design.widget.AppBarLayout
android:id="@+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="@dimen/appbar_padding_top"
android:theme="@style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:layout_scrollFlags="scroll|enterAlways"
app:popupTheme="@style/AppTheme.PopupOverlay">
</android.support.v7.widget.Toolbar>
<android.support.design.widget.TabLayout
android:id="@+id/tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</android.support.design.widget.AppBarLayout>
<android.support.v4.view.ViewPager
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
</android.support.design.widget.CoordinatorLayout>
Now that we have our layouts, let’s create our Fragment
, Adapters
, and then wrap it all together in our MainActivity
.
Building the Recycler Adapter
Let’s first define our RecyclerView
adapter and ViewHolder
to contain our collections of puppies.
PuppyHolder.kt
class PuppyHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val puppyImage: ImageView = itemView.findViewById<ImageView>(R.id.puppyImageView)
private val puppyName: TextView = itemView.findViewById(R.id.puppyTextView)
fun updateWithPuppy(puppy: Puppy) {
puppyImage.setImageDrawable(puppy.imageFile)
puppyName.text = puppy.name
}
}
This code defines a class that inherits the RecyclerView.ViewHolder
with a default constructor that requires a View
parameter that is also passed into the base class constructor. It then defines the two subviews we need to populate – the TextView
and ImageView
of a single puppy. Lastly, we create our updateWithPuppy
function that will be called by our Adapter
to instantiate the content with the given puppy’s information.
Now that we have our ViewHolder
, we can create our Adapter
:
PuppyAdapter.kt
class PuppyAdapter(private val puppies: ArrayList<Puppy>) : RecyclerView.Adapter<PuppyHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PuppyHolder {
val puppyItem = LayoutInflater.from(parent.context).inflate(R.layout.puppy_item, parent, false) as LinearLayout
return PuppyHolder(puppyItem)
}
override fun onBindViewHolder(holder: PuppyHolder, position: Int) {
holder.updateWithPuppy(puppies[position])
}
override fun getItemCount(): Int {
return puppies.toArray().count();
}
}
This adapter uses another cool feature of Kotlin – Defining a private field in the constructor while also auto-setting it. The class declaration and default constructor of PuppyAdapter(private val puppies: ArrayList)
is the equivalent to something like this in Java:
public class PuppyAdapter{
private final ArrayList<Puppy> puppies;
public PuppyAdapter(ArrayList<Puppy> puppies){
this.puppies = puppies;
}
}
That’s pretty sweet! The rest of the wire up for the Adapter
is pretty standard. It sets the ViewHolder
using the PuppyHolder
we created above and updates it with the puppy by finding it with the given index.
The Puppy Fragment
Now we can create our Fragment
that will contain and wire up the RecyclerView
for each puppy collection.
PuppyListFragment.kt
class PuppyListFragment(passedContext: Context) : Fragment(){
val puppyFactory : PuppyFactory = PuppyFactory(passedContext)
val ARG_LIST_TYPE = "LIST_TYPE"
val passThroughContext : Context = passedContext
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
val rootView = inflater!!.inflate(R.layout.fragment_main, container, false)
val recyclerView = rootView.findViewById<RecyclerView>(R.id.puppyRecyclerView) as RecyclerView
val listType = this.arguments.getSerializable(ARG_LIST_TYPE) as PuppyListType
var puppies = ArrayList<Puppy>()
when (listType) {
PuppyListType.All -> puppies = puppyFactory.puppies
PuppyListType.Active -> puppies = puppyFactory.activePuppies
PuppyListType.LeashTrained -> puppies = puppyFactory.leashTrainedPuppies
PuppyListType.Big -> puppies = puppyFactory.bigPuppies
PuppyListType.Small -> puppies = puppyFactory.smallPuppies
}
recyclerView.adapter = PuppyAdapter(puppies)
recyclerView.layoutManager = LinearLayoutManager(passThroughContext)
return rootView
}
companion object {
val ARG_LIST_TYPE = "LIST_TYPE"
fun newInstance(listType: PuppyListType, context: Context): PuppyListFragment {
val fragment = PuppyListFragment(context)
val args = Bundle()
args.putSerializable(ARG_LIST_TYPE, listType)
fragment.arguments = args
return fragment
}
}
}
In the onCreateView
override, we get our puppies by type from our factory class and then instantiate our PuppyAdapter
and LinearLayoutManager
that get applied to the RecyclerView
that we grab from our layout created earlier. Now we can pass in the PuppyListType
that the fragment is responsible for displaying which will then set up our RecyclerView
to render those particular puppies.
We also set up what is the equivalent of a static
function that can instantiate a new instance of a PuppyListFragment
by using a nested companion object
.
Adding Page Adapter
Now that we have our Fragment
and it’s child RecyclerView
for puppies all set up, we can now create an adapter that is responsible for handling the different pages within the TabLayout
that we are ultimately setting up.
PageAdapter.kt
class PageAdapter(fm: FragmentManager, private val context: Context) : FragmentPagerAdapter(fm) {
override fun getItem(position: Int): Fragment {
when (position) {
0 -> return PuppyListFragment.newInstance(PuppyListType.All, context)
1 -> return PuppyListFragment.newInstance(PuppyListType.Big, context)
2 -> return PuppyListFragment.newInstance(PuppyListType.Small, context)
3 -> return PuppyListFragment.newInstance(PuppyListType.LeashTrained, context)
4 -> return PuppyListFragment.newInstance(PuppyListType.Active, context)
}
return PuppyListFragment.newInstance(PuppyListType.All, context)
}
override fun getCount(): Int {
// Show 5 total pages.
return 5
}
override fun getPageTitle(position: Int): CharSequence? {
// return null to show no title.
return null
}
}
This is a pretty standard implementation of a PageAdapter
. We override the getItem
function and return the appropriate instantiated PuppyListFragment
by passing in the PuppyListType
we want to use by the grouping.
Set up the Activity
The last bit now is the set up our Activity
that will house our TabLayout
and ViewPager
that will contain multiple instances of the PuppyListFragment
to show different collections of puppies by category.
MainActivity.kt
class MainActivity : AppCompatActivity() {
private var mSectionsPagerAdapter: PageAdapter? = null
/**
* The [ViewPager] that will host the section contents.
*/
private var mViewPager: ViewPager? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val toolbar = findViewById<View>(R.id.toolbar) as Toolbar
setSupportActionBar(toolbar)
// Create the adapter that will return a fragment for each of the three
// primary sections of the activity.
mSectionsPagerAdapter = PageAdapter(supportFragmentManager, this)
// Set up the ViewPager with the sections adapter.
mViewPager = findViewById<ViewPager?>(R.id.container)
mViewPager!!.adapter = mSectionsPagerAdapter
val tabLayout = findViewById<View>(R.id.tabs) as TabLayout
tabLayout.setupWithViewPager(mViewPager)
// set icons
tabLayout.getTabAt(0)!!.setIcon(R.drawable.ic_home_white_24dp)
tabLayout.getTabAt(1)!!.setIcon(R.drawable.ic_dog_white_24dp)
tabLayout.getTabAt(2)!!.setIcon(R.drawable.ic_small_dog_white_24dp)
tabLayout.getTabAt(3)!!.setIcon(R.drawable.ic_trained_white_24dp)
tabLayout.getTabAt(4)!!.setIcon(R.drawable.ic_active_white_24dp)
}
}
Our MainActivity
holds a private field for the ViewPager
reference, and in the override of onCreate
, we set up our view components by finding them in our associated layout file, then wire up the PageAdapter
with our TabLayout
. Then we set our icons for each given tab after calling the setupWithViewPager
on our TabLayout
.
View the Results
We can run our application and view our expected results of our tabs and different list of puppy cards!

If you like what you see, don’t forget to follow me on twitter @Suave_Pirate, check out my GitHub, and subscribe to my blog to learn more mobile developer tips and tricks!
Interested in sponsoring developer content? Message @Suave_Pirate on twitter for details.
Like this:
Like Loading...