How to instantiate ViewModelFactory in Activity - android-studio

CAN SOMEONE HELP ME HOW TO INSTATIATE VIEWMODELFACTORY IN ACTIVITY
CAN SOMEONE HELP ME HOW TO INSTATIATE VIEWMODELFACTORY IN ACTIVITY
MAIN ACTIVITY
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val viewModelFactory = DiaryViewModelFactory(diaryDao)
val viewModel = ViewModelProvider(this).get(DiaryViewModel::class.java)
VIEWMODELFACTORY
override fun <T : ViewModel> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(DiaryViewModel::class.java)) {
#Suppress("UNCHECKED_CAST")
return DiaryViewModel(diaryDao) as T
}
throw IllegalArgumentException("Unknown ViewModel class")
}
}
APPLICATION CLASS
val database : DiaryDatabase by lazy {
DiaryDatabase.getDatabase(this)
}
}
DAO CLASS
interface DiaryDao {
#Query("SELECT * from diary WHERE id = :id")
fun getItem(id: Int): Flow<Entry>
#Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun insert(entry: Entry)
#Update
suspend fun update(entry: Entry)
#Delete
suspend fun delete(entry: Entry)
#Query("SELECT * from diary ORDER BY id ASC")
fun getItems(): Flow<List<Entry>>
}

Finally instantiated viewModelFactory like this
val viewModel: DiaryViewModel by viewModels{
DiaryViewModel.DiaryViewModelFactory((application as DiaryApplication).database.diaryDao())
}

Related

Update method of ViewModel class is not working

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)

MainActivity is not showing log statements

Hi I am new to android development and the problem is Whenever I am using MainActivity in logcat as a filter it is not showing me Log statements for this Log.i(TAG, "TAG on Fb")(my tag is also MainActivity) and Log.i(TAG, "onitemclick $position") for this it is shown by its function that is onItemClick but the same happen with this statement whenever I am trying to filter it on the basis of MainActivity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
userMaps = generateSampleData() as MutableList<user>
//set layout manager on the screen => RV
rvmaps.layoutManager = LinearLayoutManager(this)
//set adapter on RV
mapAdapter = mapadapter(this, userMaps, object: mapadapter.OnClickListener{
override fun onitemclick(position: Int) {
Log.i(TAG, "onitemclick $position")
val intent = Intent(this#MainActivity, googlemaps::class.java)
// here we will use putextra which help us to put extra data to another activity through data
intent.putExtra(user_data_map, userMaps[position])
startActivity(intent)
}
})
rvmaps.adapter = mapAdapter
fabcreatemap.setOnClickListener{
Log.i(TAG, "TAG on Fb")
val intent = Intent(this#MainActivity, createmap::class.java)
intent.putExtra(user_map_title, "new map name")
startActivityForResult(intent, requestcode)
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if(requestcode == requestCode && resultCode == Activity.RESULT_OK)
{
val usermap = data?.getSerializableExtra(user_data_map) as user
// userMaps.add(usermap)
// mapAdapter.notifyItemChanged(userMaps.size-1)
}
super.onActivityResult(requestCode, resultCode, data)
}
}

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

How To Show Interstitial Ads in The getView Function in Kotlin?

I'm a Beginner in Kotlin and I want a way to display Interstitial Ad every time I click on the button tvName, but on the contrary, the app crashes whenever I click on the button. I searched for the solution for a long time.
Here's MainActivity
import ...
#Suppress("UNREACHABLE_CODE")
class MainActivity : AppCompatActivity() {
lateinit var mAdView : AdView
var adapter:ChaptersAdapter?=null
var listOfChapters= ArrayList<Chapters>()
#SuppressLint("WrongViewCast")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
MobileAds.initialize(this) {}
mAdView = findViewById(R.id.adView)
val adRequest = AdRequest.Builder().build()
mAdView.loadAd(adRequest)
loadChapters()
adapter = ChaptersAdapter(listOfChapters, this)
lvchapters.adapter = adapter
MobileAds.initialize(this,
"ca-app-pub-3940256099942544~3347511713")
mInterstitialAd = InterstitialAd(this)
mInterstitialAd.adUnitId = "ca-app-pub-3940256099942544/1033173712"
mInterstitialAd.loadAd(AdRequest.Builder().build())
}
fun loadChapters(){
listOfChapters.add(Chapters(" Chapter 1 ", applicationContext.assets.open("Chapter0.txt").bufferedReader().use {
it.readText()
}
))
listOfChapters.add(Chapters(" Chapter 2 ", applicationContext.assets.open("Chapter1.txt").bufferedReader().use {
it.readText()
}
))
}
class ChaptersAdapter: BaseAdapter {
var context:Context?=null
var listOfChaptersLocal= ArrayList<Chapters>()
constructor(listOfChapters:ArrayList<Chapters>,context:Context){
listOfChaptersLocal=listOfChapters
this.context=context
}
override fun getView(p0: Int, p1: View?, p2: ViewGroup?): View {
val chapters= listOfChaptersLocal[p0]
var inflator= context!!.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
val chaptersView=inflator.inflate(R.layout.list_chapters,null)
chaptersView.tvName.text= chapters.name!!
chaptersView.**tvName.setOnClickListener** {
// **i want to show the Ads in this time frame** but like this example it didn't work
if (mInterstitialAd.isLoaded) {
mInterstitialAd.show()
} else {
Log.d("TAG", "The interstitial wasn't loaded yet.")
}
val intent =Intent(context,ChapterDetails::class.java)
intent.putExtra("name",chapters.name!!)
intent.putExtra("des",chapters.des!!)
context!!.startActivity(intent)
}
return chaptersView
}
override fun getItem(p0: Int): Any {
return listOfChaptersLocal[p0]
}
override fun getItemId(p0: Int): Long {
return p0.toLong()
}
override fun getCount(): Int {
return listOfChaptersLocal.size
}
}
}

how to fix error lkotlin.UninitializedPropertyAccessException: lateinit property adapters has not been initialized in my code?

does not set adapter and work it in Kotlin
I take data from retrofit and i test it ,it work.
but when pass array list to adapter,it cant set list
and have error:
kotlin.UninitializedPropertyAccessException: lateinit property adapters has not been initialized
myadapter:
class RecyclerSaleAdapter(var sale_list: ArrayList<sale_agahi>): RecyclerView.Adapter<RecyclerSaleAdapter.ViewHolder>(){
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val v = LayoutInflater.from(parent.context).inflate(R.layout.recycler_sale_item, parent, false)
return ViewHolder(v)
}
override fun getItemCount(): Int {
return sale_list.size
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.cost_view.text=sale_list[position].cost.toString()
holder.area.text=sale_list[position].area
holder.info_view.text=sale_list[position].info
holder.metr_view.text=sale_list[position].metr.toString()
}
class ViewHolder(itemView: View): RecyclerView.ViewHolder(itemView){
val ImageView=itemView.findViewById<ImageView>(R.id.image_sale_view)
val cost_view=itemView.findViewById<TextView>(R.id.cost_text_rec_view)
val area=itemView.findViewById<TextView>(R.id.area_text_rec_view)
val info_view=itemView.findViewById<TextView>(R.id.info_text_rec_view)
val metr_view=itemView.findViewById<TextView>(R.id.metr_text_rec_view)
val btn_rec_item=itemView.findViewById<Button>(R.id.btn_item_rec)
}
}
myactivity on create:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_profile)
val toolbar: Toolbar = findViewById(R.id.toolbar)
setSupportActionBar(toolbar)
val fab: FloatingActionButton = findViewById(R.id.fab)
fab.setOnClickListener { view ->
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show()
}
val drawerLayout: DrawerLayout = findViewById(R.id.drawer_layout)
val navView: NavigationView = findViewById(R.id.nav_view)
val toggle = ActionBarDrawerToggle(
this, drawerLayout, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close
)
drawerLayout.addDrawerListener(toggle)
toggle.syncState()
//var intent=Intent(this,Profile::class.java)
var us= User_info()
us.name=intent.getStringExtra("username")
us.image_url=intent.getStringExtra("image")
user= User_info()
this.user =us
get_sales_list()
var rec=findViewById<RecyclerView>(R.id.rec_sale)
rec.layoutManager = LinearLayoutManager(this, LinearLayout.VERTICAL, false)
rec.adapter=adapters
navView.setNavigationItemSelectedListener(this)
}
and get data fun:
fun get_sales_list()
{
var retrofit = Retrofit.Builder()
.baseUrl("http://sobosha.ir/")
.addConverterFactory(GsonConverterFactory.create())
.build()
val services=retrofit.create(Apiservices::class.java)
val call =services.sale_agahi(user.name.toString())
call.enqueue(object : Callback<sales_list> {
override fun onResponse(call: Call<sales_list>, response: retrofit2.Response<sales_list>) {
var temp:sales_list= response.body()!!
arr_sale= temp
Log.d("arr_sale",arr_sale.list[0].phone.toString())
adapters=RecyclerSaleAdapter(arr_sale.list)
}
override fun onFailure(call: Call<sales_list>, t: Throwable) {
Toast.makeText(applicationContext,t.message,Toast.LENGTH_LONG).show()
}
})
}
I have an error on rec.adapter=adapters
You only set adapters in the line
adapters=RecyclerSaleAdapter(arr_sale.list)
when Retrofit receives a response. The lines
var rec=findViewById<RecyclerView>(R.id.rec_sale)
rec.layoutManager = LinearLayoutManager(this, LinearLayout.VERTICAL, false)
rec.adapter=adapters
are executed after get_sales_list returns but the request is still likely enqueued or sent and the response is not received yet, so adapters isn't initialized.
One workaround would be to pass rec to get_sales_list and make it set rec.adapter instead of adapters. I'd prefer to e.g. make get_sales_list return Future<sales_list> or to make it suspend and return sales_list, but these would be more work.

Resources