Menu
Who Do Is
  • Home
  • What
  • How
  • Is
  • Can
  • Are
  • Does
  • Do
  • Why
  • Who
  • Where
  • Which
  • Which
  • Should
  • Will
  • When
  • What’s
  • Did
Who Do Is

[ANSWERED] kotlin – Android: RecyclerView randomly changing height

Posted on November 14, 2022

Solution 1 :

You have constraints set which are unused because your view is not inside a ConstraintLayout. Change this:

<androidx.viewpager2.widget.ViewPager2
        android_id="@+id/view_pager"
        android_layout_width="wrap_content"
        android_layout_height="wrap_content"
        android_layout_marginTop="48dp"
        app_layout_constraintEnd_toEndOf="parent"
        app_layout_constraintHorizontal_bias="0.0"
        app_layout_constraintStart_toStartOf="parent"
        app_layout_constraintTop_toBottomOf="@+id/tab_layout">

    </androidx.viewpager2.widget.ViewPager2>
    

to this:

<androidx.viewpager2.widget.ViewPager2
        android_id="@+id/view_pager"
        android_layout_width="wrap_content"
        android_layout_height="match_parent"
        android_layout_marginTop="48dp">

    </androidx.viewpager2.widget.ViewPager2>

Also, change your height of your recycler view to:

android:layout_height="match_parent"

Problem :

In my fragment I have a ViewPager2 component, each page containing a fragment that only holds a RecyclerView to display lists. The elements inside a list are meant to be moved around the lists (from the RecyclerView of one page the the RecyclerView of another page). So I wrote some logic to update the adaptors of the RecyclerViews to be able to move items around.

Updating the datasets works as expected, but for some reason, after moving an item from a list to another, the height of the lists changes. This behaviour is not consistent. Sometimes all the lists will get shrinked to the same height, sometimes only some of them have their height changed, sometimes some lists get their height set to 0, and sometimes everything works normally. Setting a fixed height to the RecyclerView fixed the issue, although I want the list to take up the entire space of the display, so a fixed height is obviously not a solution.

Also, I am not sure if it’s the RecyclerView that shrinks and the ViewPager updates it’s height accordingly or if it’s vice versa.

Looking at the Sunflower example project in the Android docs I couldn’t see any relevant difference between my project and the example, so I have no idea what is causing this behavior. Does anyone have any idea?

READ  [ANSWERED] android - Dynamic Feature Module not getting installed while onCompleteListener of splitInstallManager is showing success
Powered by Inline Related Posts

Here are the relevant parts of my application:
Note: Anything database related is using the Room API. Also, The adapter for the RecyclerView was originally RecyclerView.Adapter, not ListAdapter, but the behavior is the same. I am willing to use any of them if the problem is related to the adapter.

MainFragment:

class MainFragment : Fragment() {

    private lateinit var binding: FragmentMainBinding
    private lateinit var viewPagerAdapter: ViewPagerAdapter;
    private lateinit var viewPager2: ViewPager2

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        binding = FragmentMainBinding.inflate(inflater, container, false);
        return binding.root;
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)


        //Init the view pager
        viewPagerAdapter = ViewPagerAdapter(this)
        viewPager2 = binding.viewPager
        viewPager2.adapter = viewPagerAdapter
        viewPager2.isUserInputEnabled = false

        //init the tab layout

        binding.tabLayout.apply {
            TabLayoutMediator(this, viewPager2) { tab, position ->
                tab.text = TAB_LAYOUT_LABELS[position]
            }.attach()
        }

    }

    companion object {
        @JvmStatic
        fun newInstance() = MainFragment()
        private val TAB_LAYOUT_LABELS = arrayOf("TO BE READ", "READING", "DONE")
    }
} 

ViewPagerAdapter:

class ViewPagerAdapter(fragment: Fragment) : FragmentStateAdapter(fragment) {
    override fun getItemCount(): Int = 3

//    TODO: Create a separate fragment for the DONE list
override fun createFragment(position: Int): Fragment {
    val fragment = ReadingListFragment()
    fragment.arguments = Bundle().apply {
        putInt(ReadingListFragment.EXTRA_TYPE, position)
    }
    return fragment
}
}

ReadingListFragment

class ReadingListFragment : Fragment() {

    companion object {
        fun newInstance() =
            ReadingListFragment()

        public const val EXTRA_TYPE = "extraType"
    }

    private val viewModel: ReadingListViewModel by viewModels<ReadingListViewModel> {
        val type = ReadingListType.getType(arguments?.getInt(EXTRA_TYPE) ?: 3)
        ReadingListViewModelFactory(requireActivity().application, type)
    }
    private lateinit var binding: ReadingListFragmentBinding
    private lateinit var readingListAdapter: ReadingListAdapter

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        viewModel.readingList.observe(this) {
            val adapter = ReadingListAdapter(viewModel)
//            binding.readingListRecyclerView.swapAdapter(adapter, false)
            this.readingListAdapter.changeData(viewModel)
        }
    }


    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        binding = ReadingListFragmentBinding.inflate(inflater, container, false)

        //Init the recycler view

        val layoutManager = LinearLayoutManager(activity)
        this.readingListAdapter = ReadingListAdapter(viewModel)
        binding.readingListRecyclerView.apply {
            val value = viewModel.readingList.value
            adapter = readingListAdapter
            this.layoutManager = layoutManager
        }

        return binding.root
    }

}

ReadingListViewModel:

class ReadingListViewModel(private val app: Application, private val type: ReadingListType) :
    AndroidViewModel(app) {

    val readingList: LiveData<List<GoodreadsBook>> by lazy {
        Database.getInstance(app.applicationContext).goodreadsBookDao()
            .getReadingListAsLiveData(type)
    }


    //    Move item to the next list
    fun moveToTheNextList(pos: Int) {

        val item = readingList.value?.get(pos)

        //Update the item in memory
        if (item?.owner != null) {
            val newOwner = ReadingListType.getType(item.owner!!.value + 1)
            item.owner = newOwner


            //Update the item in the database


            viewModelScope.launch {
                withContext(Dispatchers.IO) {
                    val db = Database.getInstance(app.applicationContext)
                    db.goodreadsBookDao().updateBook(item)
                }
            }
        }
    }

}

@Parcelize
enum class ReadingListType(val value: Int) : Parcelable {
    TO_BE_READ(0), READING(1), DONE(2), UNSET(3);

    companion object {
        fun getType(value: Int) = values().first { it.value == value }
    }
}

class ReadingListTypeConverter {
    @TypeConverter
    fun fromReadingListTypeToInt(it: ReadingListType) = it.value

    @TypeConverter
    fun fromIntToReadingListType(it: Int) = ReadingListType.getType(it)
}

ReadingListViewModelFactory:

class ReadingListViewModelFactory(private val app: Application, private val type: ReadingListType) :
    ViewModelProvider.Factory {
    override fun <T : ViewModel?> create(modelClass: Class<T>): T =
        ReadingListViewModel(app, type) as T
}

ReadingListAdapter:

class ReadingListAdapter(private var viewModel: ReadingListViewModel) :
    ListAdapter<GoodreadsBook, ReadingListViewHolder>(ReadingListItemDiff()) {

    private var dataset = viewModel.readingList.value

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ReadingListViewHolder {
        val inflater = LayoutInflater.from(parent.context)
        val binding = ReadingListItemBinding.inflate(inflater, parent, false)
        return ReadingListViewHolder(binding) {
            viewModel.moveToTheNextList(it)
        }
    }

    fun changeData(newData: ReadingListViewModel) {
        viewModel = newData
        this.dataset = newData.readingList.value
        submitList(dataset)
    }


    override fun getItemCount(): Int = dataset?.size ?: 0

    override fun onBindViewHolder(holder: ReadingListViewHolder, position: Int) {
        holder.bind(this.dataset?.get(position))
    }


}

private class ReadingListItemDiff() : ItemCallback<GoodreadsBook>() {
    override fun areItemsTheSame(oldItem: GoodreadsBook, newItem: GoodreadsBook): Boolean {
        return oldItem.id == newItem.id
    }

    override fun areContentsTheSame(oldItem: GoodreadsBook, newItem: GoodreadsBook): Boolean {
        return oldItem.id == newItem.id
    }

}

ReadingListViewHolder:

class ReadingListViewHolder(
    private var binding: ReadingListItemBinding,
    private val moveBookToNextList: (pos: Int) -> Unit
) :
    RecyclerView.ViewHolder(binding.root) {

    private var animationEndId: Int = 0;

    init {
//        Add the move animation
        binding.readingListItemMotion.setTransitionListener(object :
            MotionLayout.TransitionListener {
            override fun onTransitionTrigger(p0: MotionLayout?, p1: Int, p2: Boolean, p3: Float) {
            }


            override fun onTransitionStarted(p0: MotionLayout?, p1: Int, p2: Int) {
//                Set the end ID
                animationEndId = p2
            }

            override fun onTransitionChange(p0: MotionLayout?, p1: Int, p2: Int, p3: Float) {
            }

            override fun onTransitionCompleted(p0: MotionLayout?, p1: Int) {

//                Check if it's end and not start
                if (p1 == animationEndId) {
                    moveBookToNextList(adapterPosition)
                }

            }
        })
    }

    fun bind(newData: GoodreadsBook?) {
        binding.book = newData;
        binding.executePendingBindings()
    }

}

fragment_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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_id="@+id/main_fragment__root_layout"
    android_layout_width="match_parent"
    android_layout_height="match_parent"
    android_orientation="vertical"
    tools_context=".ui.main.MainFragment">

    <com.google.android.material.tabs.TabLayout
        android_id="@+id/tab_layout"
        android_layout_width="match_parent"
        android_layout_height="wrap_content"
        android_background="@color/colorPrimaryAlt"
        app_layout_constraintStart_toStartOf="parent"
        app_layout_constraintTop_toBottomOf="@+id/toolbar"
        app_tabIndicatorColor="@color/colorAccent"
        app_tabTextColor="@color/design_default_color_background">

        <com.google.android.material.tabs.TabItem
            android_layout_width="wrap_content"
            android_layout_height="wrap_content"
            android_text="@string/tab_item_first" />

        <com.google.android.material.tabs.TabItem
            android_layout_width="wrap_content"
            android_layout_height="wrap_content"
            android_text="@string/tab_item_second" />

        <com.google.android.material.tabs.TabItem
            android_layout_width="wrap_content"
            android_layout_height="wrap_content"
            android_text="@string/tab_item_third" />
    </com.google.android.material.tabs.TabLayout>

    <androidx.viewpager2.widget.ViewPager2
        android_id="@+id/view_pager"
        android_layout_width="wrap_content"
        android_layout_height="wrap_content"
        android_layout_marginTop="48dp"
        app_layout_constraintEnd_toEndOf="parent"
        app_layout_constraintHorizontal_bias="0.0"
        app_layout_constraintStart_toStartOf="parent"
        app_layout_constraintTop_toBottomOf="@+id/tab_layout">

    </androidx.viewpager2.widget.ViewPager2>

</LinearLayout>

reading_list_fragment.xml

<LinearLayout 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_paddingTop="@dimen/search_result_padding"
    android_orientation="vertical"
    tools_context=".ui.main.readingList.ReadingListFragment">

    <androidx.recyclerview.widget.RecyclerView
        android_id="@+id/reading_list_recycler_view"
        android_layout_width="wrap_content"
        android_layout_height="wrap_content"
        app_layout_constraintStart_toStartOf="parent"
        app_layout_constraintTop_toTopOf="parent" />
</LinearLayout>

Comments

Comment posted by Shaishav

Does the size of list changes or, does the item size changes? Also, what happens when you set the

READ  [ANSWERED] android - Jetpack compose onClickLabel for Button and IconButton
Powered by Inline Related Posts

Comment posted by Adrian Pascu

The size of the list changes when moving an item from one list to another. For the recyclerview I tried match_parent,fill_parent,wrap_content. None work

Comment posted by Adrian Pascu

I tried to implement your changes. Wrap content for width produced a width of 0, but before I realized that was why there was nothing rendering on the screen, I looked over the xml layout of the activity that holds my fragment and made some changes there. Apparently, it was the fragment that had sizing problems, not the ViewPager or the recyclerview. Now it seems to be working. Thx anyways, you helped me spot the issue

Recent Posts

  • How can I play with my cat without toys?
  • What is a bag pipe band called?
  • Are Honda Civics actually fast?
  • Are Yankee candles toxic?
  • How do I pair my Michael Kors smartwatch with my Android?

Recent Comments

No comments to show.

Archives

  • January 2023
  • December 2022
  • November 2022
  • October 2022
  • September 2022

Categories

  • ¿Cómo
  • ¿Cuál
  • ¿Cuántas
  • ¿Cuánto
  • ¿Que
  • ¿Quién
  • 90” and 108” so you may have to round up to the nearest size.
  • and delete any Spotify folders from it. Once this is done
  • Android
  • Are
  • At
  • Bei
  • blink
  • C'est
  • Can
  • carbs
  • Comment
  • Did
  • Do
  • Does
  • During
  • For
  • Has
  • How
  • In
  • Is
  • Ist
  • Kann
  • Können
  • nouveau
  • On
  • or 108 inches.2020-08-03
  • Où
  • owning
  • Pourquoi
  • Puis-je
  • Quand
  • Quante
  • Quel
  • Quelle
  • Quelles
  • Quels
  • Qui
  • Should
  • Sind
  • Sollte
  • spiritual
  • tap the downward-facing arrow on the top left. A downward-facing arrow will appear underneath each song in the album; they'll turn green as the download completes.2020-07-28
  • Uncategorized
  • Wann
  • Warum
  • Was
  • Welche
  • Welcher
  • Welches
  • Welke
  • Wer
  • Were
  • What
  • What's
  • When
  • Where
  • Which
  • Who
  • Whose
  • Why
  • Wie
  • Will
  • Wo
  • Woher
  • you will receive two curtains each with the same measurements of width 66"" (168cm) x drop 54""(137cm).
  • you'll see a green downward-facing arrow next to each song.2021-02-26
©2023 Who Do Is | Powered by SuperbThemes & WordPress