Kotlin: 1.2.71
Android Studio: 3.2.1
#RecyclerView
It's a kind of listview.
This is a sample made with RecyclerView
##What do you need to prepare?
・Adapter (This will hold the Items and View
・Listener (Callback Interface for watching list click event
・ItemsArray (Items to show
・Layout of cell to show (Cell's layout
##Create the base of RecyclerView
Android Studio has a feature which helps making base components easier. Right click the package you want to add, go to New -> Fragment -> Fragment(List)
Then, configure the RecyclerView
Object Kind: Enter any name, but I don't use it
Object content layout file name: Layout for cell
List layout file name: RecyclerView layout
Adapter class name: Adapter's name. In this case, I prefer ItemAdapter or ItemFragmentAdapter.
##Fragment
When the RecyclerView is created in this way, Android Studio will creates Fragment like this
class ItemFragment : Fragment() {
// TODO: Customize parameters
private var columnCount = 1
private var listener: OnListFragmentInteractionListener? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let {
columnCount = it.getInt(ARG_COLUMN_COUNT)
}
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
val view = inflater.inflate(R.layout.fragment_item_list2, container, false)
// Set the adapter
if (view is RecyclerView) {
with(view) {
layoutManager = when {
columnCount <= 1 -> LinearLayoutManager(context)
else -> GridLayoutManager(context, columnCount)
}
adapter = MyItemRecyclerViewAdapter(DummyContent.ITEMS, listener)
}
}
return view
}
override fun onAttach(context: Context) {
super.onAttach(context)
if (context is OnListFragmentInteractionListener) {
listener = context
} else {
throw RuntimeException(context.toString() + " must implement OnListFragmentInteractionListener")
}
}
override fun onDetach() {
super.onDetach()
listener = null
}
/**
* This interface must be implemented by activities that contain this
* fragment to allow an interaction in this fragment to be communicated
* to the activity and potentially other fragments contained in that
* activity.
*
*
* See the Android Training lesson
* [Communicating with Other Fragments](http://developer.android.com/training/basics/fragments/communicating.html)
* for more information.
*/
interface OnListFragmentInteractionListener {
// TODO: Update argument type and name
fun onListFragmentInteraction(item: DummyItem?)
}
companion object {
// TODO: Customize parameter argument names
const val ARG_COLUMN_COUNT = "column-count"
// TODO: Customize parameter initialization
@JvmStatic
fun newInstance(columnCount: Int) =
ItemFragment().apply {
arguments = Bundle().apply {
putInt(ARG_COLUMN_COUNT, columnCount)
}
}
}
}
In my case, I only use these
class RecyclerViewSampleFragment : Fragment(), ItemClickListener {
private lateinit var sampleAdapter: RecyclerViewSampleAdapter
private var samples = ArrayList<FragmentType>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
samples.add(FragmentType.COUNTER_SAMPLE)
samples.add(FragmentType.EDITOR_SAMPLE)
samples.add(FragmentType.IMAGE_VIEW_SAMPLE)
samples.add(FragmentType.BOTTOM_NAVIGATION_SAMPLE)
samples.add(FragmentType.WEBVIEW_SAMPLE)
sampleAdapter = RecyclerViewSampleAdapter(samples, this)
setHasOptionsMenu(true)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.recyclerview_layout, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
recycler_view.apply {
this.layoutManager = LinearLayoutManager(context)
this.adapter = sampleAdapter
}
}
override fun onItemSelected(fragmentType: FragmentType) {
(activity?.application as App).mainActivity?.addFragment(fragmentType)
}
companion object {
// TODO: Customize parameter initialization
@JvmStatic
fun newInstance() = RecyclerViewSampleFragment()
}
}
onCreate(): Making the items to show in RecyclerView and initializing adapter
onCreateView(): specify the layout including RecyclerView
onViewCreated(): Apply layoutManager and adapter to RecyclerView
onItemSelected(): Custom Interface of notifying list click event
##Adapter
Android Studio creates Adapter like this
class MyItemRecyclerViewAdapter(
private val mValues: List<DummyItem>,
private val mListener: OnListFragmentInteractionListener?)
: RecyclerView.Adapter<MyItemRecyclerViewAdapter.ViewHolder>() {
private val mOnClickListener: View.OnClickListener
init {
mOnClickListener = View.OnClickListener { v ->
val item = v.tag as DummyItem
// Notify the active callbacks interface (the activity, if the fragment is attached to
// one) that an item has been selected.
mListener?.onListFragmentInteraction(item)
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.fragment_item2, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val item = mValues[position]
holder.mIdView.text = item.id
holder.mContentView.text = item.content
with(holder.mView) {
tag = item
setOnClickListener(mOnClickListener)
}
}
override fun getItemCount(): Int = mValues.size
inner class ViewHolder(val mView: View) : RecyclerView.ViewHolder(mView) {
val mIdView: TextView = mView.item_number
val mContentView: TextView = mView.content
override fun toString(): String {
return super.toString() + " '" + mContentView.text + "'"
}
}
}
init: Add and do something on when adapter created.
onCreateViewHolder(): Specify the layout using as the cell
onBindViewHolder(): Set data to the view
getItemCount(): return the number of Items in array.
ViewHolder Class: Define the view to use (Which need to be defined on cell's layout
On my case:
class RecyclerViewSampleAdapter(private val samples: ArrayList<FragmentType>, private val listener: ItemClickListener) : RecyclerView.Adapter<RecyclerViewSampleAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.recyclerview_item, parent, false))
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val item = samples[position]
holder.sampleView.text = item.title
holder.itemView.setOnClickListener { listener.onItemSelected(item) }
}
override fun getItemCount() = samples.size
inner class ViewHolder(mView: View) : RecyclerView.ViewHolder(mView) {
val sampleView: TextView = mView.sample_name
}
}
If list itens are selected, ItemClickListener will notify the event and gives selected item.
##Cell Layout
Based on Android Studio created
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/sample_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/text_margin"
android:textAppearance="?attr/textAppearanceListItem" />
</LinearLayout>
##RecyclerView Layout
Based on Android Studio created
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.RecyclerView 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/recycler_view"
android:name="galaxysoftware.sampleapp.fragment.RecyclerViewSamplesFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:background="#FFFFFF"
app:layoutManager="android.support.v7.widget.LinearLayoutManager"
tools:context=".fragment.RecyclerViewSampleFragment"
tools:listitem="@layout/recyclerview_item" />
##Ready
Now, it's ready to use RecyclerView
Also I'll recommend to remove DummyContent in dummy package which Android Studio created.
##What should you do when the list items are changed?
You need to notify adapter, what is changed.
Here are the methods to notify
Example: If you want to refresh everything, then use adapter.notifyDataSetChanged() (On my case, sampleAdapter.notifyDataSetChanged()
##Use with Swipe Refresh
You might see many times that list is refreshed by Swiping down. Let's make this possible
First, edit the layout to this
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.RecyclerView 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/refresh_list"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:name="galaxysoftware.sampleapp.fragment.RecyclerViewSamplesFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:background="#FFFFFF"
app:layoutManager="android.support.v7.widget.LinearLayoutManager"
tools:context=".fragment.RecyclerViewSampleFragment"
tools:listitem="@layout/recyclerview_item" />
</android.support.v7.widget.RecyclerView>
Then add this to fragment
refresh_list.setOnRefreshListener {
//Refresh items
sampleAdapter.notifyDataSetChanged()
refresh_list.isRefreshing = false
}
Refresh items and call notifyDataSetChanged(), refresh_list.isRefreshing = false
When to call notify and end refreshing is depends on how you refresh items.
So the case if you are requesting JSON result from the API, you have to wait for the response before notifying and end refreshing to avoid unexpected behavior.
#Project:
Code: https://github.com/GalaxyDevGamer/Android-Samples/blob/master/app/src/main/java/galaxysoftware/androidsamples/fragment/RecyclerViewSampleFragment.kt
Layout: https://github.com/GalaxyDevGamer/Android-Samples/blob/master/app/src/main/res/layout/recyclerview_layout.xml