Method setCurrentState must be called on the main thread, Android, Kotlin - android

Android studio shows errors in the lines
REPOSITORY.insert(note){ onSuccess() }
in the "AddNewNoteFragmentViewModel" and in the lines
viewModel.insert(AppNote(name = name, text = text)){ view?.findNavController()?.navigate(R.id.action_addNewNoteFragment_to_mainFragment)}
in the file "AddNewNoteFragment"
I realized that the error is related to streams, but I do not know how to solve it
If anyone knows, please help, I could not find anything worthwhile on the Internet
AddNewNoteFragment
package com.example.notes.fragments.add_new_note
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.lifecycle.ViewModelProvider
import androidx.navigation.findNavController
import com.example.notes.R
import com.example.notes.databinding.FragmentAddNewNoteBinding
import com.example.notes.model.AppNote
import com.example.notes.utilits.showToast
class AddNewNoteFragment : Fragment() {
private var _binding: FragmentAddNewNoteBinding? = null
val binding get() = _binding!!
private lateinit var viewModel: AddNewNoteFragmentViewModel
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FragmentAddNewNoteBinding.inflate(inflater, container, false)
val view = binding.root
return view
}
override fun onStart() {
super.onStart()
initialization()
}
private fun initialization() {
viewModel = ViewModelProvider(this).get(AddNewNoteFragmentViewModel::class.java)
binding.buttonAddNote.setOnClickListener {
val name = binding.inputNameNote.text.toString()
val text = binding.inputTextNote.text.toString()
if (name.isEmpty()){
showToast("Введите имя заметки")
} else{
viewModel.insert(AppNote(name = name, text = text)){
view?.findNavController()?.navigate(R.id.action_addNewNoteFragment_to_mainFragment)
}
}
}
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
AddNewNoteFragmentViewModel
package com.example.notes.fragments.add_new_note
import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import com.example.notes.model.AppNote
import com.example.notes.utilits.REPOSITORY
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
class AddNewNoteFragmentViewModel(application: Application): AndroidViewModel(application) {
fun insert(note: AppNote,onSuccess:()-> Unit) =
viewModelScope.launch (Dispatchers.IO){
REPOSITORY.insert(note){
onSuccess()
}
}
}
Error screen
enter image description here

Problem solved
In AddNewNoteFragmentViewModel requires Dispatchers.IO to be changed to Dispatchers.Main

Related

lateinit property recview has not been initialized

I have fragment which showing weather for 10 days with getting geolocation city and show exactly weather for this city for 10 days.
I have problem initialization with RecyclerView and Viewmodel.
Also I use Hilt to provide dependencies.
My goal is showing weather by location (already have permission) for 10 days.
import androidx.lifecycle.LiveData
import dagger.hilt.android.lifecycle.HiltViewModel
import db.entities.WeatherData
#HiltViewModel
interface ForecastViewmodel {
val dataforecast:LiveData<List<WeatherData>>
val city : LiveData<String>
val isRefreshing:LiveData<Boolean>
fun onRefresh()
}
#HiltViewModel
class ForecastViewmodelImpl(private val repository: ForecastRepository,
private val day: Day
) : ForecastViewmodel,
ViewModel(){
override val dataforecast = MutableLiveData<List<WeatherData>>()
override val city = MutableLiveData(day.city)
override val isRefreshing = MutableLiveData(true)
init {
loadForecast()
}
private fun loadForecast() {
isRefreshing.value = true
viewModelScope.launch {
try {
val data = repository.getForecast(day.days)
Timber.d("size is ${data}")
}catch (e:Exception){
Timber.e(e)
}
isRefreshing.value = false
}
}
override fun onRefresh() = loadForecast()
private fun Forecastday.toWeatherData() = WeatherData(
date = date,
temp = "${temp.roundToInt()}℃"
)
}
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.example.testtaskweatherappkevychsol.R
import db.entities.WeatherData
class WeatherRecView:RecyclerView.Adapter<WeatherRecView.WeatherHolder>() {
var listweather = emptyList<WeatherData>()
class WeatherHolder(view:View):RecyclerView.ViewHolder(view)
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): WeatherHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.daily_weather_layout,
parent,false)
return WeatherHolder(view)
}
override fun onBindViewHolder(
holder: WeatherHolder,
position: Int
) {
holder.itemView.findViewById<TextView>(R.id.Dateitem).text = listweather[position].date
holder.itemView.findViewById<TextView>(R.id.tempitem).text = listweather[position]
.temp.toString()
}
override fun getItemCount(): Int {
return listweather.size
}
fun addlistintoUI(list: List<WeatherData>){
listweather = list
}
}
data class WeatherData(
val temp:String,
val date:String
)
import RecyclerView.WeatherRecView
import Viewmodel.ForecastViewmodel
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
import com.example.testtaskweatherappkevychsol.R
import com.example.testtaskweatherappkevychsol.databinding.FragmentWeatherBinding
import dagger.hilt.android.AndroidEntryPoint
#AndroidEntryPoint
class WeatherAtTheLifeMomentFragment : Fragment(R.layout.fragment_weather){
lateinit var recview:WeatherRecView
private var binding : FragmentWeatherBinding? = null
lateinit var viewmodel:ForecastViewmodel
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val binding = FragmentWeatherBinding.inflate(inflater,container,false)
this.binding = binding
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
with(binding){
this!!.refreshbt.setOnClickListener { viewmodel.isRefreshing }
containerlistweather.adapter = recview
containerlistweather.addItemDecoration(DividerItemDecoration(containerlistweather.context,
(containerlistweather
.layoutManager as LinearLayoutManager).orientation))
with(viewmodel){
dataforecast.observe(viewLifecycleOwner) { recview.listweather = it }
City.text = city.toString()
}
}
}
override fun onDestroyView() {
super.onDestroyView()
binding = null
}
}
lateinit property recview has not been initialized
you are getting this issue as you are trying to access lateinit property recview without initialising it
in WeatherAtTheLifeMomentFragment
From
containerlistweather.adapter = recview
To
recview = WeatherRecView() // initialise recview variable by creating instance of your adapter
containerlistweather.layoutManager = LinearLayoutManager(context) // not sure you have set layout manager in your xml so just adding it in your code as it is necessary to use
containerlistweather.adapter = recview
Also you have create method addlistintoUI in WeatherRecView class so use it and make below mentioned changes to load your data
in WeatherAtTheLifeMomentFragment class
From
dataforecast.observe(viewLifecycleOwner) { recview.listweather = it }
To
dataforecast.observe(viewLifecycleOwner) { recview.addlistintoUI(it) }
in WeatherRecView class
From
fun addlistintoUI(list: List<WeatherData>){
listweather = list
}
To
fun addlistintoUI(list: List<WeatherData>){
listweather.addAll(list)
notifyDataSetChanged()
}
```

Glide won't load pictures Android Kotlin

I need images from firebase storage to be loaded into my application and displayed in recycle view
For this I decided to use a glide
But when I run the application, nothing is displayed, just a blank screen, and there are no errors
I rewrote the code several times, as for me this is the working one, but it still does not work :)
What could be causing it not to work?
NumberFragment
package com.example.hotel2.number
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.example.hotel2.UserModule
import com.example.hotel2.databinding.FragmentNumberBinding
import com.example.hotel2.utilits.showToast
import com.google.firebase.firestore.FirebaseFirestore
class NumberFragment : Fragment() {
private var _binding: FragmentNumberBinding? = null
private val binding get() = _binding!!
private lateinit var recyclerView: RecyclerView
private lateinit var adapter: NumberAdapter
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FragmentNumberBinding.inflate(inflater, container, false)
val view = binding.root
return view
}
override fun onStart() {
super.onStart()
binding.recycleView.apply {
layoutManager = LinearLayoutManager(activity)
}
fetchData()
}
fun fetchData(){
FirebaseFirestore.getInstance().collection("number/")
.get()
.addOnSuccessListener { documents ->
for (document in documents){
val user = documents.toObjects(UserModule::class.java)
binding.recycleView.adapter = NumberAdapter(context!!, user)
}
}
.addOnFailureListener {
showToast("Error")
}
}
}
NumberAdapter
package com.example.hotel2.number
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.example.hotel2.R
import com.example.hotel2.UserModule
class NumberAdapter(private val context: Context, private val users: List<UserModule>): RecyclerView.Adapter<NumberViewHolder>() {
override fun getItemCount(): Int {
return users.size
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NumberViewHolder {
return NumberViewHolder(LayoutInflater.from(parent.context)
.inflate(R.layout.recyclerview_item,parent,false))
}
override fun onBindViewHolder(holder: NumberViewHolder, position: Int) {
val user = users[position]
holder.userName.text = user.UserName
Glide.with(context)
.load(user.UserImage)
.into(holder.userImage)
}
}
class NumberViewHolder(itemView: View): RecyclerView.ViewHolder(itemView){
val userName: TextView = itemView.findViewById(R.id.textView6)
val userImage: ImageView = itemView.findViewById(R.id.image)
}
UserModule
package com.example.hotel2
data class UserModule(
val UserName: String,
val UserImage: String
){
constructor():this("","")
}

Viewmodel factory? is it something i need?

I am programing an app in kotlin using MVVM structur. i tried to instantiate my viewmodel through my factory. Though im getting an error when i start the app. It says this `
java.lang.RuntimeException: Cannot create an instance of class com.example.paperseller.ui.menu.ViewModels.AuthViewModel
I have no clue at all why im getting this trouble. My code looks like this
I have already tried to skip the factory implementation.
I would not use this way of implementing mvvm in kotlin nowadays. I would take a look at renewed tutorials 2021.
LoginRegister_fragment:
package com.example.paperseller.ui.menu
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.EditText
import android.widget.Toast
import androidx.fragment.app.activityViewModels
import androidx.fragment.app.viewModels
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.example.paperseller.Data.User
import com.example.paperseller.R
import com.example.paperseller.databinding.LoginRegisterFragmentBinding
import com.example.paperseller.ui.menu.ViewModels.AuthViewModel
class LoginRegister_fragment : Fragment(R.layout.login_register_fragment) {
private var _binding : LoginRegisterFragmentBinding? = null
private val binding get() = _binding!!
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View?
{
_binding = LoginRegisterFragmentBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
initializeUI()
}
private fun initializeUI()
{
val factory = InjectorUtils.providePaperSellerViewModelFactory()
val viewModel : AuthViewModel by viewmodels()
viewModel.message.observe(this)
{
message ->
Toast.makeText(requireActivity(), message, Toast.LENGTH_LONG).show()
}
binding.registerButton.setOnClickListener()
{
val user = User(binding.emailText.text.toString(), binding.passwordText.text.toString());
viewModel.register(user)
}
}
override fun onDestroy() {
super.onDestroy()
_binding = null
}
}
ViewModelFactory:
package com.example.paperseller.ui.menu.ViewModelFactory
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.example.paperseller.Data.PaperSellerRepository
import com.example.paperseller.ui.menu.ViewModels.AuthViewModel
class PaperSellerViewModelFactory(private val paperSellerRepository: PaperSellerRepository) : ViewModelProvider.NewInstanceFactory()
{
#Suppress("UNCHECKED_CAST")
override fun <T:ViewModel?> create(modelClass: Class<T>):T{
return AuthViewModel(paperSellerRepository) as T
}
}
I tried to follow this tutorial:
https://resocoder.com/2018/09/07/mvvm-on-android-crash-course-kotlin-
This tutorial is 4 years old. Tip to people seeing this question later. Check tutorials on youtube that is up to date this helped me alot.
Your ViewModel takes in a repository and since then You have to use the ViewModelFactory. You can either use ViewModelProvider:
val factory = InjectorUtils.providePaperSellerViewModelFactory()
val viewModel = ViewModelProvider(
this,
factory)
.get(AuthViewModel::class.java)
or, as #IR42 has pointed out, You can use viewModels delegate:
val viewModel: AuthViewModel by viewModels { factory }

How do I use popBackStack() to return to a previous fragment in my Android app?

I'm trying to return back to a previous fragment from an activity but I cannot get the functionality to work.
Here is the relevant code from my activity class:
fun previousSubCountryListButtonClicked(view: View) {
val fragmentManager = supportFragmentManager
fragmentManager.popBackStack(R.id.navigation_scotland, POP_BACK_STACK_INCLUSIVE)
}
and here is my fragment class:
package com.riverstonetech.gositeuk.ui.scotland
import android.content.Intent
import android.content.Intent.getIntent
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ArrayAdapter
import android.widget.ProgressBar
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import com.google.firebase.firestore.FirebaseFirestore
import com.riverstonetech.gositeuk.CountriesActivity
import com.riverstonetech.gositeuk.R
import com.riverstonetech.gositeuk.RegionActivity
import kotlinx.android.synthetic.main.fragment_scotland.*
class ScotlandFragment : Fragment() {
// Access a Cloud Firestore instance
val db = FirebaseFirestore.getInstance()
lateinit var adapter : ArrayAdapter<String>
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val root = inflater.inflate(R.layout.fragment_scotland, container, false)
(requireActivity() as CountriesActivity).initializeCustomActionBar(R.drawable.scotland_flag, R.string.title_regions)
var regions : ArrayList<String>
val docRef = db.collection("UKSites").document("Scotland")
val progressBar: ProgressBar = root.findViewById(R.id.regionsLoadingProgressBar)
docRef.get()
.addOnSuccessListener { document ->
progressBar?.visibility = ProgressBar.VISIBLE
if (document != null) {
regions = document.get("Regions") as ArrayList<String>
adapter = ArrayAdapter(requireContext(), R.layout.list_item, regions)
regionsListView.adapter = adapter
regionsListView.setOnItemClickListener { parent, view, position, id ->
val intent = Intent(activity!!, RegionActivity::class.java)
intent.putExtra("SUB_COUNTRY", regions[position])
startActivity(intent)
}
progressBar?.visibility = ProgressBar.GONE
} else {
Log.d("Debug", "No such document")
}
}
.addOnFailureListener { exception ->
Log.d("Debug", "get failed with ", exception)
}
return root
}
}
I am struggling to understand the official documentation on Android so I would appreciate any help possible. Do I need to add a transaction in my fragment class?
I used finish() instead of writing code to manipulate a fragment manager.

Using LibGDX inside Android Project as Fragment : How do i call libgdx method from android part?

I am using libGDX inside android project as fragment all with kotlin and its working fine.
What i am trying to do is call a method of libgdx part of project from android part(MainActivity) of the project.
So for example if user presses on button which is made by android part,object in game will move.
First of all this is the project structure:
MainActivity:
package com.krytasoft.androidwithlibgdx
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
//import android.support.v4.app.Fragment throws unresolved error.without this compiles fine and works but shows type mismatch error.
import com.badlogic.gdx.backends.android.AndroidFragmentApplication
import com.krytasoft.gdxandroid.AndroidGameFragment
class MainActivity : AppCompatActivity(), AndroidFragmentApplication.Callbacks {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val libgdxGameFragment:AndroidGameFragment = AndroidGameFragment()
val button = findViewById<Button>(R.id.openFlexBoxTestButton)
val moveRightButton = findViewById<Button>(R.id.moveRightButton)
//never mind if this supportFragmentManager... shows type mismatch error.Its working. this line puts libgdx into fragment.fragment is similar to component in react.
supportFragmentManager.beginTransaction().replace(R.id.fragment_container, libgdxGameFragment, AndroidGameFragment::class.java.simpleName).commit()
button.setOnClickListener{
val intent = Intent(this, FlexBoxTestActivity::class.java)
startActivity(intent)
}
moveRightButton.setOnClickListener {
libgdxGameFragment.moveRight()
}
}
override fun exit() {
}
}
Here in MainActivity, notice moveRightButton.Its calling moveRight() function from fragment.
AndroidFragment class:
package com.krytasoft.gdxandroid
import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.badlogic.gdx.backends.android.AndroidApplicationConfiguration
import com.badlogic.gdx.backends.android.AndroidFragmentApplication
import com.krytasoft.mygdxgame.core.MyGdxGame
class AndroidGameFragment : AndroidFragmentApplication() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
super.onCreateView(inflater, container, savedInstanceState)
val config = AndroidApplicationConfiguration()
return initializeForView(MyGdxGame(), config)
}
override fun startActivity(intent: Intent?) {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
fun moveRight(){
MyGdxGame().moveRight()
}
}
MyGDX Game:
package com.krytasoft.mygdxgame.core
import com.badlogic.gdx.ApplicationAdapter
import com.badlogic.gdx.Gdx
import com.badlogic.gdx.graphics.GL20
import com.badlogic.gdx.graphics.Texture
import com.badlogic.gdx.graphics.g2d.Sprite
import com.badlogic.gdx.graphics.g2d.SpriteBatch
class MyGdxGame : ApplicationAdapter() {
lateinit var batch: SpriteBatch
lateinit var img: Texture
lateinit var sprite:Sprite
override fun create() {
batch = SpriteBatch()
img = Texture("badlogic.jpg")
sprite = Sprite(img)
println("create done from libgdx")
}
override fun render() {
Gdx.gl.glClearColor(1f, 0f, 0f, 1f)
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT)
batch.begin()
batch.draw(sprite, 50f, 50f)
batch.end()
}
override fun dispose() {
batch.dispose()
img.dispose()
}
fun moveRight(){
sprite.x +=50f; // throws lateinit var sprite is uninitialzied error.
}
}
So what i expect is after pressing move right button,eventually call fun moveRight in mylibgdxgame and change position of sprite.
kotlin.UninitializedPropertyAccessException: lateinit property sprite has not been initialized
eventhough its initialized.But for some reason its not seen.
I uploaded my project to github:
https://github.com/lastpeony/libgdx-in-android-kotlin
Call create() method before using MyGdxGame instance. Also use a single instance of MyGdxGame across fragment.
class AndroidGameFragment : AndroidFragmentApplication() {
val myGdxGame = MyGdxGame()
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
super.onCreateView(inflater, container, savedInstanceState)
val config = AndroidApplicationConfiguration()
myGdxGame.create()
return initializeForView(myGdxGame, config)
}
fun moveRight(){
myGdxGame .moveRight()
}
}

Categories

Resources