Last active
July 21, 2019 12:02
-
-
Save jpshelley/4e0d4aa16c20e91a6c01e48d278b4282 to your computer and use it in GitHub Desktop.
A Sectioned Adapter for Android via Kotlin
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* A section to use in Recycler Adapter's. | |
* It holds a sectioned position for use in determining the offset of the adapter items. | |
*/ | |
data class AdapterSection(var firstPosition: Int, var title: String, var sectionedPosition: Int = 0) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* An adapter that allows a RecyclerView to contain sections or subheaders | |
* much like the material docs describe. | |
* https://material.google.com/components/subheaders.html | |
*/ | |
interface SectionedAdapter { | |
/** | |
* A list of sections to display in adapter. | |
*/ | |
var sections: SparseArray<AdapterSection> | |
/** | |
* Returns true if the given position contains a section | |
*/ | |
fun isSectionHeaderPosition(position: Int) = sections[position] != null | |
/** | |
* Determines the correct position based off of the number of currently displayed sections. | |
*/ | |
fun sectionedPositionToPosition(sectionedPosition: Int): Int { | |
if (isSectionHeaderPosition(sectionedPosition)) { | |
return RecyclerView.NO_POSITION | |
} | |
var offset = 0 | |
for (i in 0..sections.size() - 1) { | |
if (sections.valueAt(i).sectionedPosition > sectionedPosition) { | |
break | |
} | |
--offset | |
} | |
return sectionedPosition + offset | |
} | |
fun positionToSectionedPosition(position: Int): Int { | |
var offset = 0 | |
for (i in 0..sections.size() - 1) { | |
if (sections.valueAt(i).firstPosition > position) { | |
break | |
} | |
++offset | |
} | |
return position + offset | |
} | |
/** | |
* Clears the current set of selections and sets a new list. | |
* In order for the new sections to be displayed, one must first call | |
* notifyDatasetChanged() | |
* | |
* @param newSections The new sections to be set | |
*/ | |
fun setSections(newSections: Array<AdapterSection>) { | |
sections.clear() | |
val sortedSections = newSections.clone() | |
Arrays.sort<AdapterSection>(sortedSections) { o, o1 -> | |
when { | |
o.firstPosition == o1.firstPosition -> 0 | |
o.firstPosition < o1.firstPosition -> -1 | |
else -> 1 | |
} | |
} | |
var offset = 0 // offset positions for the headers we're adding | |
sortedSections.forEach { | |
it.sectionedPosition = it.firstPosition + offset | |
sections.append(it.sectionedPosition, it) | |
++offset | |
} | |
} | |
/** | |
* Returns the nearest section header if one exists | |
*/ | |
tailrec fun getNearestSectionHeader(sectionedPosition: Int): Int { | |
if (isSectionHeaderPosition(sectionedPosition)) { | |
return sectionedPosition | |
} else { | |
return getNearestSectionHeader(sectionedPosition - 1) | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Could you please share how you use it with delegates approach?