I am trying to make a game of life grid based game in Android Studio using kotlin. Essentially, every second the grid will update based off a set of rules. However, I can not for the life of me get the grid to update.
For some reason, notifyItemChanged() is not calling onBindViewHolder to properly update the grid visually. I was able to find a work around for some cases, that being calling onBindViewHolder specifically and passing through the current cellViewHolder and it's position, but this won't get me the result I am looking for. Specifically, in the function I declared testUpdate(), I need to be able to change an array of data that the grid is based on, and then from that function notify the grid that changes were made and it should update. However, notifyItemChanged() as well as notifyDatasetChanged() both do not call onBindViewHolder(). I am at a loss, any help would be much appreciated!
I am very new to Kotlin, so I apologize if this code is horrible.
Here is my code:
package com.example.project2
import android.graphics.Color
import android.os.Bundle
import android.text.Layout
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.ImageButton
import android.widget.TextView
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import android.os.Handler;
class BoardFragment : Fragment() {
private lateinit var recyclerView: RecyclerView
var gridArray = IntArray(400)
val handler = Handler()
var isRunning: Boolean = false
val colorRed = Color.RED
val colorBlue = Color.BLUE
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.board_fragment, container, false)
val startButton = view.findViewById(R.id.start_button) as Button
val recyclerView = view.findViewById(R.id.recycler_view) as RecyclerView
recyclerView.layoutManager = GridLayoutManager(activity, 20)
recyclerView.adapter = GridAdapter()
startButton.setOnClickListener {
isRunning = !isRunning
if (isRunning) {
testUpdate()
}
}
return view
}
fun testUpdate() {
handler.postDelayed({
if (gridArray[3] == 0) {
gridArray[3] = 1
} else {
gridArray[3] = 0
}
if (isRunning) {
testUpdate()
}
GridAdapter().notifyItemChanged(3)
}, 1000)
}
override fun onStart() {
super.onStart()
}
private inner class CellViewHolder(cellView: View): RecyclerView.ViewHolder(cellView) {
val button: ImageButton
init {
button = cellView.findViewById(R.id.cell_button)
button.setColorFilter(null)
button.setOnClickListener {
if (gridArray[position] == 0) {
gridArray[position] = 1
} else {
gridArray[position] = 0
}
GridAdapter().notifyItemChanged(position)
GridAdapter().onBindViewHolder(this, position)
}
}
fun display(position: Int) {
}
}
private inner class GridAdapter: RecyclerView.Adapter<CellViewHolder>() {
final val NUM_CELLS: Int = 400
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CellViewHolder {
val inflater: LayoutInflater = LayoutInflater.from(parent.context)
val cellView: View = inflater.inflate(R.layout.board_cell, parent, false)
return CellViewHolder(cellView)
}
override fun onBindViewHolder(holder: CellViewHolder, position: Int) {
holder.display(position)
if (gridArray[position] == 0) {
holder.button.setColorFilter(null)
} else {
holder.button.setColorFilter(Color.RED)
}
}
override fun getItemCount(): Int {
return NUM_CELLS
}
}
}
I don't know what you're trying to achieve but a few tip
notifyItemChange() should be called from the Adapter class.
You're creating a new instances of GridAdapter GridAdapter().notifyItemChanged(position)
change
GridAdapter().notifyItemChanged(position)
to
recyclerView.adapter.notifyItemChanged(position)
modify your class like this
package com.example.project2
import android.graphics.Color
import android.os.Bundle
import android.text.Layout
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.ImageButton
import android.widget.TextView
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import android.os.Handler;
class BoardFragment : Fragment() {
private lateinit var recyclerView: RecyclerView
var gridArray = IntArray(400)
val handler = Handler()
var isRunning: Boolean = false
val colorRed = Color.RED
val colorBlue = Color.BLUE
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.board_fragment, container, false)
val startButton = view.findViewById(R.id.start_button) as Button
val recyclerView = view.findViewById(R.id.recycler_view) as RecyclerView
recyclerView.layoutManager = GridLayoutManager(activity, 20)
recyclerView.adapter = GridAdapter()
startButton.setOnClickListener {
isRunning = !isRunning
if (isRunning) {
testUpdate()
}
}
return view
}
fun testUpdate() {
handler.postDelayed({
if (gridArray[3] == 0) {
gridArray[3] = 1
} else {
gridArray[3] = 0
}
if (isRunning) {
testUpdate()
}
recyclerView.adapter.notifyItemChanged(3)
}, 1000)
}
override fun onStart() {
super.onStart()
}
private inner class CellViewHolder(cellView: View): RecyclerView.ViewHolder(cellView) {
val button: ImageButton
init {
button = cellView.findViewById(R.id.cell_button)
button.setColorFilter(null)
button.setOnClickListener {
if (gridArray[position] == 0) {
gridArray[position] = 1
} else {
gridArray[position] = 0
}
}
}
fun display(position: Int) {
}
}
private inner class GridAdapter: RecyclerView.Adapter<CellViewHolder>() {
final val NUM_CELLS: Int = 400
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CellViewHolder {
val inflater: LayoutInflater = LayoutInflater.from(parent.context)
val cellView: View = inflater.inflate(R.layout.board_cell, parent, false)
return CellViewHolder(cellView)
}
override fun onBindViewHolder(holder: CellViewHolder, position: Int) {
if (gridArray[position] == 0) {
holder.button.setColorFilter(null)
} else {
holder.button.setColorFilter(Color.RED)
}
holder.display(position)
recyclerView.adapter.notifyItemChanged(3)
}
override fun getItemCount(): Int {
return NUM_CELLS
}
}
}
Related
I'm pretty new at android development, and I'm trying to fetch data from Realtime Database into recycler view in a fragment. I've seen pretty much every tutorial how to do it but it simply wont work. Below I'll put the source code.
main fragment (where i need to fetch the data)
package hr.ferit.tomislav.lucic5.tl5_projekt
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.floatingactionbutton.FloatingActionButton
import com.google.firebase.database.*
private lateinit var transactions : ArrayList<Transaction>
private lateinit var recyclerView : RecyclerView
private lateinit var adapter: TransactionAdapter
private lateinit var loadingData : TextView
private lateinit var dbRef : DatabaseReference
class MainFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
val view = inflater.inflate(R.layout.fragment_main, container, false)
return view
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val newTransaction = view.findViewById<FloatingActionButton>(R.id.addBtn)
recyclerView = view.findViewById(R.id.recyclerview)
recyclerView.layoutManager = LinearLayoutManager(context)
recyclerView.setHasFixedSize(true)
loadingData = view.findViewById(R.id.tvLoadingData)
transactions = arrayListOf<Transaction>()
getTransactionData()
newTransaction?.setOnClickListener {
val homeFragment = AddTransactionFragment()
val transaction = fragmentManager?.beginTransaction()
transaction?.replace(R.id.mainFrame,homeFragment)
transaction?.addToBackStack(null)
transaction?.commit()
}
}
private fun updateDashboard(){
var balance = view?.findViewById<TextView>(R.id.balance)
var expense = view?.findViewById<TextView>(R.id.expense)
var budget = view?.findViewById<TextView>(R.id.budget)
val totalAmount = transactions.map { it.amount.toInt() }.sum()
val budgetAmount = transactions.filter { it.amount.toInt() >0 }.map{it.amount.toInt()}.sum()
val expenseAmount = totalAmount - budgetAmount
balance?.text = "$ %.2f".format(totalAmount)
budget?.text = "$ %.2f".format(budgetAmount)
expense?.text = "$ %.2f".format(expenseAmount)
}
private fun getTransactionData(){
recyclerView.visibility= View.GONE
loadingData.visibility = View.VISIBLE
dbRef = FirebaseDatabase.getInstance().getReference("transactions")
dbRef.addValueEventListener(object: ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
transactions.clear()
if (snapshot.exists()) {
for (tranSnap in snapshot.children) {
val tranData =
tranSnap.getValue(Transaction::class.java)
transactions.add(tranData!!)
}
val mAdapter = TransactionAdapter(transactions)
recyclerView.adapter = mAdapter
recyclerView.visibility = View.VISIBLE
loadingData.visibility = View.GONE
}
}
override fun onCancelled(error: DatabaseError) {
TODO("Not yet implemented")
}
})
}
}
adapter
package hr.ferit.tomislav.lucic5.tl5_projekt
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
class TransactionAdapter(private val trList:ArrayList<Transaction>) :
RecyclerView.Adapter<TransactionAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val itemView = LayoutInflater.from(parent.context).inflate(R.layout.transaction_layout,parent,false)
return ViewHolder(itemView)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val curTransaction = trList[position]
holder.amount.text = curTransaction.amount
holder.label.text = curTransaction.label
}
override fun getItemCount(): Int {
return trList.size
}
fun addItem(transaction: Transaction){
trList.add(0, transaction)
notifyItemInserted(0)
}
class ViewHolder(itemView: View): RecyclerView.ViewHolder(itemView){
val label : TextView = itemView.findViewById(R.id.label)
val amount : TextView = itemView.findViewById(R.id.amount)
}
}
data class for Transaction
package hr.ferit.tomislav.lucic5.tl5_projekt
data class Transaction (
var label: String,
var amount: String,
var description: String
)
I don't know what can I do else.. it's always the same error Class hr.ferit.tomislav.lucic5.tl5_projekt.Transaction does not define a no-argument constructor. If you are using ProGuard, make sure these constructors are not stripped. That happens after i try to add a new transaction:
package hr.ferit.tomislav.lucic5.tl5_projekt
import android.annotation.SuppressLint
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.ImageButton
import androidx.core.widget.addTextChangedListener
import com.google.android.material.textfield.TextInputEditText
import com.google.android.material.textfield.TextInputLayout
import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.database.FirebaseDatabase
import com.google.firebase.database.ktx.database
import com.google.firebase.ktx.Firebase
class AddTransactionFragment : Fragment() {
private lateinit var database : FirebaseDatabase
#SuppressLint("MissingInflatedId")
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_add_transaction2, container, false)
val addBtn = view.findViewById<Button>(R.id.addTransactionBtn)
val titleInput = view.findViewById<TextInputEditText>(R.id.titleInput)
val amountInput = view.findViewById<TextInputEditText>(R.id.amountInput)
val descriptionInput = view.findViewById<TextInputEditText>(R.id.descriptionInput)
val titleLayout = view.findViewById<TextInputLayout>(R.id.titleLayout)
val amountLayout = view.findViewById<TextInputLayout>(R.id.amountLayout)
val closeBtn = view.findViewById<ImageButton>(R.id.closeBtn)
var recyclerAdapter : TransactionAdapter
var database = Firebase.database.reference
val userId = FirebaseAuth.getInstance().currentUser!!.uid
titleInput.addTextChangedListener {
if(it!!.count() > 0)
titleLayout.error = null
}
amountInput.addTextChangedListener {
if(it!!.count() > 0)
amountLayout.error = null
}
addBtn.setOnClickListener{
val title = titleInput.text.toString()
val description = descriptionInput.text.toString()
val amount = amountInput.text.toString()
var transactionToAdd = Transaction(
label = titleInput.text.toString(),
description = descriptionInput.text.toString(),
amount = amountInput.text.toString()
)
val list = ArrayList<Transaction>()
recyclerAdapter = TransactionAdapter(list)
if(title.isEmpty()) {
titleLayout.error = "Please enter a valid title"
}
if(amount.isEmpty()) {
amountLayout.error = "Please enter a valid amount"
}
else{
database.child("transactions").push().setValue(transactionToAdd)
.addOnSuccessListener {
recyclerAdapter.addItem(transactionToAdd)
var homeFragment = MainFragment()
val transaction = fragmentManager?.beginTransaction()
transaction?.replace(R.id.mainFrame,homeFragment)
transaction?.commit()
}
}
}
closeBtn.setOnClickListener{
var homeFragment = MainFragment()
val transaction = fragmentManager?.beginTransaction()
transaction?.replace(R.id.mainFrame,homeFragment)
transaction?.commit()
}
return view
}
}
I tried everything, basically every tutorial I found on youtube but nothing helped. I don't know what's the problem, I get that it is something to do with the data class and the constructor but im unable to solve it.
There was a problem with the recyclerview list being added only initially and not being added after the second.
MainActivity.kt
package com.malkinfo.editingrecyclerview
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Toast
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.floatingactionbutton.FloatingActionButton
import com.malkinfo.editingrecyclerview.added_data.Companion.start_time_hou
import com.malkinfo.editingrecyclerview.added_data.Companion.start_time_min
import com.malkinfo.editingrecyclerview.model.UserData
import com.malkinfo.editingrecyclerview.view.UserAdapter
class MainActivity : AppCompatActivity() {
private lateinit var addsBtn:FloatingActionButton
private lateinit var recv:RecyclerView
private lateinit var userList:ArrayList<UserData>
private lateinit var userAdapter:UserAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
userList = ArrayList()
addsBtn = findViewById(R.id.addingBtn)
recv = findViewById(R.id.mRecycler)
userAdapter = UserAdapter(this,userList)
recv.layoutManager = LinearLayoutManager(this)
recv.adapter = userAdapter
addsBtn.setOnClickListener {
startActivity(Intent(this#MainActivity,next_activity::class.java))
}
if(intent.hasExtra("name")){
addInfo2()
}
}
private fun addInfo2(): ArrayList<UserData> {
var names = start_time_hou
var number = start_time_min
userList.add(UserData("Name: ${names}","Mobile No. : $number"))
userAdapter.notifyDataSetChanged()
Toast.makeText(this,"${userList}",Toast.LENGTH_LONG).show()
return userList
}
}
UserAdapter.kt
package com.malkinfo.editingrecyclerview.view
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.*
import androidx.recyclerview.widget.RecyclerView
import com.malkinfo.editingrecyclerview.R
import com.malkinfo.editingrecyclerview.model.UserData
class UserAdapter(val c:Context,val userList:ArrayList<UserData>):RecyclerView.Adapter<UserAdapter.UserViewHolder>()
{
inner class UserViewHolder(val v:View):RecyclerView.ViewHolder(v){
var name:TextView
var mbNum:TextView
var mMenus:ImageView
var mToggle:ToggleButton
init {
name = v.findViewById<TextView>(R.id.mTitle)
mbNum = v.findViewById<TextView>(R.id.mSubTitle)
mToggle=v.findViewById(R.id.toggleButton)
mMenus = v.findViewById(R.id.mMenus)
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserViewHolder {
val inflater = LayoutInflater.from(parent.context)
val v = inflater.inflate(R.layout.list_item,parent,false)
return UserViewHolder(v)
}
override fun onBindViewHolder(holder: UserViewHolder, position: Int) {
val newList = userList[position]
holder.name.text = newList.userName
holder.mbNum.text = newList.userMb
}
override fun getItemCount(): Int {
return userList.size
}
}
next_activity.kt
package com.malkinfo.editingrecyclerview
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import com.malkinfo.editingrecyclerview.added_data.Companion.end_time_hou
import com.malkinfo.editingrecyclerview.added_data.Companion.end_time_min
import com.malkinfo.editingrecyclerview.added_data.Companion.start_time_hou
import com.malkinfo.editingrecyclerview.added_data.Companion.start_time_min
import kotlinx.android.synthetic.main.activity_next2.*
import kotlinx.android.synthetic.main.add_item.*
class next_activity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_next2)
button2.setOnClickListener {
finish()
}
button.setOnClickListener {
start_time_hou="1"
start_time_min="2"
end_time_hou=3
end_time_min=4
val intent_time= Intent(this#next_activity,MainActivity::class.java)
intent_time.putExtra("name","lir")
startActivity(intent_time)
}
button3.setOnClickListener {
start_time_hou="1123"
start_time_min="52"
val intent_time= Intent(this#next_activity,MainActivity::class.java)
intent_time.putExtra("name","lir")
startActivity(intent_time)
}
}
}
added_data.kt
package com.malkinfo.editingrecyclerview
class added_data {
companion object {
var start_time_hou = "0"
var start_time_min = "0"
var end_time_hou = 0
var end_time_min = 0
}
}
UserData.kt
package com.malkinfo.editingrecyclerview.model
data class UserData (
var userName:String,
var userMb:String
)
What I want is to move from MainACtivity to next_activity, and every time I press the button in next_activity, a new recycleview element is created. However, as I said earlier, there is no additional generation and only one element of data changes.
The issue here is, you are adding items to your userList in MainActivity but you are not passing them to the recyclerview. That is why userAdapter.notifyDataSetChanged() is not causing any effect.
Try this and let me know if there is still some issue :)
In MainActivity
class MainActivity : AppCompatActivity() {
private lateinit var addsBtn:FloatingActionButton
private lateinit var recv:RecyclerView
private lateinit var userList:ArrayList<UserData>
private lateinit var userAdapter:UserAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
userList = ArrayList()
addsBtn = findViewById(R.id.addingBtn)
recv = findViewById(R.id.mRecycler)
userAdapter = UserAdapter(this)
userAdapter.setData(userList)
recv.layoutManager = LinearLayoutManager(this)
recv.adapter = userAdapter
addsBtn.setOnClickListener {
startActivity(Intent(this#MainActivity,next_activity::class.java))
}
if(intent.hasExtra("name")){
addInfo2()
}
}
private fun addInfo2(): ArrayList<UserData> {
var names = start_time_hou
var number = start_time_min
userList.add(UserData("Name: ${names}","Mobile No. : $number"))
userAdapter.setData(userList)
Toast.makeText(this,"${userList}",Toast.LENGTH_LONG).show()
return userList
}
}
UserAdapter
class UserAdapter(val c:Context):RecyclerView.Adapter<UserAdapter.UserViewHolder>()
{
private var _userList: ArrayList<UserData>? = null
inner class UserViewHolder(val v:View):RecyclerView.ViewHolder(v){
var name:TextView
var mbNum:TextView
var mMenus:ImageView
var mToggle:ToggleButton
init {
name = v.findViewById<TextView>(R.id.mTitle)
mbNum = v.findViewById<TextView>(R.id.mSubTitle)
mToggle=v.findViewById(R.id.toggleButton)
mMenus = v.findViewById(R.id.mMenus)
}
}
fun setData(userList: ArrayList<UserData>) {
_userList = userList
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int):
UserViewHolder {
val inflater = LayoutInflater.from(parent.context)
val v = inflater.inflate(R.layout.list_item,parent,false)
return UserViewHolder(v)
}
override fun onBindViewHolder(holder: UserViewHolder, position: Int) {
val newList = _userList?.get(position)
holder.name.text = newList?.userName
holder.mbNum.text = newList?.userMb
}
override fun getItemCount(): Int {
if(_userList == null)
return 0
else
return _userList!!.size
}
}
Peace
i have made simple recyclerview, with interface clickListener
consisting of 4 items showing toast on click.
It works fine if i single click per second,
but if i Rapidly click on different items ( 2 or 3 times in 1 seconds )
it still shows the toast each second,
not rapidly changing the toast
data class code:
package com.elsersy.rv5
data class Items (var textt:String) {
}
adapter code
package com.elsersy.rv5
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
class MyAdapter(private var stringList:ArrayList<Items>,private val listener:MyOnClickListener):
RecyclerView.Adapter<MyAdapter.MyViewHolder>() {
inner class MyViewHolder(itemView:View):RecyclerView.ViewHolder(itemView) {
var text:TextView = itemView.findViewById(R.id.textView)
init {
itemView.setOnClickListener {
val position = absoluteAdapterPosition
if (position != RecyclerView.NO_POSITION)
{
listener.onItemClick(position)
}
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val itemView = LayoutInflater.from(parent.context).inflate(R.layout.row_item,parent,false)
return MyViewHolder(itemView)
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val currentItem = stringList[position]
holder.text.text = currentItem.textt
}
override fun getItemCount(): Int {
return stringList.size
}
interface MyOnClickListener{
fun onItemClick(position: Int)
}
fun setData(newItemList:ArrayList<Items>){
val diffUtil = MyDiffUtil(stringList,newItemList)
val diffResults = DiffUtil.calculateDiff(diffUtil)
stringList = newItemList
diffResults.dispatchUpdatesTo(this)
}
}
ActivityMainCode
package com.elsersy.rv5
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Toast
import androidx.recyclerview.widget.LinearLayoutManager
import com.elsersy.rv5.databinding.ActivityMainBinding
class MainActivity : AppCompatActivity(),MyAdapter.MyOnClickListener {
private lateinit var binding: ActivityMainBinding
private lateinit var stringList:ArrayList<Items>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
binding.recyclerView.layoutManager = LinearLayoutManager(this)
stringList = arrayListOf<Items>()
getStrings()
binding.recyclerView.adapter=MyAdapter(stringList,this)
}
private fun getStrings() {
for (a in 0..4){
var string_Array = resources.getStringArray(R.array.string_array)
stringList.add(Items(string_Array[a]))
}
}
override fun onItemClick(position: Int) {
Toast.makeText(this,"$position is clicked", Toast.LENGTH_SHORT).show()
}
}
If you want to cancel the previous toast before showing a new one, assign it as a member variable and cancel it before showing the new one:
private var toast : Toast? = null
..
override fun onItemClick(position: Int) {
toast?.cancel()
toast = Toast.makeText(this,"$position is clicked", Toast.LENGTH_SHORT)
toast?.show()
}
I am trying to retrieve data from the Firebase Firestore using the MVVM pattern . The build gets successfull everytime but the app fails to open up. I am not able to find faults in the code.The application is able to add data but retrieving data is the issue. Please help.Thank-You
ViewModel
import android.util.Log
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.google.firebase.firestore.FirebaseFirestore
import com.karuneshpalekar.firestorepagination.models.Note
import java.lang.Exception
class DataViewModel : ViewModel() {
companion object{
private const val TAG =" VIEWMODEL"
}
private val db = FirebaseFirestore.getInstance()
private val noteref = db.collection("record")
private val _results = MutableLiveData<Exception>()
val results: LiveData<Exception>
get() = _results
private val _notes = MutableLiveData<List<Note>>()
val notes: LiveData<List<Note>>
get() = _notes
fun addData(note: Note) {
noteref.add(note).addOnCompleteListener {
if (it.isSuccessful) {
Log.w("TAG", "success")
_results.value = null
} else {
_results.value = it.exception
}
}
}
fun fetchData() {
noteref.addSnapshotListener{ snapshot,e->
if (e!=null){
Log.w(TAG,"Listen failed",e)
return#addSnapshotListener
}
if (snapshot != null) {
val items = mutableListOf<Note>()
for (docs in snapshot){
val notes = docs.toObject(Note::class.java)
notes.let {
items.add(it)
}
_notes.value = items
}
}
}
}
}
Adapter
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.karuneshpalekar.firestorepagination.R
import com.karuneshpalekar.firestorepagination.models.Note
import kotlinx.android.synthetic.main.list_item.view.*
class DataAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private var items= mutableListOf<Note>()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return DataViewHolder(
LayoutInflater.from(parent.context).inflate(R.layout.list_item, parent, false)
)
}
override fun getItemCount(): Int {
return items.size
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (holder) {
is DataViewHolder -> {
holder.bind(items[position])
}
}
}
fun setNote(items:List<Note>){
this.items = items as MutableList<Note>
notifyDataSetChanged()
}
class DataViewHolder constructor(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val textName = itemView.text_view_name
fun bind(note: Note) {
textName.text = note.name
}
}
}
Fragment
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProviders
import com.karuneshpalekar.firestorepagination.R
import kotlinx.android.synthetic.main.fragment_recyclerview.*
class RecyclerView : Fragment() {
private lateinit var viewModel: DataViewModel
private lateinit var dataadapter : DataAdapter
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
viewModel = ViewModelProviders.of(this).get(DataViewModel::class.java)
return inflater.inflate(R.layout.fragment_recyclerview, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
recycler_view.apply {
dataadapter = DataAdapter()
adapter = dataadapter
}
viewModel.fetchData()
viewModel.notes.observe(viewLifecycleOwner, Observer {
dataadapter.setNote(it)
})
floating_btn_add.setOnClickListener {
DialogFragment().show(childFragmentManager, "")
}
}
}
DialogFragment - To add Data
class DialogFragment :DialogFragment(){
private lateinit var viewmodel:DataViewModel
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
viewmodel = ViewModelProviders.of(this).get(DataViewModel::class.java)
return inflater.inflate(R.layout.fragment_dialog, container, false)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setStyle(STYLE_NO_TITLE, android.R.style.Theme_DeviceDefault_Light_Dialog_MinWidth)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
viewmodel.results.observe(viewLifecycleOwner, Observer {
val message = if (it == null) {
getString(R.string.name_added)
} else {
getString(R.string.name_error)
}
Toast.makeText(requireContext(), message, Toast.LENGTH_SHORT).show()
dismiss()
})
button_add.setOnClickListener {
val names = edit_text_name.text.toString().trim()
val note= Note("",names)
viewmodel.addData(note)
}
}
}
RecyclerView XML
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".ui.RecyclerView">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="#dimen/margin"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:listitem="#layout/list_item"
/>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/floating_btn_add"
app:fabSize="normal"
android:layout_gravity="bottom|end"
android:layout_margin="#dimen/margin"
/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
The above is the code of my Project
The only thing missing in the code that provided errors was not having null-check in the Model class ,i.e., Note Class.
I have a RecyclerView and I have to pass data to another activity when u click on an item. When someone clicks on the first item I need to pass the data of that item to an activity.
This is the part of the fragment where the recycler view is located:
class TrafficFragment : Fragment() {
private lateinit var trafficViewModel: TrafficViewModel
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
trafficViewModel =
ViewModelProviders.of(this).get(TrafficViewModel::class.java)
val root = inflater.inflate(R.layout.fragment_traffic, container, false)
//...
val recyclerViewTraffic: RecyclerView = root.findViewById(R.id.recyclerViewTraffic)
recyclerViewTraffic.apply {
layoutManager = LinearLayoutManager (this#TrafficFragment.context)
Log.d("DEBUG", MainActivity.clickArray.toString())
adapter = TrafficAdapter(MainActivity.clickArray){
var intent = Intent(activity, ClickDetail::class.java)
startActivity(intent)
}
}
return root
}
}
The adapter:
package com.example.example
import android.content.Intent
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.traffic_recycler_layout.view.*
class TrafficAdapter(private val trafficClick: List<TrafficClick>, val clickDetail: () -> Unit) :
RecyclerView.Adapter<TrafficAdapter.ViewHolder>(){
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val layoutView = LayoutInflater.from(parent.context).inflate(R.layout.traffic_recycler_layout, parent, false)
return ViewHolder(layoutView)
}
override fun getItemCount(): Int = MainActivity.clicksNumber
companion object{
var idClick: String?= String()
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val clicks = trafficClick[position]
holder.view.indirizzoIp.text = "${clicks.ip}"
holder.view.isp.text= "${clicks.organization}"
holder.view.data.text = "${clicks.data_creation}"
holder.view.numberText.text = "${clicks.id_campaign}"
holder.view.setOnClickListener {
clickDetail.invoke()
}
}
class ViewHolder(val view: View) : RecyclerView.ViewHolder(view)
}
and this is the activity that I need to open when I click on the item with the details of that item:
package com.example.example
import android.os.Bundle
import android.util.Log
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import kotlinx.android.synthetic.main.traffic_click_detail.*
class ClickDetail : AppCompatActivity(){
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.traffic_click_detail)
}
}
var intent = Intent(activity, ClickDetail::class.java)
intent.putExtra("your_key",MainActivity.clickArray.toString())
startActivity(intent)
class ClickDetail : AppCompatActivity(){
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.traffic_click_detail)
val YourData = getIntent().getStringExtra("your_key")
}
}
If I suppose you want to pass and id, replace this
holder.view.setOnClickListener {
clickDetail.invoke()
}
by
holder.view.setOnClickListener {
ClickDetail.launch(holder.itemView.context, /*your ID*/)
}
Then to retrieve your data
class ClickDetail : AppCompatActivity(){
companion object {
private const val EXTRA_KEY_ID = "CLICKDETAIL.EXTRA_KEY_ID"
fun launch(launcher: Activity, id: Int) {
val intent = Intent(launcher, ClickDetail::class.java)
.apply {
putExtra(EXTRA_KEY_ID, id)
}
launcher.startActivity(intent)
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.traffic_click_detail)
val id = intent.getIntExtra("EXTRA_KEY_ID")
}
}