google / flexbox-layout

Flexbox for Android
Apache License 2.0
18.25k stars 1.79k forks source link

Allow dual scrolling by setting sizes explicitly #596

Open Danielku15 opened 2 years ago

Danielku15 commented 2 years ago

Issues and steps to reproduce

I would like to use the FlexboxLayoutManager within a RecyclerView to achieve a dual scrolling like in a WebView. My goal is to a have a left to right layouting with wrapping, but each row can potentially exceed the viewport.

image

According to the Source Code the layout manager can handle both kinds of scrolling but the layouting and scrolling behavior is heavily dependent on the size of the RecyclerView. The size of the RecyclerView is constraint to its usage in the app which is correct. But unfortunately the FlexboxLayoutManager does all the layouting based on the actual size of the RecyclerView. I am using FlexDirection.Row and FlexWrap.Wrap to get the left-to-right layouting with wrapping at the "end".

But unfortunately the wrapping always happens at the width of the RecyclerView (black) and not at a custom virtual width (red). Is there some hidden way to achieve what I want to achieve (e.g. through overriding some methods).

Version of the flexbox library

3.0.0

Link to code

I guess you can just take the demo-cat-gallery app from this repository. There try to get a dual scrolling which allows scrolling horizontally 2x the viewport and the remaining vertical depending on the number of cats.

Danielku15 commented 2 years ago

As an update: I built the following custom layout manager which allows me to inject a custom width into the FlexboxLayoutManager. With this adjustment it is possible to have dual scrolling as described. At least this proofs that the core functionality would be available, but: it breaks the virtualization/recycling of items on horizontal scrolling. If I have only 1 line with 100 items, the RecyclerView creates 100 ViewHolders and binds them all instead of only holding the items in the visual viewport.

I guess this comes from the fact that I am actually telling the RecyclerView that the full width of the layout is spanning the 100 items. I fear without changes in the library this cannot be fixed. The FlexboxLayoutManager would need to do the layouting and scrolling logic based on a virtual width, while keeping the actual with actual width.

class MyLayoutManager : FlexboxLayoutManager {
    private var _width: Int = 0

    constructor(context: Context?) :
        super(context, FlexDirection.ROW, FlexWrap.WRAP) {
        justifyContent = JustifyContent.FLEX_START
        alignItems = AlignItems.FLEX_START
    }

    override fun canScrollHorizontally(): Boolean {
        return true
    }

    override fun canScrollVertically(): Boolean {
        return true
    }

    override fun getWidth(): Int {
        return Math.max(_width, super.getWidth())
    }

    fun setWidth(value: Int) {
        _width = value
        requestLayout()
    }
}

My use case is the follwing: I am having a music notation engraver which takes care of the precise layouting of the measures (green boxes) according to some custom configurations. As the rendered green boxes are in-memory bitmaps this is very costly in terms of memory. To solve this, I encode each frame into PNGs (for now still in-memory) and decode them dynamically again when shown on screen.

My next level optimization would be to have a 1st level and 2nd level cache to keep items close to the viewport in-memory and the ones far away even on disk (some cache directory).