I'm trying to get my RecyclerView adapter to obtain values from an Array in my fragment but for some reason is doesn't work and I get this error (I think I have gotten lost somewhere):
lateinit property dataTitles has not been initialized
What's the correct way to obtain the data in this scenario?
Fragment class
class MyFragment : androidx.fragment.app.Fragment() {
private lateinit var mRecyclerView: RecyclerView
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_rv, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
val v = view
val dataTitles: ArrayList<String> = ArrayList()
val dataSubtitles: ArrayList<String> = ArrayList()
mRecyclerView = v!!.findViewById<RecyclerView>(R.id.my_recyclerview)
// Set the linear layout manager
mRecyclerView.layoutManager = LinearLayoutManager(activity)
// create the adapter
val mAdapter = MyRVAdapter()
// init data
dataTitles.add(getString(R.string.addition))
dataTitles.add(getString(R.string.subtraction))
dataTitles.add(getString(R.string.multiplication))
dataTitles.add(getString(R.string.division))
dataSubtitles.add(getString(R.string.addition_information))
dataSubtitles.add(getString(R.string.subtraction_information))
dataSubtitles.add(getString(R.string.multiplication_information))
dataSubtitles.add(getString(R.string.division_information))
// Set the adapter
mRecyclerView.adapter = mAdapter
super.onActivityCreated(savedInstanceState)
}
}
Adapter class
class MyRVAdapter(private val dataTitles: Array<String>, private val dataSubtitles: Array<String>): RecyclerView.Adapter<MyRVAdapter.MyViewHolder>() {
private val typeItem = 1
private val typeHeader = 2
private val typeGrid = 3
private val primeNumbers = arrayOf("2", "3", "5", "7", "11", "13",
"17", "19", "23")
private lateinit var adapterGV: MyAdapter
override fun onCreateViewHolder(parent: ViewGroup, type: Int): MyRVAdapter.MyViewHolder {
return when (type) {
typeHeader -> MyRVAdapter.MyViewHolder(inflateHelper(R.layout.rv_header, parent))
typeItem -> MyRVAdapter.MyViewHolder(inflateHelper(R.layout.rv_item, parent))
typeGrid -> MyRVAdapter.MyViewHolder(inflateHelper(R.layout.rv_gv, parent))
else -> MyRVAdapter.MyViewHolder(inflateHelper(R.layout.rv_item, parent))
}
}
override fun onBindViewHolder(viewHolder: MyRVAdapter.MyViewHolder, position: Int) {
val llGV = viewHolder.itemView.findViewById<LinearLayout>(R.id.ll_gv)
val llTV = viewHolder.itemView.findViewById<LinearLayout>(R.id.ll_tv)
val mGridView = viewHolder.itemView.findViewById<GridView>(R.id.my_gv)
when (getItemViewType(position)) {
typeHeader -> {
val tValueE = TypedValue()
// test
llTV.context.theme.resolveAttribute(R.attr.imgUnfoldMore, tValueE, true)
val tValueC = TypedValue()
// test
llTV.context.theme.resolveAttribute(R.attr.imgUnfoldLess, tValueC, true)
}
typeGrid -> {
val titleG = viewHolder.itemView.findViewById<TextView>(R.id.tv_gv_A)
titleG.setText(R.string.prime_numbers)
//
val cardViewGV = viewHolder.itemView.findViewById<CardView>(R.id.cv_gv)
val mLinearLayoutGV = viewHolder.itemView.findViewById<LinearLayout>(R.id.cardview_gv_titlerow
mGridViewA.isEnabled = false
mGridViewA.isVerticalScrollBarEnabled = false
// adapterGV = MyAdapter(activity!!.applicationContext, 0)
mGridViewA.adapter = adapterGV
for (ldnBusesRoute in ldnBusesRoutes) {
adapterGV.addAdapterItem(AdapterItem(ldnBusesRoute))
}
typeItem -> {
// get the current item
val itemA = dataTitles[position - 2]
val itemB = dataSubtitles[position - 2]
//
val txtTitle = viewHolder.itemView.findViewById<TextView>(R.id.tv_title)
txtTitle.text = itemA
val txtSubtitle = viewHolder.itemView.findViewById<TextView>(R.id.tv_subtitle)
txtSubtitle.text = itemB
}
}
}
private fun inflateHelper(resId: Int, parent: ViewGroup): View {
return LayoutInflater.from(parent.context).inflate(resId, parent, false)
}
// inner class for view holder to use,
class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
// Adapter for GridView
private inner class MyAdapter internal constructor(context: Context, textviewid: Int) : ArrayAdapter<AdapterItem>(context, textviewid) {
private val items = ArrayList<AdapterItem>()
internal fun addAdapterItem(item: AdapterItem) {
items.add(item)
}
override fun getCount(): Int {
return items.size
}
override fun getItem(position: Int): AdapterItem? {
return items[position]
}
override fun getItemId(position: Int): Long {
return position.toLong()
}
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
val rowView: View = when (convertView) {
null -> LayoutInflater.from(parent.context).inflate(R.layout.gridview_item, parent, false)
// activity!!.layoutInflater.inflate(R.layout.gridview_item, parent, false)
else -> convertView
}
val tv = rowView.findViewById(R.id.item_gridview) as TextView
tv.text = items[position].first
return rowView
}
}
internal inner class AdapterItem // add more items
(var first: String)
override fun getItemCount(): Int {
return dataTitles.size + 2
}
override fun getItemViewType(position: Int): Int {
return when (position) {
0 -> typeHeader
1 -> typeGrid
else -> typeItem
}
}
}
Well, the error is pretty straightforward: you're not initializing dataTitles. When you label a variable with lateinit, Kotlin expects you to initialize it later. It's a way to avoid having to declare the variable as nullable.
To initialize it, you can do something like:
private val mutableList = mutableListOf<String>()
And you're doing the same with dataSubtitles.
Related
I am creating a GenericAdapter for handling single row|item layouts. Everything working fine only view-binding is not updating data..
I want to get RecyclerView.ViewHolder binding in callback ,I know I can bind it in adapter using BR.item and executePending
I want viewDataBinding context in a Callback
holder.binding.name.text = mutableList[pos]
Above line in TestActivity not working properly
GenericAdapter.kt
class GenericAdapter<T,VB:ViewDataBinding>(
var items:MutableList<T>,
#LayoutRes val resLayoutID:Int,
val onBind:(holder:GenericViewHolder<T,VB>,pos:Int) -> Unit
): RecyclerView.Adapter<GenericViewHolder<T,VB>>() {
lateinit var mItemBinding:VB
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): GenericViewHolder<T,VB> {
val layoutInflater = LayoutInflater.from(parent.context)
mItemBinding = DataBindingUtil.inflate(layoutInflater, resLayoutID, parent, false)
return GenericViewHolder(mItemBinding)
}
override fun onBindViewHolder(holder: GenericViewHolder<T,VB>, position: Int) {
onBind(holder,position)
}
override fun getItemCount(): Int = items.size
}
GenericViewHolder.kt
class GenericViewHolder<T,VB: ViewDataBinding>(val binding: VB)
:RecyclerView.ViewHolder(binding.root){
val mItemBinding:VB = binding
}
TestActivity.kt
class TestActivity:AppCompatActivity() {
lateinit var recyclerView: RecyclerView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_test)
recyclerView = findViewById(R.id.recyclerView)
recyclerView.layoutManager = LinearLayoutManager(this)
recyclerView.setHasFixedSize(true)
populateList()
}
private fun populateList(){
val mutableList = mutableListOf<String>("apple","mango","tutti fruti","apricot",
"apple","mango","tutti fruti","apricot",
"apple","mango","tutti fruti","apricot",
"apple","mango","tutti fruti","apricot")
val mAdapter = GenericAdapter<String,ItemCountryBinding>(mutableList,R.layout.item_country){ holder,pos ->
//val nameTv = holder.itemView.findViewById<TextView>(R.id.name)
//nameTv.text = mutableList[pos]
holder.binding.name.text = mutableList[pos]
}
recyclerView.adapter = mAdapter
}
}
Where as below code working fine
val nameTv = holder.itemView.findViewById<TextView>(R.id.name)
nameTv.text = mutableList[pos]
I would recommend do onBind inside view holder.
I don't see reason why you need this callback.
You already passed mutableList to the adapter.
For example here is my ViewHolder
class NewsViewHolder(private val binding: ListItemNewsBinding) : RecyclerView.ViewHolder(binding.root) {
fun bind(news: RedditPost, itemClick: (RedditPost, View) -> Unit) {
with(binding) {
root.transitionName = news.thumbnail
root.setOnClickListener { itemClick.invoke(news, binding.root) }
title.text = news.title
image.setImageUrl(news.thumbnail)
author.text = news.author
xHoursAgo.text = news.created_utc
numComments.text = news.numComments
}
}
}
And
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
if (isLoadingMore && position == (itemCount - 1)) return
(holder as? NewsViewHolder)?.bind(news[position], itemClick)
}
i have defined a val in my fragment like so
private var selectionMode: Boolean = false
and a property to change it
fun enableSelectionMode() {
selectionMode = true
}
and i passed both to Adapter and want to call enableSelectionMode after clicking on each recycler item but nothing happen
here is my fragment:
class PostsFragment : Fragment() {
private val model: PostsViewModel by activityViewModels()
private var selectionMode: Boolean = false
fun enableSelectionMode() {
selectionMode = true
}
private lateinit var recyclerView: RecyclerView
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_posts, container, false)
recyclerView = view.findViewById(R.id.posts_recycler)
recyclerView.layoutManager = LinearLayoutManager(view.context)
model.posts.observe(viewLifecycleOwner, { items ->
recyclerView.adapter = PostsAdapter(items, model, selectionMode,
::enableSelectionMode)
})
return view
}
}
and my adapter
class PostsAdapter(
private val posts: List<Post>,
private val model: PostsViewModel,
private val selectionMode: Boolean,
private val enableIt: () -> Unit
) :
RecyclerView.Adapter<PostsAdapter.ViewHolder>() {
companion object {
private const val TYPE_FROM = 0
private const val TYPE_TO = 1
}
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var postContent: TextView = itemView.findViewById(R.id.post_content)
var selectPostCheckBox: ImageView = itemView.findViewById(R.id.post_select_checkbox)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(
if (viewType == TYPE_TO) R.layout.to_post else R.layout.from_post,
parent,
false
)
return ViewHolder(view)
}
#SuppressLint("CheckResult")
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.selectPostCheckBox.visibility = if (selectionMode) View.VISIBLE else View.GONE
holder.postContent.text = selectionMode.toString()
holder.selectPostCheckBox.setImageResource(R.drawable.cursor_color)
holder.itemView.setOnClickListener() {
enableIt()
notifyDataSetChanged()
}
}
override fun getItemCount() = posts.size
override fun getItemViewType(position: Int): Int {
return posts[position].selfPost
}
}
I'm building quiz app and all the time I have trouble with notifying RecyclerView about updated item.
DiffUtil didn't work for me, anyway - I can use adapter.notifyItemChanged() but after this call my TabLayout jumps to start of it's width.
I have set onClickListener to my floatingButton to be sure that it's notifyItemChanged() issue.
Am I doing something wrong?
Fragment
class TestFragment : Fragment() {
private lateinit var viewModel: TestViewModel
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val binding: FragmentTestBinding = DataBindingUtil.inflate(
inflater,
R.layout.fragment_test,
container,
false
)
val args = TestFragmentArgs.fromBundle(requireArguments())
val viewModelFactory = TestViewModelFactory(args.course)
viewModel = ViewModelProvider(this, viewModelFactory).get(TestViewModel::class.java)
binding.viewModel = viewModel
binding.lifecycleOwner = viewLifecycleOwner
val adapter = PagerAdapter(ClickListener { questionDatabase: QuestionDatabase, string: String ->
val questionUpdated = questionDatabase.apply { answer = string }
viewModel.updateAnswer(questionDatabase, questionUpdated)
})
binding.viewPager.adapter = adapter
TabLayoutMediator(binding.tabLayout, binding.viewPager) { tab: TabLayout.Tab, i: Int ->
tab.text = (i + 1).toString()
}.attach()
viewModel.pytania.observe(viewLifecycleOwner, Observer {
adapter.differ.submitList(it)
})
viewModel.score.observe(viewLifecycleOwner, Observer {
if (it != null) {
findNavController().navigate(
TestFragmentDirections.actionTestFragmentToScoreFragment(it)
)
viewModel.score.value = null
}
})
binding.bttnFinish.setOnClickListener {
adapter.notifyItemChanged(binding.viewPager.currentItem)
}
return binding.root
}
}
ViewModel
class TestViewModel(
test: String
): ViewModel() {
private val _pytania = MutableLiveData<List<QuestionDatabase>>()
val pytania: LiveData<List<QuestionDatabase>>
get() = _pytania
val score = MutableLiveData<Int?>()
init {
val listOfQuestions = mutableListOf<QuestionDatabase>()
for (x in 1..15) {
listOfQuestions.add(
QuestionDatabase(
question = "Question $x",
answerA = "AnswerA",
answerB = "AnswerB",
answerC = "AnswerC",
answerD = "AnswerD",
correctAnswer = "AnswerD",
image = 0,
questionNumber = 0
)
)
}
_pytania.value = listOfQuestions.toList()
}
fun updateAnswer(questionDatabase: QuestionDatabase, questionUpdated: QuestionDatabase) {
_pytania.value = _pytania.value?.replace(questionDatabase, questionUpdated)
}
}
ViewPagerAdapter
class PagerAdapter(
private val clickListener: ClickListener
) : RecyclerView.Adapter<ViewHolder>() {
private val differCallBack = object : DiffUtil.ItemCallback<QuestionDatabase>() {
override fun areItemsTheSame(oldItem: QuestionDatabase, newItem: QuestionDatabase): Boolean {
return oldItem.question == newItem.question
}
override fun areContentsTheSame(oldItem: QuestionDatabase, newItem: QuestionDatabase): Boolean {
return oldItem == newItem
}
}
val differ = AsyncListDiffer(this, differCallBack)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder.from(parent)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(differ.currentList[position], clickListener)
}
override fun getItemCount(): Int {
return differ.currentList.size
}
}
class ViewHolder private constructor(val binding: VpItemQuestionBinding): RecyclerView.ViewHolder(
binding.root
) {
fun bind(
question: QuestionDatabase,
clickListener: ClickListener
) {
binding.question = question
binding.onClickListener = clickListener
binding.executePendingBindings()
}
companion object {
fun from(parent: ViewGroup): ViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val binding = VpItemQuestionBinding.inflate(layoutInflater, parent, false)
return ViewHolder(binding)
}
}
}
class ClickListener(val clickListener: (QuestionDatabase, string: String) -> Unit) {
fun onClick(question: QuestionDatabase, string: String) = clickListener(question, string)
}
Before notifyItemChanged()
After notifyItemChanged()
I have list_detail that contains DataDay and DataDetail objects inherit DetailType abstract class.
list_detail has DataDay object in its zero index, following indexes have DataDetail objects.
like below,,
abstract class DetailType {
abstract val type: Int
companion object {
val DAY_TYPE = 1
val DETAIL_TYPE = 2
}
}
data class DataDay(
var day: Long? = null
): DetailType() {
override val type: Int
get() = DetailType.DAY_TYPE
}
data class DataDetail(
var moneyUsed: Double? = null,
var type_used: String? = null
): DetailType() {
override val type: Int
get() = DetailType.DETAIL_TYPE
}
The problem is, when I use recyclerview to make list_detail show in order by their index,
their viewType doesn't match correctly at onCreateViewHolder and onBindViewHolder.
Zero Index in list_detail that have DataDay Object matches its viewType(DAY_TYPE). So it is not the problem.
Index over zero (index 1, 2, 3...), it's data on list should have DataDetail Object and their viewType also should have DETAIL_TYPE I think.. BUT, it doesn't enter onCreateViewHolder() or onBindViewHolder().. pass those methods, also not shown at screen! (Not produce any error on logcat)
Below is my code
class ContentActivity : AppCompatActivity(), View.OnClickListener {
val helper = DBHelper(this)
var list: MutableList<Long> = mutableListOf()
var list_detail: MutableList<DetailType> = mutableListOf()
var selected_day: Long? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_content)
selectContentDayDB()
selectMoneyDB()
}
fun selectContentDayDB() {
list = mutableListOf()
val db = helper.writableDatabase
val num = intent.getIntExtra("num", 0)
val cursor = db.rawQuery("select start_day, end_day from t_travel where num=?", arrayOf(num.toString()))
cursor.moveToNext()
val start = cursor.getLong(0)
val end = cursor.getLong(1) + 1000
for(i in start .. end step 24*60*60*1000) {
list.add(i)
}
recycler.layoutManager = LinearLayoutManager(this)
recycler.adapter = ContentAdapter(list)
}
inner class ContentViewHolder(view: View): RecyclerView.ViewHolder(view) {
val content_day = view.content_day
val content_month = view.content_month
}
inner class DateViewHolder(view: View): RecyclerView.ViewHolder(view) {
val content_day_text = view.content_day_text
}
inner class DetailViewHolder(view: View): RecyclerView.ViewHolder(view) {
val content_detail_text = view.content_detail_text
val content_type_text = view.content_type_text
val content_image = view.content_image
}
inner class ContentAdapter(val list: MutableList<Long>): RecyclerView.Adapter<ContentViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ContentViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
return ContentViewHolder(layoutInflater.inflate(R.layout.item_content, parent, false))
}
override fun getItemCount(): Int {
return list.size
}
override fun onBindViewHolder(holder: ContentViewHolder, position: Int) {
val item = list[position]
holder.content_day.text = SimpleDateFormat("d").format(item).toString()
holder.content_month.text = SimpleDateFormat("M").format(item).toString()
holder.itemView.setOnClickListener {
list_detail = mutableListOf()
list_detail.add(DataDay(item))
selectDetailDB(item)
}
}
}
fun selectDetailDB(item: Long) {
val db = helper.writableDatabase
val num = intent.getIntExtra("num", 0)
val datecode = SimpleDateFormat("yyMMdd").format(item)
val isExistSQL = db.rawQuery("select count(*) from t_content where num=? and datecode=?",
arrayOf(num.toString(), datecode))
isExistSQL.moveToNext()
val isExist = isExistSQL.getInt(0)
if(isExist != 0){
val cursor = db.rawQuery("select type, moneyUsed from t_content where num=? and datecode=?",
arrayOf(num.toString(), datecode))
while(cursor.moveToNext()) {
val moneyUsed = cursor.getDouble(1)
val type = cursor.getString(0)
list_detail.add(DataDetail(moneyUsed, type))
}
}
recycler_content.layoutManager = LinearLayoutManager(this)
recycler_content.adapter = DetailAdapter(list_detail)
}
inner class DetailAdapter(val list: MutableList<DetailType>): RecyclerView.Adapter<RecyclerView.ViewHolder>() {
override fun getItemViewType(position: Int): Int {
return list[position].type
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
when{
viewType == DetailType.DAY_TYPE -> {
val layoutInflater = LayoutInflater.from(parent.context)
return DateViewHolder(layoutInflater.inflate(R.layout.item_content_day, parent, false))
}
else -> {
val layoutInflater = LayoutInflater.from(parent.context)
return DetailViewHolder(layoutInflater.inflate(R.layout.item_content_detail, parent, false))
}
}
}
override fun getItemCount(): Int {
Log.d("ContentAct: getItem", list.size.toString())
return list.size
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val itemType = list[position]
when(itemType.type){
DetailType.DAY_TYPE -> {
val viewHolder = holder as DateViewHolder
val item = itemType as DataDay
viewHolder.content_day_text.text = SimpleDateFormat("yyyy MM dd").format(item.day)
}
DetailType.DETAIL_TYPE -> {
val viewHolder = holder as DetailViewHolder
val item = itemType as DataDetail
viewHolder.content_detail_text.text = item.moneyUsed.toString()
viewHolder.content_type_text.text = item.type_used
...
}
}
}
}
}
}
}
I solved it by myself.
The reason is in my layout xml.
My item_content_day.xml in recyclerView has height:match-parent property in all covered layout.
I can see my view when i scrolled down.. It was in below the screen.
If you have same situation like mine, Please check your layout.xml
I'm trying to use a RecyclerView to show an array of values in CardViews in a specific order i.e. "Title A" & "Subtitle A" in 1 CardView; "Title B" & "Subtitle B" in another. Is there a way to obtain the values from the arrays in my fragment without having to create a new verbose class, or do I have to use a data class to achieve this?
Fragment class
class MyFragment : androidx.fragment.app.Fragment() {
private lateinit var mRecyclerView: RecyclerView
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_rv, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
val v = view
mRecyclerView = v!!.findViewById<RecyclerView>(R.id.my_recyclerview)
mRecyclerView.layoutManager = LinearLayoutManager(activity)
val myList = mutableListOf(
RVAdapterTConnections.ITEM_A,
RVAdapterTConnections.ITEM_B,
RVAdapterTConnections.ITEM_B
)
val titlesList = mutableListOf(
"Title A",
"Title B"
)
val subtitlesList = mutableListOf(
"Subtitle A",
"Subtitle B"
)
val mAdapter = MyRVAdapter(myList, titlesList)
mRecyclerView.adapter = mAdapter
super.onActivityCreated(savedInstanceState)
}
}
Adapter class
internal class MyRVAdapter(private val listViewType: List<Int>, private val myList: List<CharSequence>) : RecyclerView.Adapter<MyRVAdapter.ViewHolder>() {
companion object {
const val ITEM_A = 1
const val ITEM_B = 2
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val inflater = LayoutInflater.from(parent.context)
return when (viewType) {
ITEM_A -> ViewHolderItemA(inflater.inflate(R.layout.layout_item_a, parent, false))
else -> ViewHolderItemB(inflater.inflate(R.layout.layout_item_b, parent, false))
}
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val viewType = listViewType[position]
when (viewType) {
ITEM_A -> {
val viewHolderA = holder as ViewHolderItemA
viewHolderA.textView.text = "Lorem Ipsum"
}
ITEM_B -> {
val viewHolderB = holder as ViewHolderItemB
viewHolderB.ivExpandCollapse.setImageDrawable(ContextCompat.getDrawable(holder.ivExpandCollapse.context, R.drawable.ic_keyboard_arrow_down))
viewHolderB.tvTitle.text = titlesList[position]
viewHolderB.tvSubtitle.text = subtitlesList[position]
}
else -> {
val viewHolderA = holder as ViewHolderItemA
viewHolderA.textView.text = "Lorem Ipsum"
}
}
}
override fun getItemCount(): Int = listViewType.size
override fun getItemViewType(position: Int): Int = listViewType[position]
open inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
inner class ViewHolderItemA(itemView: View) : ViewHolder(itemView) {
val textView: TextView = itemView.findViewById(R.id.my_custom_tv)
}
inner class ViewHolderItemB(itemView: View) : ViewHolder(itemView) {
val tvTitle: TextView = itemView.findViewById(R.id.tv_title)
val tvSubtitle: TextView = itemView.findViewById(R.id.tv_subtitle)
}
}
Data class
data class MyItem (val title: String, val subtitle: String)
the view-type ordinary can be derived from the data model, most commonly from some type of groupId. these titlesList and subtitlesList need to be replaced; use BaseAdapter to populate the RecyclerView.Adapter. for example (it loads string resources, where SpinnerItem is a simple data model):
abstract class BaseArrayAdapter : BaseAdapter {
private var mItems: ArrayList<SpinnerItem>? = null
private var layoutInflater: LayoutInflater? = null
internal constructor(#NonNull context: Context) {
this.layoutInflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
}
internal constructor(#NonNull context: Context, #NonNull #ArrayRes arrayKeys: Int, #NonNull #ArrayRes arrayValues: Int) {
this.layoutInflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
this.setItems(context, arrayKeys, arrayValues)
}
#NonNull
#Override
override fun getItem(position: Int): SpinnerItem {
return this.mItems!!.get(position)
}
#Override
override fun getItemId(position: Int): Long {
return this.mItems!!.get(position).id!!
}
#NonNull
#Override
override fun getView(position: Int, #Nullable view: View?, #NonNull parent: ViewGroup): View {
var convertView = view
if (convertView == null) {
convertView = this.layoutInflater!!.inflate(R.layout.support_simple_spinner_dropdown_item, parent, false)
}
convertView!!.setTag(this.mItems!!.get(position))
val textView: TextView = convertView.findViewById(android.R.id.text1)
textView.text = this.mItems!![position].name
textView.setAllCaps(true)
return convertView
}
private fun clearItems() {
if (this.mItems != null) {
this.mItems!!.clear()
} else {
this.mItems = ArrayList()
}
}
internal fun setItems(#NonNull context: Context, #NonNull #ArrayRes arrayKeys: Int, #NonNull #ArrayRes arrayValues: Int) {
this.clearItems()
val res = context.getResources()
val keys = res.getStringArray(arrayKeys)
val values = res.getStringArray(arrayValues)
for (i in keys.indices) {
this.mItems!!.add(i, SpinnerItem((i + 1).toLong(), keys[i], values[i]))
}
}
override fun getCount(): Int {
return this.mItems!!.size
}
}
some sub-class of the abstract class:
class SomeAdapter(#NonNull context: Context) : BaseArrayAdapter(context) {
init {
this.setItems(context, R.array.some_keys, R.array.some_values)
}
}
and then in the RecyclerView.Adapter:
init {
this.setItems(new SomeAdapter(context));
}