evrencoskun / TableView

TableView is a powerful Android library for displaying complex data structures and rendering tabular data composed of rows, columns and cells.
MIT License
3.14k stars 454 forks source link

Invalidate header width cache when changing dataset #167

Open damianogiusti opened 5 years ago

damianogiusti commented 5 years ago

Hello, I noticed that calling the method setAllItems(List<CH>, List<RH>, List<List<C>>) on the adapter with different data will make the table view render the new columns keeping the size of the previous ones.

The initial dataset:

The dataset physically removing the first column from the list:

The cache which is kept inside the CellLayoutManager and the ColumnHeaderLayoutManager is not cleared when the dataset changes.

Is this the expected behavior?

evrencoskun commented 5 years ago

Is this the expected behavior?

Nope.

You need to the clear cached width size using removeCachedWidth function.

The bad news is you have to call this method in a loop considering your column count. Because there is no any function to clear all cached values.

private void clearCachedWidth(int columnPosition){
        // Firstly clear the column header cache map
        mTableView.getColumnHeaderLayoutManager().removeCachedWidth(columnPosition);
        // Clear each of cell items that is located on the column position
        mTableView.getCellLayoutManager().removeCachedWidth(columnPosition);
}

You just need to call the above function in a loop before showing new items on your TableView

damianogiusti commented 5 years ago

Thanks for the quick reply.

As I can see the CellLayoutManager does not have any method called removeCachedWidth. I'm currently using the 0.8.8 release. I added the method clearCache() into the CellLayoutManager and ColumnHeaderLayoutManager classes, which simply clear out the Map instance.

Now the column resizing works, but the column cell widths are not all the same. The cell is wrapping the width to its content and is not getting resized based on the largest one.

evrencoskun commented 5 years ago

Now the column resizing works, but the column cell widths are not all the same. The cell is wrapping the width to its content and is not getting resized based on the largest one.

Probably, you are clearing cache at a wrong time. clear cache after setAllItemscode line.

It should work as we expected. Because before showing a cell item, the TableView calculates the proper width value when it has no cached width value. you can check the measureChild function on [ColumnLayoutManager ]

(https://github.com/evrencoskun/TableView/blob/master/tableview/src/main/java/com/evrencoskun/tableview/layoutmanager/ColumnLayoutManager.java)

damianogiusti commented 5 years ago

Probably, you are clearing cache at a wrong time. clear cache after setAllItems code line.

I tried, but actually doesn't seem to have a different effect. The recycler view render process should not be synchronous so calling the clear cache before or after should not be relevant.

I'm trying to debug the problem starting from the measureChild method you mentioned. Could be maybe some instance member which is causing an unwanted side effect when re-setting all the table view items?

(Thanks for your support!)

damianogiusti commented 5 years ago

Basically requestLayout() called on the item view did not work for making the cell respect the WRAP_CONTENT layout param of width. In an ugly way, requesting the layout in an itemView.post(Runnable) in my app adapter implementation made the auto-size working.

In my opinion, the resizing mechanism should be applied not directly to the view in the onBindViewHolder as suggested by the readme, but using a listener on the recyclerview tree instead. I saw that there already is a TableViewLayoutChangeListener which it manages the layout of the recyclerview, and calls the requestLayout() method. (https://github.com/evrencoskun/TableView/blob/master/tableview/src/main/java/com/evrencoskun/tableview/listener/TableViewLayoutChangeListener.java). I noticed that the method is called only the first time the recyclerview is layouted. The subsequent times it does not pass this condition:

if (v.isShown() && (right - left) != (oldRight - oldLeft))

because the v (which actually is the RecyclerView) has the same dimensions it had at the previous invocation, but the children sizes are different.

alexeenkop commented 5 years ago

Hey, Guys!

When do you plan to release an update with fix "Invalidate cache when changing dataset"? or what you can propose for fix this in v0.8.8? Because ColumnLayoutManager don't have any method for clear CachedWidth.

damianogiusti commented 5 years ago

Hi @alexeenkop, the PR regarding the issue has already been merged (https://github.com/evrencoskun/TableView/pull/169). Now we're waiting for the repo owner to provide a new version.

alexeenkop commented 5 years ago

I understand. Thanks, @damianogiusti .

So, this question to repo owner @evrencoskun )

And for last release (0.8.8) we not have any solutions ?

evrencoskun commented 5 years ago

Hi fellas, @alexeenkop @damianogiusti

I'm gonna work on this library this weekend. So, the next release may be released at the weekend.

alexeenkop commented 5 years ago

Hi. @evrencoskun .

What about new release?) When is it planned?) Thanks.

sgallego commented 5 years ago

Based on @damianogiusti pull request and if you use Kotlin language, you can use extensions to do the same while waiting for the new release:

inline fun CellLayoutManager.clearCachedWidths() {
    try {
        javaClass.getDeclaredField("mCachedWidthList").let {
            it.isAccessible = true
            it.set(this, HashMap<Int, Map<Int, Int>>())
        }
    } catch (e: Exception) {
        e.printStackTrace()
    }
}

inline fun ColumnHeaderLayoutManager.clearCachedWidths() {
    try {
        javaClass.getDeclaredField("mCachedWidthList").let {
            it.isAccessible = true
            it.set(this, HashMap<Int, Int>())
        }
    } catch (e: Exception) {
        e.printStackTrace()
    }
}

An then call:

tableView.columnHeaderLayoutManager.clearCachedWidths()
tableView.cellLayoutManager.clearCachedWidths()

before setAllItems.