Update method of ViewModel class is not working - android-studio

I am making this basic Notes App named Scribble
I wrote ViewModel class and all of its methods that is insert and delete are working fine but for some reason update method is not working, Its just not updating the passed note object
Please find code below :
First of all this is my Note i.e. Entity Class :
#Entity(tableName = "NotesTable")
data class Note constructor(
#ColumnInfo(name = "title") var noteTitle: String,
#ColumnInfo(name = "description") var noteDescription: String,
#ColumnInfo(name = "timeStamp") var timeStamp: String
):java.io.Serializable {
#PrimaryKey(autoGenerate = true)
#ColumnInfo(name = "id") var id : Int = 0
}
NoteViewModel Class :
class NoteViewModel (application: Application) : AndroidViewModel (application){
val allNotes: LiveData<List<Note>>
val repository: NoteRepository
init {
val dao = NoteDatabase.getDatabase(application).getNotesDao()
repository = NoteRepository(dao)
allNotes = repository.allNotes
}
fun insertNote(note: Note) {
viewModelScope.launch(Dispatchers.IO){
repository.insertNote(note)
}
}
fun deleteNote(note: Note) = viewModelScope.launch(Dispatchers.IO){repository.deleteNote(note)}
fun updateNote(note: Note) = viewModelScope.launch(Dispatchers.IO){repository.updateNote(note)}
Repository class :
class NoteRepository (private val noteDao: NoteDao) {
val allNotes : LiveData<List<Note>> = noteDao.getAllNotes()
suspend fun insertNote(note : Note){
noteDao.insert(note)
}
suspend fun deleteNote(note : Note){
noteDao.delete(note)
}
suspend fun updateNote(note : Note){
noteDao.update(note)
}
DAO class :
#Dao
public interface NoteDao {
#Insert(onConflict = OnConflictStrategy.IGNORE)
fun insert(note: Note)
#Update
fun update(note: Note)
#Delete
fun delete(note: Note)
#Query("Select * from NotesTable order by id ASC")
fun getAllNotes() : LiveData<List<Note>>
Now this is my EditNote Activity on which I come after clicking on a note card in my recycler view I am saving note when user presses back button after editing the note thus called updateNote method inside onDestroy :
class EditNoteActivity : AppCompatActivity() {
lateinit var titleOfNote : EditText
lateinit var bodyOfNote : EditText
lateinit var noteToUpdate: Note
lateinit var noteViewModel: NoteViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_edit_note)
noteViewModel = ViewModelProvider(this, ViewModelProvider.AndroidViewModelFactory.getInstance(application))[NoteViewModel::class.java]
titleOfNote = findViewById(R.id.titleEt)
bodyOfNote = findViewById(R.id.bodyEt)
noteToUpdate = intent.getSerializableExtra("noteObject") as Note
titleOfNote.setText(noteToUpdate.noteTitle)
bodyOfNote.setText(noteToUpdate.noteDescription)
}
fun updateNote(){
Log.d("AppLogs", "updateNote: called")
noteToUpdate.noteTitle = titleOfNote.text.toString()
noteToUpdate.noteDescription = bodyOfNote.text.toString()
val current = LocalDateTime.now()
val formatter = DateTimeFormatter.ofPattern("dd-MM-yyyy \nHH:mm")
noteToUpdate.timeStamp = current.format(formatter)
noteViewModel.updateNote(noteToUpdate)
}
override fun onDestroy() {
super.onDestroy()
Log.d("AppLogs", "onDestroy(updateNote): called")
updateNote()
}
I've used literally the same logic in AddNoteActivity and it works, it saves my note when i press back button and recycler view shows it perfectly
AddNoteActivity class :
class AddNoteActivity : AppCompatActivity() {
lateinit var noteTitle : EditText
lateinit var noteBody : EditText
lateinit var noteViewModel: NoteViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_add_note)
noteTitle = findViewById(R.id.noteTitleEt)
noteBody = findViewById(R.id.noteBodyEt)
noteViewModel = ViewModelProvider(this,ViewModelProvider.AndroidViewModelFactory.getInstance(application))[NoteViewModel::class.java]
}
fun saveNote(){
Log.d("AppLogs", "saveNote: called")
var titleOfNote : String = noteTitle.text.toString()
var bodyOfNote : String = noteBody.text.toString()
val current = LocalDateTime.now()
val formatter = DateTimeFormatter.ofPattern("dd-MM-yyyy \nHH:mm")
val timeStamp = current.format(formatter)
var note = Note(titleOfNote,bodyOfNote,timeStamp)
noteViewModel.insertNote(note)
}
override fun onStop() {
super.onStop()
Log.d("AppLogs", "onStop: called !")
saveNote()
}
}
means insertNote method from NoteViewModel works fine, similarly deleteNote also works fine, it deletes the note when I press delete button its just something wrong with updateNote method that I am not able to figure out
OTHER CLASSES ::::
NoteViewAdapter Class :
class NoteViewAdapter( allNotes : List<Note>, context: Context, clickListeners: ClickListeners)
: RecyclerView.Adapter<NoteViewAdapter.NoteViewHolder>() {
private val clickListeners : ClickListeners
private val context : Context
private var allNotes : List<Note>
init {
this.clickListeners = clickListeners
this.context = context
this.allNotes = allNotes
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int)
: NoteViewAdapter.NoteViewHolder {
val itemView = LayoutInflater.from(parent.context).inflate(R.layout.note_row,parent,false)
return NoteViewHolder(itemView)
}
override fun onBindViewHolder(holder: NoteViewHolder, position: Int) {
holder.noteTitleTV.text = allNotes[position].noteTitle
holder.noteBodyTV.text = allNotes[position].noteDescription
holder.timeStampTV.text = allNotes[position].timeStamp
holder.deleteButton.visibility = View.INVISIBLE
holder.deleteButton.setOnClickListener {
clickListeners.onDeleteButtonClick(allNotes[position])
}
holder.itemView.setOnClickListener{
clickListeners.onNoteClick(allNotes[position])
}
holder.itemView.setOnLongClickListener{
holder.deleteButton.visibility = View.VISIBLE
val timer = Timer("schedule", true);
timer.schedule(2000) {
holder.deleteButton.visibility = View.INVISIBLE
}
return#setOnLongClickListener true
}
}
override fun getItemCount(): Int {
return allNotes.size
}
inner class NoteViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView){
val noteTitleTV: TextView = itemView.findViewById(R.id.titleTV)
val noteBodyTV: TextView = itemView.findViewById(R.id.bodyTV)
val deleteButton: FloatingActionButton = itemView.findViewById(R.id.deleteButton)
val timeStampTV : TextView = itemView.findViewById(R.id.timeStamp)
}
}
interface ClickListeners {
fun onNoteClick(note: Note)
fun onDeleteButtonClick(note: Note)
}
MainActivity class :
class MainActivity : AppCompatActivity(), ClickListeners {
lateinit var recyclerView : RecyclerView
lateinit var addButton : FloatingActionButton
lateinit var viewModel : NoteViewModel
lateinit var adapter : NoteViewAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
recyclerView = findViewById(R.id.NotesRV)
addButton = findViewById(R.id.addButton)
viewModel = ViewModelProvider(this,ViewModelProvider.AndroidViewModelFactory.getInstance(application))[NoteViewModel::class.java]
viewModel.allNotes.observe(this, Observer {list ->
adapter = NoteViewAdapter(list ,this, this)
recyclerView.adapter = adapter
})
recyclerView.layoutManager = GridLayoutManager(this,2,GridLayoutManager.VERTICAL,false)
addButton.setOnClickListener{
val intent : Intent = Intent(this,AddNoteActivity::class.java)
startActivity(intent)
}
}
override fun onNoteClick(note: Note) {
val intent : Intent = Intent(this,EditNoteActivity::class.java)
intent.putExtra("noteObject",note)
startActivity(intent)
}
override fun onDeleteButtonClick(note: Note) {
viewModel.deleteNote(note)
}
Here I am passing note object with intent Extras because I implemented java.io.Serializable in Note class
When I click on note it opens edit note activity then I change the previous text set there, but when i press back button it doesnt update note
I used debug logs and found that both onDestroy and updateNote are getting called but still it is note updating my note
Also these lines from EditNoteActivity.kt are setting previous entered text in note after clicking on note card
titleOfNote.setText(noteToUpdate.noteTitle)
bodyOfNote.setText(noteToUpdate.noteDescription)
but for some reason this line isn't working
noteViewModel.updateNote(noteToUpdate)

Related

How to load firebase realtime data into a recycler view in kotlin?

When I try to view the list of data coming from realtime firebase no information is displayed
My adapter:
class MarcacaoAdapter: RecyclerView.Adapter<MarcacaViewHolder>() {
private var items = listOf<Marcacao>()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MarcacaViewHolder {
val view = LayoutInflater
.from(parent.context)
.inflate(R.layout.item_lista, parent, false)
return MarcacaViewHolder(view)
}
override fun onBindViewHolder(holder: MarcacaViewHolder, position: Int) {
holder.bind(items[position])
}
override fun getItemCount(): Int {
return items.size
}
}
My view holder:
class MarcacaViewHolder(view: View): RecyclerView.ViewHolder(view) {
private val textviewData = itemView.findViewById<TextView>(R.id.tvDataM)
private val textviewHora = itemView.findViewById<TextView>(R.id.tvHoraM)
fun bind (item: Marcacao){
textviewData.text = item.dia
textviewHora.text = item.hora
}
}
My activity list:
class ListaActivity : AppCompatActivity() {
private val adapterMarcacao = MarcacaoAdapter()
private var tvDataM: TextView? = null
private var tvHoraM: TextView? = null
private lateinit var mDatabaseReference: DatabaseReference
private lateinit var mRecycler : RecyclerView
private lateinit var mArrayList : ArrayList<Marcacao>
override fun onCreate(savedInstanceState : Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_lista)
mRecycler = findViewById(R.id.recyclerView)
mArrayList = arrayListOf<Marcacao>()
mDatabaseReference = FirebaseDatabase.getInstance().getReference("data")
}
}
I would like some help to identify why my recycler view is not loading information from firebase.

How to display data in the form of images from the intent process sent using arraylist and parcelable?

class DetailActivity : AppCompatActivity() {
companion object {
const val EXTRA_USER = "extra_user"
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_detail)
val imgAvatar:CircleImageView = findViewById(R.id.img_dtlavtr)
val txtName:TextView = findViewById(R.id.txt_names)
val txtUname:TextView = findViewById(R.id.txt_uname)
val users = intent.getParcelableArrayListExtra<User>(EXTRA_USER) as ArrayList<User>
imgAvatar.setImageResource(users)
txtName.text
txtUname.text
}
}
It's my first time learning Android Studio and Kotlin,
Above is the second activity DetailActivity.kt, in the section I will display an image (imgAvatar.setImageresource(users)), an error appears
I use arraylist and parcelable to send data sets in the form of images and text
"Type missmatch
Required: Int
Found: kotlin.collection. ArrayList "
and here's the code for MainActivity.kt
class MainActivity : AppCompatActivity() {
private lateinit var adapter: UserAdapter
private lateinit var dataName: Array<String>
private lateinit var dataUname: Array<String>
private lateinit var dataAvatar: TypedArray
private var users = arrayListOf<User>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val listView: ListView = findViewById(R.id.lv_list)
adapter = UserAdapter(this)
listView.adapter = adapter
prepare()
addItem()
listView.onItemClickListener = AdapterView.OnItemClickListener {
_, _, position, _ ->
//Toast.makeText(this#MainActivity, users[position].name, Toast.LENGTH_SHORT).show()
val detailIntent = Intent(this#MainActivity, DetailActivity::class.java)
detailIntent.putParcelableArrayListExtra(EXTRA_USER, users)
startActivity(detailIntent)
}
}
private fun addItem() {
for (position in dataName.indices) {
val user = User(
dataAvatar.getResourceId(position, -1),
dataName[position],
dataUname[position]
)
users.add(user)
}
adapter.users = users
}
private fun prepare() {
dataName = resources.getStringArray(R.array.name)
dataUname = resources.getStringArray(R.array.username)
dataAvatar = resources.obtainTypedArray(R.array.avatar)
}
}
Firstly, please check the android documentations about imageview setImageResource
It only accepts a single resource integer. i.e. you can give image sources such as
setImageResource(R.drawable.yourImage)
R.drawable.yourImage actually is an int. That is why exception says you that it expects int however found list.

kotlin adapter give me error in mainactivity

I have a class called medicine, I got an error when writing adapter = MedicineAdapter(this, this.medicineList!!) in main activity I saw the error in run window.
My app is crashing when I want to run and I saw one error which is about adapter's line
Also, I have medicine_items it is my custom design I assigned it to the adapter in my adapter also I checked my ids but ı don t know why am I got an error
MainActivity
class MainActivity : AppCompatActivity() {
private var adapter: MedicineAdapter? = null
private var medicineList : ArrayList<Medicine>? = null
private var recyclerView: RecyclerView? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
recyclerView =
findViewById<View>(R.id.recycler) as RecyclerView
adapter = MedicineAdapter(this, this.medicineList!!)
val layoutManager = LinearLayoutManager(applicationContext)
recyclerView!!.layoutManager = layoutManager
recyclerView!!.itemAnimator = DefaultItemAnimator()
// Add a neat dividing line between items in the list
recyclerView!!.addItemDecoration(
DividerItemDecoration(this,
LinearLayoutManager.VERTICAL))
// set the adapter
recyclerView!!.adapter = adapter
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.menu, menu)
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean = when (item.itemId) {
R.id.addBtn -> {
val intent = Intent(this,AddNewMedicine::class.java)
startActivity(intent)
true
}
else -> super.onOptionsItemSelected(item)
}
fun addMedicine(m: Medicine){
medicineList!!.add(m)
adapter!!.notifyDataSetChanged()
}
}
MyAdapter
class MedicineAdapter(
private val mainActivity: MainActivity,
private val medicineList: ArrayList<Medicine>)
: RecyclerView.Adapter<MedicineAdapter.ListItemHolder>(){
override fun onCreateViewHolder(
parent: ViewGroup, viewType: Int): ListItemHolder {
val itemView = LayoutInflater.from(parent.context).inflate(R.layout.medicine_items, parent, false)
return ListItemHolder(itemView)
}
inner class ListItemHolder(view: View) :
RecyclerView.ViewHolder(view),
View.OnClickListener {
internal var name = view.findViewById<TextView>(R.id.name)
internal var amount = view.findViewById<TextView>(R.id.amount)
internal var description = view.findViewById<TextView>(R.id.description)
init {
view.isClickable = true
view.setOnClickListener(this)
}
override fun onClick(view: View) {
//val intentToCarPager = Intent(view!!.context, CarPagerActivity::class.java)
//view.context.startActivity(intentToCarPager)
}
}
override fun onBindViewHolder(holder: ListItemHolder, position: Int) {
val medicine = medicineList!![position]
holder.name.text = medicine.name.toString()
holder.amount.text = medicine.amount.toString()
holder.description.text = medicine.desription.toString()
}
override fun getItemCount(): Int {
if (medicineList != null) {
return medicineList.size
}
return -1
}
}
my run window
It's because here:
private var medicineList : ArrayList<Medicine>? = null
you have initialized medicineList as null and you haven't given it a proper value along the way and here:
adapter = MedicineAdapter(this, this.medicineList!!)
you have asserted that it is not null and tried to assign it to the adapter.
If you have no elements to add to your array at first, initialize it this way:
private var medicineList : ArrayList<Medicine> = arrayListOf()
and then you can add elements to it:
medicineList.add(myMedicine)

lateinit property mUserViewModel has not been initialized

I´m trying to read my database, therefore i have created an editext where I will enter a username and a button that allows me to search for the user, but I am presenting an the error in class ListFragment : Fragment(), which says lateinit property mUserViewModel has not been initialized,please help me :(.
//Entity
#Entity(tableName = "user_table")
data class User(
#PrimaryKey(autoGenerate = true)
val id: Int,
val firstName: String,
val lastName: String,
val age: Int
)
//Dao
#Dao
interface UserDao {
#Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun addUser(user: User)
#Query("SELECT * FROM user_table where firstName LIKE :search LIMIT 1")
suspend fun readAllData(search : String) : User
}
//UserRepository
class UserRepository(private val userDao: UserDao) {
suspend fun readAllData(user: String):User{
return userDao.readAllData(user)
}
}
//UserViewModel
class UserViewModel(application: Application): AndroidViewModel(application) {
private lateinit var repository:UserRepository
fun search( search : String):User{
viewModelScope.launch {
repository.readAllData(search)
}
return search(String())
}
}
}
//class
class ListFragment : Fragment() {
//erro(lateinit property mUserViewModel has not been initialized)
lateinit var mUserViewModel:UserViewModel
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
val view = inflater.inflate(R.layout.fragment_list, container, false)
val name = view.editTextTextNameee.text.toString()
val prueba = mUserViewModel.search(name)
if (prueba!=null){
textView1.text = prueba.firstName
textView2.text = prueba.lastName
textView3.text = prueba.age.toString()
}else {
Toast.makeText(context, " Unregistered use", Toast.LENGTH_SHORT).show()
}
view.floatingActionButton.setOnClickListener {
findNavController().navigate(R.id.action_listFragment_to_addFragment)
}
return view
}
}
The reason for the lateinit not initialized property is you haven't created the ViewModel instance. can u try this
viewModel = ViewModelProvider(requireParentFragment(), factory).get(UserViewModel::class.java)
// factory will be the instance of the View Model
you can observe the livedata changes the onviewCreated()
It's normal because your Dao return a LiveData<List> so when you observe this data the return value is a List and not only a User. I don't know what you want to do. But you have to :
mUserViewModel.searchResult.observe(viewLifecycleOwner, Observer {
textViewName.text = it.name
textViewLastName.text = it.lastname
textViewAge.text = it.age
})
And your query will be :
#Query("SELECT * FROM user_table where name LIKE :search LIMIT 1")
suspend fun searchUser(search : String) : User
And in your mUserViewModel create a value searchResult and a method search:
fun search(val search : String){
viewModelScope.launch {
searchResult.postValue(dao.searchUser(search))
}
}

Kotlin with Anko unknown resource and null reference

I Want to get Data from https://www.thesportsdb.com/api/v1/json/1/eventspastleague.php?id=4328
But I have error
java.lang.IllegalArgumentException: Parameter specified as non-null is
null: method kotlin.jvm.internal.Intrinsics.checkParameterIsNotNull,
parameter data at
com.yutra.jadwalbola.last_match.LastMatchFragment.showTeamList(Unknown
Source:21) at
com.yutra.jadwalbola.last_match.LastMatchPresenter$getTeamList$1$1.invoke(LastMatchPresenter.kt:21)
at
com.yutra.jadwalbola.last_match.LastMatchPresenter$getTeamList$1$1.invoke(LastMatchPresenter.kt:8)
this is my LastMatchDBApi
object LastMatchDBApi {
fun getMatch(): String {
return Uri.parse(BuildConfig.BASE_URL).buildUpon()
.appendPath("api")
.appendPath("v1")
.appendPath("json")
.appendPath(BuildConfig.TSDB_API_KEY)
.appendPath("eventspastleague.php")
.appendQueryParameter("id", "4328")
.build()
.toString()
}
}
this is my serialized
data class LastMatch(
#SerializedName("strEvent")
var eventName: String? = null
)
this is my presenter
class LastMatchPresenter(private val view: MainView,
private val apiRepository: ApiRepository,
private val gson: Gson) {
fun getTeamList() {
view.showLoading()
doAsync {
val data = gson.fromJson(apiRepository
.doRequest(LastMatchDBApi.getMatch()),
LastMatchResponse::class.java
)
uiThread {
view.hideLoading()
view.showTeamList(data.lastMatch)
}
}
}
}
this is my response
data class LastMatchResponse(
val lastMatch: List<LastMatch>
)
this is my fragment activity
class LastMatchFragment : Fragment(), MainView {
private lateinit var progressBar: ProgressBar
private lateinit var swipeRefresh: SwipeRefreshLayout
private var lastMatch: MutableList<LastMatch> = mutableListOf()
private lateinit var adapter: MainAdapter
private lateinit var listTeam: RecyclerView
private var key : String = "4328"
companion object {
fun newInstance(): LastMatchFragment {
return LastMatchFragment()
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
val view = UI {
linearLayout {
lparams (width = matchParent, height = wrapContent)
orientation = LinearLayout.VERTICAL
topPadding = dip(16)
leftPadding = dip(16)
rightPadding = dip(16)
// textView {
// text = "test image"
// }
relativeLayout{
lparams (width = matchParent, height = wrapContent)
listTeam = recyclerView {
lparams (width = matchParent, height = wrapContent)
layoutManager = LinearLayoutManager(ctx)
}
progressBar = progressBar {
}.lparams{
centerHorizontally()
}
}
}
}.view
adapter = MainAdapter(lastMatch)
listTeam.adapter = adapter
val request = ApiRepository()
val gson = Gson()
var presenter = LastMatchPresenter(this, request, gson)
presenter.getTeamList()
return view
}
override fun showLoading() {
progressBar.visible()
}
override fun hideLoading() {
progressBar.invisible()
}
override fun showTeamList(data: List<LastMatch>) {
lastMatch.clear()
lastMatch.addAll(data)
adapter.notifyDataSetChanged()
}
}
IF anyone have sourcode to get this data please help me
In below code you are passing list as null from LastMatchPresenter class
view.showTeamList(data.lastMatch)
By default all variables and parameters in Kotlin are non-null. If you want to pass null parameter to the method you should add ? to it's type, for example:
override fun showTeamList(data: List<LastMatch>?) {
lastMatch.clear()
lastMatch.addAll(data)
adapter.notifyDataSetChanged()
}

Resources