Я создал нечто подобное, используя ItemDecoration
надеюсь, это поможет.
в вашем Activity/Fragment
вызове setAdapter()
@RequiresApi(Build.VERSION_CODES.O)
private fun setAdapter() {
val data = arrayListOf<EventPOJO>(
EventPOJO(
"Entry 1 \n Lorem Ipsum is simply dummy text of the printing and typesetting industry. ",
"12:00 AM"
),
EventPOJO("Entry 2 ", "01:00 AM")
)
val mAdapter = Adapter<EventPOJO>(data)
recyclerDay.run {
adapter = mAdapter
layoutManager = LinearLayoutManager(context)
addItemDecoration(ScheduleTimeHeadersDecoration(data))
}
}
Ваш обычный RecyclerView.Adapter
class Adapter<T>(
private val eventList: ArrayList<T>
) : RecyclerView.Adapter<Adapter.EventListViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): EventListViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val view: View = layoutInflater.inflate(R.layout.item_day, parent, false)
return EventListViewHolder(view)
}
override fun getItemCount() = eventList.size
override fun onBindViewHolder(holder: EventListViewHolder, position: Int) {
holder.bind(eventList[position])
}
class EventListViewHolder(var view: View) :
RecyclerView.ViewHolder(view) {
private var name: TextView? = null
init {
name = view.findViewById(R.id.name)
name?.creatRandomColor()
}
fun <T> bind(bindObj: T) {
if (bindObj is EventPOJO) {
name?.text = bindObj.name
}
}
private fun TextView.creatRandomColor() {
val rnd = Random()
val color = Color.argb(
255,
rnd.nextInt(256),
rnd.nextInt(256),
rnd.nextInt(256)
)
this.setBackgroundColor(color)
}
}}
Объект данных
data class EventPOJO(
val name: String,
val time: String
)
ваш item_day. xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:minHeight="50dp"
android:padding="5dp"
android:text="Entry 1"
android:textSize="20sp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/guideline"
app:layout_constraintTop_toTopOf="parent" />
<android.support.constraint.Guideline
android:id="@+id/guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_begin="100dp" />
</android.support.constraint.ConstraintLayout>
и, наконец, ваш itemDecoration
/**
* A [RecyclerView.ItemDecoration] which draws sticky headers for a given list of
sessions.
*/
@RequiresApi(Build.VERSION_CODES.O)
class ScheduleTimeHeadersDecoration(
sessions: ArrayList<EventPOJO>
) : RecyclerView.ItemDecoration() {
private val paint: TextPaint = TextPaint(ANTI_ALIAS_FLAG).apply {
color = Color.BLUE
textSize = 50f
}
private val width: Int = 350
// Get the sessions index:start time and create header layouts for each
@RequiresApi(Build.VERSION_CODES.O)
private val timeSlots: Map<Int, StaticLayout> =
sessions
.mapIndexed { index, session ->
index to session.time
}.map {
it.first to createHeader(it.second)
}.toMap()
override fun onDrawOver(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
val parentPadding = parent.paddingTop
var earliestPosition = Int.MAX_VALUE
var previousHeaderPosition = -1
var previousHasHeader = false
var earliestChild: View? = null
for (i in parent.childCount - 1 downTo 0) {
val child = parent.getChildAt(i)
if (child.y > parent.height || (child.y + child.height) < 0) {
// Can't see this child
continue
}
val position = parent.getChildAdapterPosition(child)
if (position < 0) {
continue
}
if (position < earliestPosition) {
earliestPosition = position
earliestChild = child
}
val header = timeSlots[position]
if (header != null) {
drawHeader(c, child, parentPadding, header, child.alpha, previousHasHeader)
previousHeaderPosition = position
previousHasHeader = true
} else {
previousHasHeader = false
}
}
if (earliestChild != null && earliestPosition != previousHeaderPosition) {
// This child needs a sicky header
findHeaderBeforePosition(earliestPosition)?.let { stickyHeader ->
previousHasHeader = previousHeaderPosition - earliestPosition == 1
drawHeader(c, earliestChild, parentPadding, stickyHeader, 1f, previousHasHeader)
}
}
}
private fun findHeaderBeforePosition(position: Int): StaticLayout? {
for (headerPos in timeSlots.keys.reversed()) {
if (headerPos < position) {
return timeSlots[headerPos]
}
}
return null
}
private fun drawHeader(
canvas: Canvas,
child: View,
parentPadding: Int,
header: StaticLayout,
headerAlpha: Float,
previousHasHeader: Boolean
) {
val childTop = child.y.toInt()
val childBottom = childTop + child.height
var top = (childTop).coerceAtLeast(parentPadding)
if (previousHasHeader) {
top = top.coerceAtMost(childBottom - header.height - 25)
}
paint.alpha = (headerAlpha * 255).toInt()
canvas.withTranslation(y = top.toFloat()) {
header.draw(canvas)
}
}
/**
* Create a header layout for the given [startTime].
*/
private fun createHeader(startTime: String): StaticLayout {
val text = SpannableStringBuilder().apply {
append(startTime.split(" ")[0])
append(System.lineSeparator())
append(startTime.split(" ")[1])
}
return newStaticLayout(text, paint, width, ALIGN_CENTER, 1f, 0f, false)
}
fun newStaticLayout(
source: CharSequence,
paint: TextPaint,
width: Int,
alignment: Layout.Alignment,
spacingmult: Float,
spacingadd: Float,
includepad: Boolean
): StaticLayout {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
StaticLayout.Builder.obtain(source, 0, source.length, paint, width).apply {
setAlignment(alignment)
setLineSpacing(spacingadd, spacingmult)
setIncludePad(includepad)
}.build()
} else {
@Suppress("DEPRECATION")
StaticLayout(source, paint, width, alignment, spacingmult, spacingadd, includepad)
}
}
private inline fun Canvas.withTranslation(
x: Float = 0.0f,
y: Float = 0.0f,
block: Canvas.() -> Unit
) {
val checkpoint = save()
translate(x, y)
try {
block()
} finally {
restoreToCount(checkpoint)
}
}
}
ссылка Google I / O 2019 Android Приложение