I'm gonna add a button at the end of my recycler view and i don't know how to do it . i will appreciate you if you teach me .
this is my main activity code :
class MainActivity : AppCompatActivity() {
private var number: Int = 0
private lateinit var name: String
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val myRecyclerView = findViewById<RecyclerView>(R.id.recyclerView)
val myViewModel = ViewModel()
myRecyclerView.layoutManager = LinearLayoutManager(this)
myViewModel.getUserData()
myViewModel.myList.observe(this) {
myRecyclerView.adapter = MyAdapter(it)
}
}
and this is my adapter code :
class MyAdapter(private val myList: List<Player>) : RecyclerView.Adapter<MyHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyHolder {
val myLayoutInflater = LayoutInflater.from(parent.context)
val myView = myLayoutInflater.inflate(R.layout.view_for_rv, parent, false)
return MyHolder(myView)
}
override fun onBindViewHolder(holder: MyHolder, position: Int) {
val item = myList[position]
holder.bind(item)
}
override fun getItemCount(): Int {
return myList.size
}
and my holder class :
class MyHolder(view: View) : RecyclerView.ViewHolder(view) {
private val playerNumber = view.findViewById<TextView>(R.id.tvNumber)
private val playerName = view.findViewById<TextView>(R.id.tvName)
fun bind(item : Player){
playerName.text=item.name
playerNumber.text=item.number.toString()
}
CodePudding user response:
You will achieve this by adding the RecyclerView
in a parent NestedScrollView
and then place the Button
below the RecyclerView
.
So your code should look like this
<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</androidx.core.widget.NestedScrollView>
NOTE
Add
recyclerView.nestedScrollingEnabled = false
for scroll to work for sdk 21
CodePudding user response:
add a new itemType is good selection。
class MyAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
companion object {
const val ITEM_TYPE_CONTENT = 0
const val ITEM_TYPE_BUTTON = 1
}
val dataList: List<String>? = null
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
if (viewType == ITEM_TYPE_CONTENT) {
// TODO return a content holder
}else {
// TODO return a button holder
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
TODO("Not yet implemented")
}
override fun getItemCount(): Int {
return (dataList?.size ?: 0) 1
}
override fun getItemViewType(position: Int): Int {
return if (position == itemCount - 1) {
ITEM_TYPE_BUTTON
} else {
ITEM_TYPE_CONTENT
}
}
}
CodePudding user response:
You can use this
package com.joon.fm.core.base.genericRecycler
import android.content.Context
import android.os.Handler
import android.os.Looper
import android.util.Log
import android.util.SparseArray
import android.view.View
import android.view.ViewGroup
import androidx.core.util.set
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import com.joon.fm.core.extensions.getClass
import com.joon.fm.features.explore.section.SectionExploreNewReleases
import java.lang.reflect.Constructor
import java.util.concurrent.atomic.AtomicInteger
interface GenericAdapterView<Model> {
fun onBind(model: Model, position: Int, extraObject: Any?)
}
data class Section<Model>(
var type: Int,
var data: Model,
var extraObject: Any?,
var index: Int = -1
)
open class VH(view: View) : RecyclerView.ViewHolder(view)
interface DiffUtilModel {
fun areItemsTheSame(o: DiffUtilModel): Boolean
fun areContentsTheSame(o: DiffUtilModel): Boolean
}
class MyDiffCallback(private val oldList: List<Any?>, private val newList: List<Any?>) :
DiffUtil.Callback() {
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
val old = oldList[oldItemPosition]
val new = newList[newItemPosition]
if (old is DiffUtilModel && new is DiffUtilModel) {
return old.areItemsTheSame(new)
}
return oldList[oldItemPosition] == newList[newItemPosition]
}
override fun getOldListSize() = oldList.size
override fun getNewListSize() = newList.size
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
val old = oldList[oldItemPosition]
val new = newList[newItemPosition]
if (old is DiffUtilModel && new is DiffUtilModel) {
return old.areContentsTheSame(new)
}
return (old == null && new == null) || (old != null && old == new)
}
}
@Suppress("unused")
open class AppGenericAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
val sections = ArrayList<Section<*>>()
var providers = SparseArray<(ViewGroup, Int) -> View>()
private var types = HashMap<Class<*>, Pair<Int, Constructor<*>?>>()
private var typeIds = AtomicInteger(0)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return VH(providers[viewType](parent, viewType))
}
@Suppress("UNCHECKED_CAST")
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val v = holder.itemView as? GenericAdapterView<Any>
val sec = sections[position]
sec.data?.run {
v?.onBind(this, position, sec.extraObject)
}
}
override fun getItemCount() = sections.size
override fun getItemViewType(position: Int) = sections[position].type
inline fun <reified V : View> viewType(addDefaultProvider: Boolean = true) =
viewType(V::class.java, addDefaultProvider)
fun viewType(cls: Class<*>, addDefaultProvider: Boolean = true): Int {
val prevAns = types[cls]
if (prevAns == null) {
var cons: Constructor<*>? = null
try {
cons = cls.getDeclaredConstructor(Context::class.java)
} catch (ignored: Throwable) {
}
types[cls] = Pair(typeIds.getAndIncrement(), cons)
val type = types[cls]!!
if (addDefaultProvider && providers[type.first] == null) {
providers[type.first] = { vg, _ ->
type.second!!.newInstance(vg.context) as View
}
}
return type.first
} else {
return prevAns.first
}
}
inline fun <reified V> setProvider(noinline provider: (ViewGroup, Int) -> V) where V : View, V : GenericAdapterView<*> {
providers[viewType<V>(false)] = provider
}
inline fun <reified V> provider(noinline provider: (Context) -> V) where V : View, V : GenericAdapterView<*> {
providers[viewType<V>(false)] = { vg, _ -> provider(vg.context) }
}
inline fun <reified V> addProvider(noinline provider: (Context) -> V) where V : View, V : GenericAdapterView<*> {
providers.set(viewType<V>(false)) { vg, _ -> provider(vg.context) }
}
inline fun <reified V, reified Model : Any> setSections(
data: Collection<Model>,
extra: Any? = null
) where V : View, V : GenericAdapterView<Model> {
sections.clear()
addSections<V, Model>(data, extra)
}
inline fun <reified V, reified Model : Any> setSectionsDiffUtil(
data: Collection<Model>,
extra: Any? = null
) where V : View, V : GenericAdapterView<Model> {
val diffCallback = MyDiffCallback(sections.map { it.data }, data.toList())
val diffResult = DiffUtil.calculateDiff(diffCallback)
sections.clear()
addSections<V, Model>(data, extra)
diffResult.dispatchUpdatesTo(this)
}
fun setSectionsDiffUtil(newSections: Collection<Section<*>>) {
val diffCallback = MyDiffCallback(sections.map { it.data }, newSections.map { it.data })
val diffResult = DiffUtil.calculateDiff(diffCallback)
sections.clear()
sections.addAll(newSections)
diffResult.dispatchUpdatesTo(this)
}
inline fun <reified V, reified Model : Any> setSectionsAndNotify(
data: Collection<Model>,
extra: Any? = null
) where V : View, V : GenericAdapterView<Model> {
setSections<V, Model>(data, extra)
notifyDataSetChanged()
}
fun removeItemFromSectionsAndNotify(
index: Int,
) {
sections.removeAt(index)
notifyDataSetChanged()
}
inline fun <reified V, reified Model : Any> setSectionsAndNotifyRange(
data: Collection<Model>,
extra: Any? = null
) where V : View, V : GenericAdapterView<Model> {
val prevSize = itemCount
setSections<V, Model>(data, extra)
val newSize = itemCount
notifyItemRangeChanged(0, newSize)
if (prevSize > newSize) {
notifyItemRangeRemoved(newSize, prevSize - newSize)
}
}
inline fun <reified V, reified Model : Any> addSections(
data: Collection<Model>,
extra: Any? = null
) where V : View, V : GenericAdapterView<Model> {
val type = viewType<V>()
sections.addAll(data.map { Section(type, it, extra) })
}
inline fun <reified V> addView(
) where V : View {
val type = viewType<V>()
sections.add(Section(viewType<V>(), null, null))
}
inline fun <reified V, reified Model : Any> addSectionsAndNotify(
data: Collection<Model>,
extra: Any? = null
) where V : View, V : GenericAdapterView<Model> {
addSections<V, Model>(data, extra)
notifyDataSetChanged()
}
inline fun <reified V, reified Model : Any> addSectionsAndNotifyRange(
data: Collection<Model>,
extra: Any? = null
) where V : View, V : GenericAdapterView<Model> {
addSections<V, Model>(data, extra)
notifyItemRangeChanged(itemCount - data.size, itemCount)
}
inline fun <reified V, reified Model : Any> addSectionsAndNotifyCompleteRange(
data: Collection<Model>,
extra: Any? = null
) where V : View, V : GenericAdapterView<Model> {
addSections<V, Model>(data, extra)
notifyItemRangeChanged(itemCount - data.size, itemCount)
}
inline fun <reified V, reified Model : Any?> setSection(
position: Int,
data: Model,
extra: Any? = null
) where V : View, V : GenericAdapterView<Model> {
sections[position] = Section(viewType<V>(), data, extra)
}
inline fun <reified V, reified Model : Any?> setSectionAndNotify(
position: Int,
data: Model,
extra: Any? = null
) where V : View, V : GenericAdapterView<Model> {
setSection<V, Model>(position, data, extra)
notifyItemChanged(position)
}
fun updateSection(
position: Int,
) {
notifyItemChanged(position)
Handler(Looper.getMainLooper()).post {
notifyItemChanged(position)
}
}
inline fun <reified V, reified Model : Any> addSection(
data: Model,
extra: Any? = null
) where V : View, V : GenericAdapterView<Model> {
sections.add(Section(viewType<V>(), data, extra))
}
inline fun <reified V, reified Model : Any> addSectionAndNotify(
data: Model,
extra: Any? = null
) where V : View, V : GenericAdapterView<Model> {
addSection<V, Model>(data, extra)
notifyItemInserted(sections.size - 1)
}
inline fun <reified V, reified Model : Any?> createEmptySection(
data: Model?,
extra: Any? = null
): Section<Model?> where V : View, V : GenericAdapterView<Model> =
Section(viewType<V>(), data, extra)
inline fun <reified V, reified Model : Any> createSection(
data: Model,
extra: Any? = null
): Section<Model> where V : View, V : GenericAdapterView<Model> =
Section(viewType<V>(), data, extra)
inline fun <reified V, reified Model : Any> addSection(
position: Int,
data: Model,
extra: Any? = null
) where V : View, V : GenericAdapterView<Model> {
sections.add(position, Section(viewType<V>(), data, extra))
}
inline fun <reified V, reified Model : Any?> addEmpotySection(
position: Int,
data: Model? = null,
extra: Any? = null
) where V : View, V : GenericAdapterView<Model> {
val type = viewType<V>()
if (sections.find { it.type == type } != null) {
sections.add(position, Section(viewType<V>(), data, extra))
val index = sections.indexOfLast { it.type == type }
providers.delete(index)
sections.removeAt(index)
notifyItemRangeChanged(index-1,1)
} else {
sections.add(position, Section(viewType<V>(), data, extra))
notifyItemInserted(position)
}
}
inline fun <reified V, reified Model : Any> addSectionAndNotify(
position: Int,
data: Model,
extra: Any? = null
) where V : View, V : GenericAdapterView<Model> {
addSection<V, Model>(position, data, extra)
notifyItemInserted(position)
}
inline fun <reified V, reified Model : Any> createSections(
data: Collection<Model>,
extra: Any? = null
): List<Section<Model>> where V : View, V : GenericAdapterView<Model> =
data.map { Section(viewType<V>(), it, extra) }
fun createSection(
cls: Class<*>,
data: Any,
extra: Any? = null
): Section<*> =
Section(viewType(cls), data, extra)
fun clearData() {
sections.clear()
}
fun clearDataAndNotify() {
sections.clear()
providers.clear()
notifyDataSetChanged()
}
inline fun <reified V : View> clearViews() {
val type = viewType<V>()
sections.removeAll { it.type == type }
}
inline fun <reified V : View> findIndexItem(): Int {
val type = viewType<V>()
var item = 0
sections.forEachIndexed { index, section ->
if (section.type == type) {
item = index
}
}
return item
}
inline fun <reified V : View> findIndexItemNew(): Int? {
val type = viewType<V>()
var item: Int? = null
sections.forEachIndexed { index, section ->
if (section.type == type) {
item = index
}
}
return item
}
inline fun <reified V : View> clearSectionsWithTypeAndNotify() {
clearViews<V>()
notifyItemRemoved(viewType<V>(false))
providers.delete(viewType<V>(false))
}
inline fun <reified T> sectionsWithType(): List<Section<T>> =
sections.mapIndexed { index, section -> section.index = index; section }
.filter { it.data is T }.map { it as Section<T> }
}
fun AppGenericAdapter.setSectionsInViewsListAndTryToRecycle(
parent: ViewGroup,
lpProvider: (AppGenericAdapter.(Section<*>, Int) -> ViewGroup.LayoutParams)? = null
) {
for (i in 0 until sections.size) {
val section = sections[i]
if (i < parent.childCount) {
try {
val child = parent.getChildAt(i) as GenericAdapterView<Any>
child.onBind(section.data as Any, i, section.extraObject)
} catch (e: Exception) {
parent.removeAllViews()
setSectionsInViewsListAndTryToRecycle(parent)
}
} else {
val vh = onCreateViewHolder(parent, section.type)
if (vh.itemView is GenericAdapterView<*>) {
(vh.itemView as GenericAdapterView<Any>).onBind(
section.data!!,
i,
section.extraObject
)
}
if (lpProvider != null) {
vh.itemView.layoutParams = lpProvider(section, i)
}
parent.addView(vh.itemView)
}
}
if (parent.childCount - sections.size > 0) {
parent.removeViews(sections.size, parent.childCount - sections.size)
}
}
This is a Generic adapter class you can use in all project instances of a simple adapter. you just add an item and pass to the generic class to create your list.
How to User First of all, you must add this class to your project.
abstract class BaseCustomView<viewDataBinding : ViewDataBinding> @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : RelativeLayout(context, attrs, defStyleAttr) {
lateinit var binding: viewDataBinding
init {
this.inflate(context, attrs)
}
@CallSuper
protected open fun inflate(context: Context, attrs: AttributeSet?){
val bindingClass = javaClass.findGenericWithType<viewDataBinding>(ViewDataBinding::class.java)
if(isInEditMode){
if(bindingClass != null){
val resId = bindingClass.layoutId(context)
LayoutInflater.from(context).inflate(resId, this, true)
}
} else {
binding = bindingClass?.getMethod("inflate", LayoutInflater::class.java, ViewGroup::class.java, Boolean::class.java)?.invoke(null, LayoutInflater.from(context), this, true) as viewDataBinding
binding.setView(this)
}
}
}
after that, your item (VH), must inheritance this class like this
class YourItem @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : BaseCustomView<YouBinding>(context, attrs, defStyleAttr),
GenericAdapterView<Model> {
override fun onBind(model: Model, position: Int, extraObject: Any?) {
}
}
and finally, append an item to the adapter
private val adapter = AppGenericAdapter().apply {
provider { context ->
YourItem (context,)// if you want to pass extra data you can pass data as arg
}
provider { context ->
YourItemButton(context,)// if you want to pass extra data you can pass data as arg
}
}
binding.recycler.adapter = adapter
adapter.addSectionsAndNotify<YourItem , Model>(data)
// you can check with if the last item add this item
adapter.addSectionsAndNotify<YourItemButton , Model>(data)