1

I have horizontal recycler view in which each child has nested scroll view in which I have another horizontal recycler view. View seems to bo displaying fine but I am not able to scroll child recycler view. When I am trying to scroll - parent recycler view is scrolling, which is not what I want. Already was trying to set android:nestedScrollingEnabled="false” but it’s not working.

Main view xml:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".views.home.WeatherView">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/rv_WeatherRecycler"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_marginStart="1dp"
        android:layout_marginTop="1dp"
        android:layout_marginEnd="1dp"
        android:layout_marginBottom="1dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <ProgressBar
        android:id="@+id/pb_WeatherLoading"
        style="?android:attr/progressBarStyle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

Parent recycler view item xml:

<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_margin="8dp"
    app:cardCornerRadius="40dp"
    app:cardElevation="5dp">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:srcCompat="@drawable/ic_location">

        <ImageView
            android:id="@+id/iv_CityImage"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:visibility="gone"
            tools:srcCompat="@tools:sample/avatars"
             />

        <androidx.core.widget.NestedScrollView
            android:id="@+id/sv_ScrollView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:fillViewport="true"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent">

            <androidx.constraintlayout.widget.ConstraintLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent">

                <ImageView
                    android:id="@+id/iv_LocationIcon"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="32dp"
                    app:layout_constraintEnd_toStartOf="@+id/tv_CityName"
                    app:layout_constraintHorizontal_bias="0.5"
                    app:layout_constraintHorizontal_chainStyle="packed"
                    app:layout_constraintStart_toStartOf="parent"
                    app:layout_constraintTop_toTopOf="parent"
                    app:srcCompat="@drawable/ic_location" />

                <TextView
                    android:id="@+id/tv_CityName"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="TextView"
                    app:layout_constraintBottom_toBottomOf="@+id/iv_LocationIcon"
                    app:layout_constraintEnd_toEndOf="parent"
                    app:layout_constraintHorizontal_bias="0.5"
                    app:layout_constraintStart_toEndOf="@+id/iv_LocationIcon"
                    app:layout_constraintTop_toTopOf="@+id/iv_LocationIcon" />

                <TextView
                    android:id="@+id/tv_CurrentTemp"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="400dp"
                    android:text="TextView"
                    android:textSize="48sp"
                    app:layout_constraintBottom_toTopOf="@+id/tv_CurrentWeatherDesc"
                    app:layout_constraintEnd_toEndOf="parent"
                    app:layout_constraintHorizontal_bias="0.1"
                    app:layout_constraintStart_toStartOf="parent"
                    app:layout_constraintTop_toBottomOf="@+id/iv_LocationIcon"
                    app:layout_constraintVertical_chainStyle="spread" />

                <TextView
                    android:id="@+id/tv_CurrentWeatherDesc"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="24dp"
                    android:text="TextView"
                    android:textSize="20sp"
                    app:layout_constraintBottom_toTopOf="@+id/tv_SunriseLabel"
                    app:layout_constraintEnd_toEndOf="@+id/tv_CurrentTemp"
                    app:layout_constraintHorizontal_bias="0.0"
                    app:layout_constraintStart_toStartOf="@+id/tv_CurrentTemp"
                    app:layout_constraintTop_toBottomOf="@+id/tv_CurrentTemp" />

                <TextView
                    android:id="@+id/tv_SunriseLabel"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="32dp"
                    android:text="Sunrise"
                    app:layout_constraintBottom_toTopOf="@+id/tv_SunsetLabel"
                    app:layout_constraintEnd_toStartOf="@+id/tv_SunriseValue"
                    app:layout_constraintHorizontal_chainStyle="packed"
                    app:layout_constraintStart_toStartOf="parent"
                    app:layout_constraintTop_toBottomOf="@+id/tv_CurrentWeatherDesc" />

                <TextView
                    android:id="@+id/tv_SunsetLabel"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="16dp"
                    android:layout_marginBottom="32dp"
                    android:text="Sunset"
                    app:layout_constraintBottom_toBottomOf="parent"
                    app:layout_constraintEnd_toStartOf="@+id/tv_SunsetValue"
                    app:layout_constraintHorizontal_chainStyle="packed"
                    app:layout_constraintStart_toStartOf="parent"
                    app:layout_constraintTop_toBottomOf="@+id/tv_SunriseLabel" />

                <TextView
                    android:id="@+id/tv_SunriseValue"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginStart="32dp"
                    android:text="SunriseVal"
                    app:layout_constraintBaseline_toBaselineOf="@+id/tv_SunriseLabel"
                    app:layout_constraintEnd_toEndOf="parent"
                    app:layout_constraintHorizontal_bias="0.5"
                    app:layout_constraintStart_toEndOf="@+id/tv_SunriseLabel" />

                <TextView
                    android:id="@+id/tv_SunsetValue"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginStart="32dp"
                    android:text="SunsetVal"
                    app:layout_constraintBaseline_toBaselineOf="@+id/tv_SunsetLabel"
                    app:layout_constraintEnd_toEndOf="parent"
                    app:layout_constraintHorizontal_bias="0.5"
                    app:layout_constraintStart_toEndOf="@+id/tv_SunsetLabel" />

                <androidx.recyclerview.widget.RecyclerView
                    android:id="@+id/rv_HourlyForecastRecycler"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginStart="8dp"
                    android:layout_marginTop="32dp"
                    android:layout_marginEnd="8dp"
                    android:layout_marginBottom="32dp"
                    android:scrollbars="horizontal|vertical"
                    app:layout_constraintBottom_toBottomOf="parent"
                    app:layout_constraintEnd_toEndOf="parent"
                    app:layout_constraintStart_toStartOf="parent"
                    app:layout_constraintTop_toBottomOf="@+id/tv_SunsetLabel" />

            </androidx.constraintlayout.widget.ConstraintLayout>
        </androidx.core.widget.NestedScrollView>
    </androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>

Child item xml:

<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_margin="8dp"
    app:cardCornerRadius="10dp"
    app:cardElevation="4dp">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <TextView
            android:id="@+id/tv_HourlyForecast_Time"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="8dp"
            android:layout_marginTop="8dp"
            android:layout_marginEnd="8dp"
            android:layout_marginBottom="8dp"
            android:text="TextView"
            app:layout_constraintBottom_toTopOf="@+id/iv_ForecastHourly_WeatherIcon"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <ImageView
            android:id="@+id/iv_ForecastHourly_WeatherIcon"
            android:layout_width="64dp"
            android:layout_height="64dp"
            android:layout_marginStart="8dp"
            android:layout_marginTop="8dp"
            android:layout_marginEnd="8dp"
            android:layout_marginBottom="8dp"
            app:layout_constraintBottom_toTopOf="@+id/tv_HourlyForecast_Temp"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/tv_HourlyForecast_Time"
            app:srcCompat="@drawable/ic_error" />

        <TextView
            android:id="@+id/tv_HourlyForecast_Temp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="8dp"
            android:layout_marginTop="8dp"
            android:layout_marginEnd="8dp"
            android:layout_marginBottom="8dp"
            android:text="TextView"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/iv_ForecastHourly_WeatherIcon" />

    </androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>

Main view code:

@AndroidEntryPoint
class WeatherView : Fragment() {

    private var _binding: FragmentWeatherViewBinding? = null
    private val binding: FragmentWeatherViewBinding get() = _binding!!

    private val viewModel by viewModels<WeatherViewModel>()

    private lateinit var weatherAdapter: WeatherAdapter

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        _binding = FragmentWeatherViewBinding.inflate(inflater, container, false)
        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        val cities = getSavedCitiesFromFirebase()
        viewModel.getWeatherAndImageForCities(cities)
        setupRecyclerView()
        observeWeatherData()
    }

    private fun observeWeatherData() {
        viewModel.citiesLoadingLiveData.observe(viewLifecycleOwner, { isLoading ->
            if (isLoading) {
                showLoading(true)
            } else {
                showLoading(false)
            }
        })

        viewModel.citiesDataLiveData.observe(viewLifecycleOwner, { cities ->
            if (cities != null) {
                weatherAdapter.setCities(cities)
            }
        })

        viewModel.cityFetchErrorLiveData.observe(viewLifecycleOwner, {
            /* To Do */
        })
    }

    private fun setupRecyclerView() {
        weatherAdapter = WeatherAdapter()

        binding.rvWeatherRecycler.apply {
            layoutManager = LinearLayoutManager(
                requireContext(), RecyclerView.HORIZONTAL, false
            )
            adapter = weatherAdapter
        }
    }

    private fun showLoading(isLoading: Boolean) {
        binding.pbWeatherLoading.visibility = if (isLoading) View.VISIBLE else View.GONE
    }

    private fun getSavedCitiesFromFirebase(): List<String> {
        return listOf("Berlin", "Amsterdam")
    }
    
}

Parent recycler view adapter code:

class WeatherAdapter: RecyclerView.Adapter<WeatherAdapter.WeatherViewHolder>() {

    private var cities: MutableList<CityData> = mutableListOf()

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): WeatherViewHolder {
        val itemBinding = ItemWeatherBinding.inflate(LayoutInflater.from(parent.context), parent, false)
        return WeatherViewHolder(itemBinding.root, itemBinding)
    }

    override fun onBindViewHolder(holder: WeatherViewHolder, position: Int) {
        holder.bind(cities[position])
    }

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

    fun setCities(newCities: List<CityData>) {
        cities.clear()
        cities.addAll(newCities)
        notifyDataSetChanged()
    }

    class WeatherViewHolder(private val itemView: View, private val itemBinding: ItemWeatherBinding): RecyclerView.ViewHolder(itemBinding.root) {

        fun bind(item: CityData) {
            itemBinding.rvHourlyForecastRecycler.apply {
                layoutManager = LinearLayoutManager(
                    itemView.context, RecyclerView.HORIZONTAL, false
                )
                adapter = HourlyForecastAdapter(item.forecastModel.list)
                setHasFixedSize(true)
            }

            itemBinding.tvCityName.text = item.weatherModel.name
            itemBinding.tvCurrentTemp.text = TemperatureConverter.convertKelvinToCelsius(item.weatherModel.main.temp).toString()
            itemBinding.tvCurrentWeatherDesc.text = item.weatherModel.weather[0].description
            itemBinding.tvSunriseValue.text = TimeConverter.convertTime(item.weatherModel.sys.sunrise)
            itemBinding.tvSunsetValue.text = TimeConverter.convertTime(item.weatherModel.sys.sunset)

            Glide
                .with(itemBinding.ivCityImage)
                .load(item.cityImg)
                .error(R.drawable.ic_error)
                .centerCrop()
                .into(itemBinding.ivCityImage)
        }
    }

}

Child recycler view adapter code:

class HourlyForecastAdapter(private val forecast: List<WeatherForecast>): RecyclerView.Adapter<HourlyForecastAdapter.HourlyForecastViewHolder>() {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HourlyForecastViewHolder {
        val itemBinding = ItemWeatherHourlyForecastBinding.inflate(LayoutInflater.from(parent.context), parent, false)
        return HourlyForecastViewHolder(itemBinding)
    }

    override fun onBindViewHolder(holder: HourlyForecastViewHolder, position: Int) {
        holder.bind(forecast[position])
    }

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

    class HourlyForecastViewHolder(private val itemBinding: ItemWeatherHourlyForecastBinding): RecyclerView.ViewHolder(itemBinding.root) {

        fun bind(item: WeatherForecast) {
            Timber.d("DT: ${item.dt}")
            itemBinding.tvHourlyForecastTime.text = TimeConverter.convertTime(item.dt.toInt())
            itemBinding.tvHourlyForecastTemp.text = TemperatureConverter.convertKelvinToCelsius(item.main.temp).toString()
            Glide
                .with(itemBinding.ivForecastHourlyWeatherIcon)
                .load("https://openweathermap.org/img/wn/${item.weather[0].icon}@2x.png")
                .error(R.drawable.ic_error)
                .centerCrop()
                .into(itemBinding.ivForecastHourlyWeatherIcon)
        }

    }

}
gawron103
  • 167
  • 6
  • 21
  • Can you share an image of the design you want to achieve? Having multiple nested views inside each other can be difficult to manage. I'd like to see what you're trying to build. – Marton Jun 13 '22 at 11:10

0 Answers0