Search code examples
androidxmlandroid-layoutandroid-recyclerviewandroid-viewmodel

No adapter attached; skipping layout. Error with Recycler View


In my app I integrate rest api to show weather at life time , and I have problems to bind viewmodel and Recyclerview adapter in fragment to get final result and show weather. I have successful response , so I don't have problems with it.

Also I integrate locations permissions , maybe errors could be of this.

Here is the code of my xml layout and logic.

data class WeatherCurrentResponse(
    @SerializedName("latitude")
    var lat :Double,
    @SerializedName("longitude")
    var lon:Double,
    @SerializedName("current_weather")
    var weathercurrent:CurrentWeather,

)
data class CurrentWeather(
    val time: LocalDateTime,
    @SerializedName("temperature")
    val temp : Int
)

@HiltViewModel
class WeatherViewModel @Inject constructor(private val repository: CurrentRepository,
                                           private val location: TrackLocation ):ViewModel() {
private val _weatherstate = MutableLiveData<Response<WeatherCurrentResponse>>()
    val weatherstate:LiveData<Response<WeatherCurrentResponse>> = _weatherstate

fun displayweatherbylocation() {
    viewModelScope.launch {
try {
    location.getCurrentlocation()?.let {
        loc->
      val result = repository.getCurrentWeather(loc.latitude,loc.longitude)
          _weatherstate.value = result
    }
}catch (e:ViewmodelException){
Log.d("VM EXCEPTION","Check viewmodel probably nullpointexcept")
}

    }
}
    }

class CurrentWeatherRecView
    :RecyclerView.Adapter<CurrentWeatherRecView.WeatherListHolder>() {
    var weatherlist = ArrayList<WeatherCurrentResponse>()
    inner class WeatherListHolder(itemView:View) :RecyclerView.ViewHolder(itemView){
        fun bind(data:WeatherCurrentResponse){
            itemView.findViewById<TextView>(R.id.tempcurrent).text =
                data.weathercurrent.temp.toString()
            itemView.findViewById<TextView>(R.id.time).text = data.weathercurrent.time.format(
                DateTimeFormatter.ofPattern("yyyy/MM/dd")
            )
            itemView.findViewById<TextView>(R.id.city).text =
                data.lat.compareTo(data.lon).toString()
        }
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): WeatherListHolder {
        val view = LayoutInflater.from(parent.context).inflate(R.layout.forecast_items,parent,
            false)
        return WeatherListHolder(view)
    }

    override fun getItemCount(): Int {
   return  weatherlist.size
    }

    override fun onBindViewHolder(holder: WeatherListHolder, position: Int) {
     weatherlist[position].let {
         holder.bind(data = it)
     }
    }


    fun updateInfo(list:ArrayList<WeatherCurrentResponse>){
        weatherlist.clear()
        weatherlist.addAll(list)
        notifyDataSetChanged()
    }
}

@AndroidEntryPoint
class ListWeatherFragment : Fragment() {
    lateinit var recView:RecyclerView
private lateinit var binding:FragmentListWeatherBinding
private val viewModel:WeatherViewModel by viewModels()
private lateinit var permissionlauncher:ActivityResultLauncher<Array<String>>
    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        binding = FragmentListWeatherBinding.inflate(inflater)
        return binding.root
    }


    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        permissionlauncher = registerForActivityResult(
            ActivityResultContracts.RequestMultiplePermissions()
        ){
            recView = binding.recviewInFragment
            recView.apply {
                layoutManager = LinearLayoutManager(context,LinearLayoutManager.VERTICAL,
                    false)
                val weatheradapter = CurrentWeatherRecView()
                adapter = weatheradapter
                viewModel.displayweatherbylocation()
                viewModel.weatherstate.observe(viewLifecycleOwner) {
                 data->
                    data.body()?.let {
                        weatheradapter.updateInfo(
                            list = ArrayList()
                        )
                    }
                }
            }
        }
        permissionlauncher.launch(arrayOf(
            Manifest.permission.ACCESS_FINE_LOCATION,
            Manifest.permission.ACCESS_COARSE_LOCATION
        ))
    }
    }


Solution

  • Here is the problem:

    viewModel.weatherstate.observe(viewLifecycleOwner) {
                 data->
                    data.body()?.let {
                        weatheradapter.updateInfo(
                            list = ArrayList()
                        )
                    }
                }
    

    Instead of giving the result to your RecyclerViewAdapter, you always give a new empty list list = ArrayList(). You must use the received body inside your let-block to update the RecyclerViewAdapter.

    viewModel.weatherstate.observe(viewLifecycleOwner) {
                     data->
                        data.body()?.let { body ->
                            weatheradapter.updateInfo(
                                list = body
                            )
                        }
                    }
    

    You can simplify your Adapter by using ListAdapter. It will manage updating the list and animate any changed (see also DiffCallback for better update handling).