TabLayout jumps to start of its witdth after notifyItemChanged() - android

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()

Related

Arraylist loop unexpectedly on kotlin recycleview

Here is when the list increase
I call data from API and place it in recycleview horizontal and vertical, but when I try to move on Profile Menu with bottom navigation and back again on Home Menu where my horizontal and vertical recycleview is, the data on recycleview start to increase, it only happen for vertical recycleview
Here is my code:
SectionPagerAdapter for vertical recycleview
class SectionsPagerAdapter(fm: FragmentManager) :
FragmentPagerAdapter(
fm
) {
var new_craft: ArrayList<Data>? = ArrayList()
var popular: ArrayList<Data>? = ArrayList()
var recommended: ArrayList<Data>? = ArrayList()
override fun getItem(position: Int): Fragment {
var fragment : Fragment
return when (position) {
0 -> {
fragment = HomeNewCraftFragment()
val bundle = Bundle()
bundle.putParcelableArrayList("data", new_craft)
fragment.setArguments(bundle)
return fragment
}
1 -> {
fragment = HomePopularFragment()
val bundle = Bundle()
bundle.putParcelableArrayList("data", popular)
fragment.setArguments(bundle)
return fragment
}
2 -> {
fragment = HomeRecommendedFragment()
val bundle = Bundle()
bundle.putParcelableArrayList("data", recommended)
fragment.setArguments(bundle)
return fragment
}
else -> {
fragment = HomeNewCraftFragment()
val bundle = Bundle()
bundle.putParcelableArrayList("data", new_craft)
fragment.setArguments(bundle)
return fragment
}
}
}
override fun getPageTitle(position: Int): CharSequence? {
return when (position) {
0 -> "New Craft"
1 -> "Popular"
2 -> "Recommended"
else -> null
}
}
override fun getCount(): Int {
return 3
}
fun setData(newStateListParms: ArrayList<Data>?, popularListParms: ArrayList<Data>?, recomendedListParms: ArrayList<Data>?) {
new_craft = newStateListParms
popular = popularListParms
recommended = recomendedListParms
}
}
Home Fragment:
class HomeFragment : Fragment(), HomeAdapter.ItemAdapterCallback, HomeContract.View {
private var adapter: HomeAdapter? = null
var progressDialog: Dialog? = null
private var newStateList: ArrayList<Data>? = ArrayList()
private var popularList: ArrayList<Data>? = ArrayList()
private var recomendedList: ArrayList<Data>? = ArrayList()
private lateinit var presenter: HomePresenter
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?, savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_home, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
initView()
presenter = HomePresenter(this)
presenter.getHome()
}
private fun initView() {
progressDialog = Dialog(requireContext())
val dialogLayout = layoutInflater.inflate(R.layout.dialog_loader, null)
progressDialog?.let {
it.setContentView(dialogLayout)
it.setCancelable(false)
it.window?.setBackgroundDrawableResource(android.R.color.transparent)
}
var user = IcaCraft.getApp().getUser()
var userResponse = Gson().fromJson(user, User::class.java)
if (!userResponse.profile_photo_url.isNullOrEmpty()) {
Glide.with(requireActivity())
.load(userResponse.profile_photo_url)
.into(ivProfile)
}
}
override fun onClick(v: View?, data: Data) {
var bundle = Bundle()
bundle.putParcelable("data", data)
val detail = Intent(activity, DetailActivity::class.java).putExtras(bundle)
startActivity(detail)
}
override fun onHomeSuccess(homeResponse: HomeResponse) {
for (a in homeResponse.data.indices) {
var items: List<String> = homeResponse.data[a].types?.split(",") ?: ArrayList()
for (x in items.indices)
{
if (items[x].equals("new_craft", true)) {
newStateList?.add(homeResponse.data[a])
} else if (items[x].equals("recommended", true)) {
recomendedList?.add(homeResponse.data[a])
} else if (items[x].equals("popular", true)) {
popularList?.add(homeResponse.data[a])
}
}
}
adapter = HomeAdapter(homeResponse.data, this)
val layoutManager: RecyclerView.LayoutManager =
LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
rvList.layoutManager = layoutManager
rvList.adapter = adapter
val sectionsPagerAdapter = SectionsPagerAdapter(
childFragmentManager
)
sectionsPagerAdapter.setData(newStateList, popularList, recomendedList)
viewPager.adapter = sectionsPagerAdapter
tabLayout.setupWithViewPager(viewPager)
}
override fun onHomeFailed(message: String) {
Toast.makeText(activity, message, Toast.LENGTH_LONG).show()
}
override fun showLoading() {
progressDialog?.show()
}
override fun dismissLoading() {
progressDialog?.dismiss()
}
}
HomeCraftAdapter: adapter for vertical recycleview
class HomeNewCraftAdapter (
private val listData: ArrayList<Data>,
private val itemAdapterCallback: ItemAdapterCallback
) : RecyclerView.Adapter<HomeNewCraftAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val view = layoutInflater.inflate(R.layout.item_home_vertical, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(listData[position], itemAdapterCallback)
}
override fun getItemCount(): Int {
return listData.size
}
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bind(data : Data, itemAdapterCallback : ItemAdapterCallback) {
itemView.apply {
tvTitle.text = data.name
tvPrice.formatPrice(data?.price.toString())
rbCraft.rating = data.rate?.toFloat() ?: 0f
Glide.with(context)
.load(data.picturePath)
.into(ivPoster)
itemView.setOnClickListener { view -> itemAdapterCallback.onClick(view, data) }
}
}
}
interface ItemAdapterCallback {
fun onClick(v: View?, data:Data)
}
}
And HomeNewCraftFragment : for vertical recycleview fragment
class HomeNewCraftFragment : Fragment() , HomeNewCraftAdapter.ItemAdapterCallback {
private var adapter: HomeNewCraftAdapter? = null
private var newcraftlist: ArrayList<Data>? = ArrayList()
private var test: String? = null
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?, savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_home_new_craft, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
newcraftlist = arguments?.getParcelableArrayList("data")
adapter = HomeNewCraftAdapter(newcraftlist!!, this)
val layoutManager: RecyclerView.LayoutManager = LinearLayoutManager(activity)
rcListNewCraft.layoutManager = layoutManager
rcListNewCraft.adapter = adapter
}
override fun onClick(v: View?, data: Data) {
val detail = Intent(activity, DetailActivity::class.java).putExtra("data", data)
startActivity(detail)
}
}
When you leave from your HomeFragment and coming back again, your HomeFragment keeps the old newStateList and add same values in it again. So, you need to empty the values before adding new values.
override fun onHomeSuccess(homeResponse: HomeResponse) {
newStateList = ArrayList() // empty it before load it again.
for (a in homeResponse.data.indices) {
You might need to empty the recomendedList and popularList as well.

Kotlin RecyclerView MVVM data not showing

I have a RecyclerView in MVVM project. I have to get text from editText (searchWordEt) and then pass it to the function that invokes API method in viewmodel.API works fine and returns data. But when I invoke searchDefAdapter.submitList(response) in SearchFragment nothing happens and RecyclerView data not showing.
class SearchDefAdapter(
private var infoListener: OnItemClickListener,
private var addListener: OnItemClickListener
):
ListAdapter<Def, SearchDefViewHolder>(differCallback) {
interface OnItemClickListener {
fun onItemClick(position: Int)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SearchDefViewHolder {
return SearchDefViewHolder(
SearchWordCardBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
),
addListener,
infoListener
)
}
override fun onBindViewHolder(holder: SearchDefViewHolder, position: Int) {
holder.bind(getItem(position))
}
}
class SearchDefViewHolder(
private val binding: SearchWordCardBinding,
addListener: SearchDefAdapter.OnItemClickListener,
infoListener: SearchDefAdapter.OnItemClickListener
): RecyclerView.ViewHolder(binding.root) {
fun bind(data: Def) {
with (binding) {
searchCardTv.text = "${data.text} - ${data.tr[0].text}"
}
}
init {
binding.addSearchCard.setOnClickListener {
addListener.onItemClick(adapterPosition)
}
binding.infoSearchCard.setOnClickListener {
infoListener.onItemClick(adapterPosition)
}
}
}
val differCallback = object : DiffUtil.ItemCallback<Def>() {
override fun areItemsTheSame(oldItem: Def, newItem: Def): Boolean {
return oldItem.text == newItem.text
}
override fun areContentsTheSame(oldItem: Def, newItem: Def): Boolean {
return oldItem == newItem
}
}
#AndroidEntryPoint
class SearchFragment : Fragment() {
private var _binding: FragmentSearchBinding? = null
private val binding get() = _binding!!
lateinit var searchDefAdapter: SearchDefAdapter
private val viewModel: DictionaryViewModel by activityViewModels()
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FragmentSearchBinding.inflate(inflater, container, false)
val view = binding.root
searchDefAdapter = SearchDefAdapter(
object : SearchDefAdapter.OnItemClickListener {
override fun onItemClick(position: Int) {
Log.d("tag", "Item Added!")
//viewModel.saveWord(position)
}
},
object : SearchDefAdapter.OnItemClickListener {
override fun onItemClick(position: Int) {
val wordFragment = WordFragment()
fragmentManager?.beginTransaction()?.replace(
R.id.nav_host_fragment_container,
wordFragment
)?.commit()
}
}
)
setUpRecyclerView(searchDefAdapter)
var job: Job? = null
binding.searchWordEt.addTextChangedListener { editable ->
job?.cancel()
job = MainScope().launch {
delay(SEARCH_WORD_TIME_DELAY)
editable?.let {
if (editable.toString().isNotEmpty())
viewModel.getTranslation(editable.toString())
}
}
}
viewModel.def.observe(viewLifecycleOwner, Observer { response ->
binding.apply {
searchDefAdapter.submitList(response)
}
})
return view
}
override fun onDestroyView() {
_binding = null
super.onDestroyView()
}
private fun setUpRecyclerView(adapter: SearchDefAdapter){
binding.searchRv.apply {
adapter
layoutManager = LinearLayoutManager(activity)
}
}
}
You haven't actually set the adapter in setUpRecyclerView()
private fun setUpRecyclerView(myAdapter: SearchDefAdapter){
binding.searchRv.apply {
layoutManager = LinearLayoutManager(activity)
adapter = myAdapter // here
}
}

How to display items loading while data is being fetched from Firebase android kotlin

I have a RecyclerView and my Fragment. I am getting data from Firebase Realtime Database into a RecyclerView.
I need to make it so that while the data is loading, I see some kind of loading effect. How can i do this?
Code from my Fragment:
private var _binding: FragmentDayDetailBinding? = null
private val binding get() = _binding!!
private var ref: DatabaseReference? = null
private lateinit var adapter: DayDetailAdapter
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentDayDetailBinding.inflate(inflater, container, false)
setupRecyclerView()
initDatabase()
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
readFromDatabase()
}
private fun setupRecyclerView() {
adapter = DayDetailAdapter()
binding.recyclerView.layoutManager = LinearLayoutManager(requireContext())
binding.recyclerView.adapter = adapter
}
private fun initDatabase() {
FirebaseApp.initializeApp(requireContext())
ref = FirebaseDatabase.getInstance()
.getReference("IMIT")
.child("groups")
}
private fun readFromDatabase() {
ref?.addValueEventListener(object: ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
if (snapshot.exists()) {
val list = ArrayList<Day>()
for (daySnapshot in snapshot.children) {
val day = daySnapshot.getValue(Day::class.java)
list.add(day!!)
}
adapter.submitList(list)
} else {
binding.apply {
lrDbEmpty.visibility = View.VISIBLE
recyclerView.visibility = View.INVISIBLE
}
}
}
override fun onCancelled(error: DatabaseError) {
}
})
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
Code from RecyclerView Adapter:
class ViewHolder(private val binding: ItemSubjectDetailBinding): RecyclerView.ViewHolder(binding.root) {
fun bind(day: Day) = with(binding) {
tvSubject.text = day.subject
tvInfo.text = "${day.teacher}, ${day.type}"
tvTime.text = day.time
tvAud.text = day.classroom
}
companion object {
fun from(parent: ViewGroup): ViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val binding = ItemSubjectDetailBinding.inflate(layoutInflater, parent, false)
return ViewHolder(binding)
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder.from(parent)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
with(holder) {
bind(getItem(position))
}
}
class ItemComparator: DiffUtil.ItemCallback<Day>() {
override fun areItemsTheSame(oldItem: Day, newItem: Day): Boolean {
return oldItem == newItem
}
override fun areContentsTheSame(oldItem: Day, newItem: Day): Boolean {
return oldItem == newItem
}
}
You already in half the way
Create a progressBar in the center of the fragment
<ProgressBar
android:id="#+id/progress_bar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
Keep it visible until the data fetched (onDataChange Called) then hide the progressBar
override fun onDataChange(snapshot: DataSnapshot) {
binding.progressBar.visibility = View.GONE
if (snapshot.exists()) {
// rest of your code
}
}
I prefer a dynamic add view.
I use the lottie player for the show wait animation. There are two approaches for adding the show wait. In this way, you can add animation or any type of view from the non-activity class. It helps you to better implement in the MVVM form.
This is both methods implementation.
1- Create and remove by Id
2- Create and remove by LiveData
class AddViewNonActivity(
private val viewGroup: ViewGroup
) {
fun addCustomWait(): Int {
val relativeLayout =
RelativeLayout(viewGroup.context)
val relativeParams =
RelativeLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)
val lottieParams =
RelativeLayout.LayoutParams(600, 600)
relativeLayout.setBackgroundColor(
viewGroup.context.getColor(R.color.wait_transparent)
)
relativeLayout.gravity = CENTER
val lottieLoading = LottieAnimationView(viewGroup.context)
lottieLoading.setAnimation("lottie/space-runner.json")
lottieLoading.repeatCount = INFINITE
lottieLoading.speed = 1f
lottieLoading.playAnimation()
relativeLayout.addView(lottieLoading, lottieParams)
val viewId = View.generateViewId()
viewGroup.addView(relativeLayout, relativeParams)
relativeLayout.id = viewId
relativeLayout.isClickable = true
return viewId
}
fun removeCustomWait(
waitViewId: Int
) {
for (view in viewGroup) {
if (view.id == waitViewId)
viewGroup.removeView(view)
}
}
fun addLiveCustomWait(
lifecycleOwner: LifecycleOwner,
liveData: LiveData<Boolean>) {
var viewId = 0
liveData.observe(lifecycleOwner) {
if (it) {
removeCustomWait(viewId)
viewId = addCustomWait()
} else {
removeCustomWait(viewId)
}
}
}
}
Activity call method
class DynamicViewActivity : AppCompatActivity() {
private lateinit var binding : ActivityDynamicViewBinding
private val liveShowWait =MutableLiveData(false)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityDynamicViewBinding.inflate(layoutInflater)
setContentView(binding.root)
var waitId = 0
initialWait()
binding.btnAddView.setOnClickListener {
waitId = AddViewNonActivity(binding.root).addCustomWait()
}
binding.btnRemoveView.setOnClickListener {
AddViewNonActivity(binding.root).removeCustomWait(waitId)
}
binding.btnLiveAddView.setOnClickListener {
liveShowWait.postValue(true)
}
binding.btnLiveRemoveView.setOnClickListener {
liveShowWait.postValue(false)
}
}
private fun initialWait() =
AddViewNonActivity(binding.root).addLiveCustomWait(this as
LifecycleOwner, liveShowWait)
}
Github link

android kotlin property does not change with passed method to adapter

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
}
}

RecyclerView not reading property to assign values

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.

Categories

Resources