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

Consecutive showColumn/hideColumn crashes app #337

Closed pandalion98 closed 4 years ago

pandalion98 commented 4 years ago

I'm looking for the ability to show/hide multiple columns at the press of a button.

As an example, within the test app's RowHeaderLongPressPopup.java, I modified onMenuItemClick to read

            case SHOWHIDE_COLUMN:
                int column = 1;
                if (mTableView.isColumnVisible(column)) {
                    for (int a = 0; a < 4; a++) {
                        mTableView.hideColumn(a);
                    }
                } else {
                    for (int a = 0; a < 4; a++) {
                        mTableView.showColumn(a);
                    }
                }

So in theory, this should hide the first 5 columns. However, the entire app crashes instead.

Stack trace:

2020-09-01 22:57:45.851 29435-29435/com.evrencoskun.tableviewsample E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.evrencoskun.tableviewsample, PID: 29435
    java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
        at com.evrencoskun.tableviewsample.tableview.TableViewAdapter.onBindCellViewHolder(TableViewAdapter.java:130)
        at com.evrencoskun.tableviewsample.tableview.TableViewAdapter.onBindCellViewHolder(TableViewAdapter.java:47)
        at com.evrencoskun.tableview.adapter.recyclerview.CellRowRecyclerViewAdapter.onBindViewHolder(CellRowRecyclerViewAdapter.java:56)
        at com.evrencoskun.tableview.adapter.recyclerview.CellRowRecyclerViewAdapter.onBindViewHolder(CellRowRecyclerViewAdapter.java:34)
        at androidx.recyclerview.widget.RecyclerView$Adapter.onBindViewHolder(RecyclerView.java:7065)
        at androidx.recyclerview.widget.RecyclerView$Adapter.bindViewHolder(RecyclerView.java:7107)
        at androidx.recyclerview.widget.RecyclerView$Recycler.tryBindViewHolderByDeadline(RecyclerView.java:6012)
        at androidx.recyclerview.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline(RecyclerView.java:6279)
        at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:6118)
        at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:6114)
        at androidx.recyclerview.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:2303)
        at androidx.recyclerview.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1627)
        at androidx.recyclerview.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1587)
        at androidx.recyclerview.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:683)
        at androidx.recyclerview.widget.RecyclerView.dispatchLayoutStep1(RecyclerView.java:4085)
        at androidx.recyclerview.widget.RecyclerView.onMeasure(RecyclerView.java:3534)
        at android.view.View.measure(View.java:26414)
        at androidx.recyclerview.widget.RecyclerView$LayoutManager.measureChildWithMargins(RecyclerView.java:9384)
        at com.evrencoskun.tableview.layoutmanager.CellLayoutManager.measureChildWithMargins(CellLayoutManager.java:386)
        at androidx.recyclerview.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1653)
        at androidx.recyclerview.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1587)
        at androidx.recyclerview.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:665)
        at androidx.recyclerview.widget.RecyclerView.dispatchLayoutStep2(RecyclerView.java:4134)
        at androidx.recyclerview.widget.RecyclerView.onMeasure(RecyclerView.java:3540)
        at android.view.View.measure(View.java:26414)
pandalion98 commented 4 years ago

Upon further investigation, it seems that the crash is caused when hiding a column, and the column beside it has a different ViewType. This then causes onBindCellViewHolder to fail, depending on how the ViewHolder is implemented.

I think the main problem is that the column indexes/positions temporarily change when showing and hiding columns.

I did not expect that; what I expected was that the column simply disappeared from view without changing the table data.

Any proposed fixes?

pandalion98 commented 4 years ago

Related issues: #163 #172

pandalion98 commented 4 years ago

Resolved: I implemented the ViewType logic within the ViewHolder itself.

My adapter now always returns 0 for the cell ViewType

    override fun getCellItemViewType(position: Int): Int {
        return 0
    }

And I don't use multiple Cell ViewHolders anymore. My CellModel has a type argument

class CellModel(private val mId: String, val data: Any, var type: String) :
    ISortableModel { 
        ...
     }
}

So that when my adapter binds it

    override fun onBindCellViewHolder(h: AbstractViewHolder, m: CellModel?, col: Int, row: Int) {
        m ?: return // Make sure CellModel is not null, else do not bind.
        (h as CellViewHolder).setCellModel(m)
    }

The ViewModel just declares the needed view as View.VISIBLE:

    private fun setViews(model: CellModel) {
        when (model.type) {
            "text" -> {
                textView.visibility = View.VISIBLE
                textView.setText(model.data as String)
            }

            "checkbox" -> {
                checkBox.visibility = View.VISIBLE
                cb.isChecked = model.data as Boolean
            }
        }
    }

Unfortunately, what this means is that all the views I need (e.g. CheckBox and TextView) needs to be contained in one XML file, with android:visibility="gone"

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="wrap_content"
    android:layout_height="@dimen/_23sdp"
    android:gravity="center">

        <CheckBox
            android:id="@+id/cb"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="center"
            android:visibility="gone" />

        <TextView
            android:id="@+id/tv"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="center"
            android:textSize="@dimen/_8ssp"
            android:visibility="gone" />
</LinearLayout>