mirror of
https://gitlab.linphone.org/BC/public/linphone-android.git
synced 2026-01-17 11:28:06 +00:00
Improved RecyclerViewHeaderDecoration to make it sticky
This commit is contained in:
parent
9c9391c95b
commit
42a115e93b
5 changed files with 98 additions and 32 deletions
|
|
@ -13,8 +13,10 @@ import androidx.recyclerview.widget.ListAdapter
|
|||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.linphone.R
|
||||
import org.linphone.databinding.CallSuggestionListCellBinding
|
||||
import org.linphone.databinding.CallSuggestionListDecorationBinding
|
||||
import org.linphone.databinding.ContactListCellBinding
|
||||
import org.linphone.ui.main.calls.model.ContactOrSuggestionModel
|
||||
import org.linphone.utils.AppUtils
|
||||
import org.linphone.utils.Event
|
||||
import org.linphone.utils.HeaderAdapter
|
||||
|
||||
|
|
@ -43,12 +45,18 @@ class ContactsAndSuggestionsListAdapter(
|
|||
}
|
||||
val previousModel = getItem(position - 1)
|
||||
return previousModel.friend != null
|
||||
}
|
||||
} else if (position == 0) return true
|
||||
return false
|
||||
}
|
||||
|
||||
override fun getHeaderViewForPosition(context: Context, position: Int): View {
|
||||
return LayoutInflater.from(context).inflate(R.layout.call_suggestion_list_decoration, null)
|
||||
val binding = CallSuggestionListDecorationBinding.inflate(LayoutInflater.from(context))
|
||||
binding.header.text = if (position == 0) {
|
||||
AppUtils.getString(R.string.call_start_contacts_list_title)
|
||||
} else {
|
||||
AppUtils.getString(R.string.call_start_suggestions_list_title)
|
||||
}
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@ class StartCallFragment : GenericFragment() {
|
|||
binding.contactsAndSuggestionsList.setHasFixedSize(true)
|
||||
binding.contactsAndSuggestionsList.adapter = adapter
|
||||
|
||||
val headerItemDecoration = RecyclerViewHeaderDecoration(requireContext(), adapter)
|
||||
val headerItemDecoration = RecyclerViewHeaderDecoration(requireContext(), adapter, true)
|
||||
binding.contactsAndSuggestionsList.addItemDecoration(headerItemDecoration)
|
||||
|
||||
adapter.contactClickedEvent.observe(viewLifecycleOwner) {
|
||||
|
|
|
|||
|
|
@ -27,7 +27,11 @@ import android.view.View
|
|||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
|
||||
class RecyclerViewHeaderDecoration(private val context: Context, private val adapter: HeaderAdapter) : RecyclerView.ItemDecoration() {
|
||||
class RecyclerViewHeaderDecoration(
|
||||
private val context: Context,
|
||||
private val adapter: HeaderAdapter,
|
||||
private val sticky: Boolean = false
|
||||
) : RecyclerView.ItemDecoration() {
|
||||
private val headers: SparseArray<View> = SparseArray()
|
||||
|
||||
override fun getItemOffsets(
|
||||
|
|
@ -74,8 +78,12 @@ class RecyclerViewHeaderDecoration(private val context: Context, private val ada
|
|||
}
|
||||
|
||||
override fun onDraw(canvas: Canvas, parent: RecyclerView, state: RecyclerView.State) {
|
||||
for (i in 0 until parent.childCount) {
|
||||
if (sticky) return
|
||||
|
||||
// Used to display the moving item decoration
|
||||
for (i in 0 until parent.childCount) { // Only returns visible children
|
||||
val child = parent.getChildAt(i)
|
||||
// Maps the visible view position to the item index in the adapter
|
||||
val position = parent.getChildAdapterPosition(child)
|
||||
if (position != RecyclerView.NO_POSITION && adapter.displayHeaderForPosition(position)) {
|
||||
canvas.save()
|
||||
|
|
@ -89,6 +97,62 @@ class RecyclerViewHeaderDecoration(private val context: Context, private val ada
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDrawOver(canvas: Canvas, parent: RecyclerView, state: RecyclerView.State) {
|
||||
if (!sticky) return
|
||||
|
||||
var latestPositionHeaderFound = -1
|
||||
var nextHeaderTopPosition = -1f
|
||||
|
||||
for (index in parent.childCount downTo 0) {
|
||||
val child = parent.getChildAt(index)
|
||||
val position = parent.getChildAdapterPosition(child)
|
||||
if (position != RecyclerView.NO_POSITION && adapter.displayHeaderForPosition(position)) {
|
||||
canvas.save()
|
||||
val headerView: View = headers.get(position) ?: adapter.getHeaderViewForPosition(
|
||||
context,
|
||||
position
|
||||
)
|
||||
|
||||
val top = child.y - headerView.height
|
||||
if (top >= 0) { // don't move the first header
|
||||
canvas.translate(0f, top)
|
||||
}
|
||||
|
||||
headerView.draw(canvas)
|
||||
canvas.restore()
|
||||
|
||||
latestPositionHeaderFound = position
|
||||
nextHeaderTopPosition = child.y
|
||||
}
|
||||
}
|
||||
|
||||
// Makes sure at least one header is displayed
|
||||
if (latestPositionHeaderFound > 0 || latestPositionHeaderFound == -1) {
|
||||
// Display first item header at top
|
||||
val topVisibleChild = parent.getChildAt(0)
|
||||
val topVisibleChildPosition = parent.getChildAdapterPosition(topVisibleChild)
|
||||
for (position in topVisibleChildPosition downTo 0) {
|
||||
if (adapter.displayHeaderForPosition(position)) {
|
||||
canvas.save()
|
||||
val headerView: View = headers.get(position) ?: adapter.getHeaderViewForPosition(
|
||||
context,
|
||||
position
|
||||
)
|
||||
|
||||
// Do not translate it as we want it sticky to the top unless in contact with next header
|
||||
if (nextHeaderTopPosition > 0 && nextHeaderTopPosition <= (headerView.height * 2)) {
|
||||
val top = nextHeaderTopPosition - (headerView.height * 2)
|
||||
canvas.translate(0f, top)
|
||||
}
|
||||
|
||||
headerView.draw(canvas)
|
||||
canvas.restore()
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface HeaderAdapter {
|
||||
|
|
|
|||
|
|
@ -185,24 +185,6 @@
|
|||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/no_contacts_nor_suggestion_image" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
style="@style/section_header_style"
|
||||
android:id="@+id/contacts_and_suggestions_label"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:padding="5dp"
|
||||
android:text="@string/call_start_contacts_list_title"
|
||||
android:visibility="@{viewModel.contactsAndSuggestionsList.size() == 0 ? View.GONE : View.VISIBLE}"
|
||||
app:layout_constraintVertical_chainStyle="packed"
|
||||
app:layout_constraintVertical_bias="0"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/group_call_icon"
|
||||
app:layout_constraintBottom_toTopOf="@id/contacts_and_suggestions_list"/>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/contacts_and_suggestions_list"
|
||||
android:layout_width="0dp"
|
||||
|
|
@ -211,7 +193,7 @@
|
|||
android:visibility="@{viewModel.contactsAndSuggestionsList.size() == 0 ? View.GONE : View.VISIBLE}"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/contacts_and_suggestions_label"
|
||||
app:layout_constraintTop_toBottomOf="@id/group_call_icon"
|
||||
app:layout_constraintBottom_toBottomOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
|
|
|||
|
|
@ -1,9 +1,21 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.appcompat.widget.AppCompatTextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
style="@style/section_header_style"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="21dp"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingBottom="16dp"
|
||||
android:text="@string/call_start_suggestions_list_title"/>
|
||||
<layout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<data>
|
||||
<import type="android.view.View" />
|
||||
</data>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
style="@style/section_header_style"
|
||||
android:id="@+id/header"
|
||||
android:background="@color/white"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="21dp"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingBottom="16dp"
|
||||
android:text="@string/call_start_suggestions_list_title"
|
||||
android:gravity="center_vertical"/>
|
||||
|
||||
</layout>
|
||||
Loading…
Add table
Reference in a new issue