titanium-sdk icon indicating copy to clipboard operation
titanium-sdk copied to clipboard

Android: Reassigning tableView data will lose click event

Open m1ga opened this issue 1 year ago • 4 comments

When reassigning tableView.data with the last data it will lose the click event.

const win = Ti.UI.createWindow();
const btn = Ti.UI.createButton({bottom: 0,title: "new data"});
const tableView = Ti.UI.createTableView({});
var tableData = [];
var t = 0;

function setData() {
	if (t > 0) {
		tableView.data = tableData;
		console.log("set data 2");
		return
	}

	t++;
	for (var i = 1; i <= 20; i++) {
		var entryRow = Ti.UI.createTableViewRow({height: 40});
		var entryNameLabel = Ti.UI.createLabel({text: "test"});
		entryRow.add(entryNameLabel);
		tableData.push(entryRow);
	}
	tableView.data = tableData;
	console.log("set data 1");
}


tableView.addEventListener("click", function(e) {
	console.log(e.index);
})
btn.addEventListener("click", function(e) {
	setData();
})

setData();
win.add([tableView, btn]);
win.open();

Steps to reproduce

  • run the code
  • click on a row
  • will output the index
  • click the "new data" button
  • no output when clicking the rows

Platform

Android

SDK version you are using

11.0.0 (and 11.1.0)

A quick log in the SDK at https://github.com/tidev/titanium_mobile/blob/81d3f4a1bfa69a5367b8cf05af40b608b62ebe3b/android/titanium/src/java/org/appcelerator/kroll/KrollProxy.java#L779-L789 showed that it is still calling the click event but it is not receiving it.

When I create new TableViewRows and set those as a data it will work. Just using the old array doesn't work (e.g. if you sort it or something else)

m1ga avatar Jul 16 '22 16:07 m1ga

Doing a lot of debug logging in https://github.com/tidev/titanium_mobile/blob/81d3f4a1bfa69a5367b8cf05af40b608b62ebe3b/android/titanium/src/java/org/appcelerator/kroll/KrollProxy.java#L870 and other places.

Looks like the connection to the TableViewSection is lost after assigning the array a second time. Both fire a click on the TableViewRow:

event received: Received: click Data: {"obscured":false,"size":0,"x":99,"searchMode":false,"y":30,"rowData":{"touchEnabled":true,"hiddenBehavior":4,"visible":true,"borderWidth":"0","soundEffectsEnabled":true,"horizontalWrap":true,"filterTouchesWhenObscured":false,"backgroundRepeat":false,"enabled":true,"height":40},"index":0,"force":0,"section":null,"source":null,"row":null,"bubbles":true}

do fire: Do fire event1: Ti.UI.TableViewRow | org.appcelerator.kroll.runtime.v8.V8Object@e9615a2
do fire: Do fire source: null data: {"obscured":false,"size":0,"x":99,"searchMode":false,"y":30,"rowData":{"touchEnabled":true,"hiddenBehavior":4,"visible":true,"borderWidth":"0","soundEffectsEnabled":true,"horizontalWrap":true,"filterTouchesWhenObscured":false,"backgroundRepeat":false,"enabled":true,"height":40},"index":0,"force":0,"section":null,"row":null}
krollthread sync: fire sync event 1: click data {searchMode=false, index=0, section=[object TableViewSectionProxy], source=[object Object], type=click, cancelBubble=false, obscured=false, size=0, x=99, y=30, rowData={touchEnabled=true, hiddenBehavior=4, visible=true, borderWidth=0, soundEffectsEnabled=true, horizontalWrap=true, filterTouchesWhenObscured=false, backgroundRepeat=false, enabled=true, height=40}, force=0, row=[object Object], bubbles=true}

but only in the good run I receive doFireEvent two more times (TableViewSection, TableView):

do fire: Do fire event1: Ti.UI.TableViewSection | org.appcelerator.kroll.runtime.v8.V8Object@dcac233
do fire: Do fire source: org.appcelerator.kroll.runtime.v8.V8Object@e9615a2 data: {"searchMode":false,"index":0,"section":null,"type":"click","cancelBubble":false,"obscured":false,"size":0,"x":99,"y":30,"rowData":{"touchEnabled":true,"hiddenBehavior":4,"visible":true,"borderWidth":"0","soundEffectsEnabled":true,"horizontalWrap":true,"filterTouchesWhenObscured":false,"backgroundRepeat":false,"enabled":true,"height":40},"force":0,"row":null}
krollthread sync: fire sync event 1: click data {searchMode=false, index=0, section=[object TableViewSectionProxy], source=[object Object], type=click, cancelBubble=false, obscured=false, size=0, x=99, y=30, rowData={touchEnabled=true, hiddenBehavior=4, visible=true, borderWidth=0, soundEffectsEnabled=true, horizontalWrap=true, filterTouchesWhenObscured=false, backgroundRepeat=false, enabled=true, height=40}, force=0, row=[object Object], bubbles=true}

do fire: Do fire event1: Ti.UI.TableView | org.appcelerator.kroll.runtime.v8.V8Object@cf269f0
do fire: Do fire source: org.appcelerator.kroll.runtime.v8.V8Object@e9615a2 data: {"searchMode":false,"index":0,"section":null,"type":"click","cancelBubble":false,"obscured":false,"size":0,"x":99,"y":30,"rowData":{"touchEnabled":true,"hiddenBehavior":4,"visible":true,"borderWidth":"0","soundEffectsEnabled":true,"horizontalWrap":true,"filterTouchesWhenObscured":false,"backgroundRepeat":false,"enabled":true,"height":40},"force":0,"row":null}
krollthread sync: fire sync event 1: click data {searchMode=false, index=0, section=[object TableViewSectionProxy], source=[object Object], type=click, cancelBubble=false, obscured=false, size=0, x=99, y=30, rowData={touchEnabled=true, hiddenBehavior=4, visible=true, borderWidth=0, soundEffectsEnabled=true, horizontalWrap=true, filterTouchesWhenObscured=false, backgroundRepeat=false, enabled=true, height=40}, force=0, row=[object Object], bubbles=true}

so lets find out why the connection gets corrupted

m1ga avatar Jul 17 '22 20:07 m1ga

The following code exhibits the original issue. The listener events on the tableView are not caught by the handler but they are happening because the row tapped on reacts to the tap. This tableView has a searchBar and filter. The issue occurs with and without the sort function before the assignment to tableView.data.

var win = Ti.UI.createWindow();

function randomInt(max) {
    return Math.floor(Math.random() * max) + 1;
}
var defaultFontSize = Ti.Platform.name === 'android' ? 16 : 14;
var tableData = [];
var search = Titanium.UI.createSearchBar({
    barColor: '#000',
    showCancel: true,
    height: 43,
    top: 0,
});
tableData = [];
for (var i = 1; i <= 20; i++) {
    var entryRow = Ti.UI.createTableViewRow({
        className: 'entryRowClass',
        height: Ti.UI.SIZE,
        layout: 'vertical',
        filter: "title",
    });
    var entryNameLabel = Ti.UI.createLabel({
        text: randomInt(20),
        top: 0,
        left: 4 + '%',
        right: 5 + 'sp',
        height: Ti.UI.SIZE,
        maxLines: 1,
        ellipsize: Ti.UI.TEXT_ELLIPSIZE_TRUNCATE_END,
        width: Ti.UI.SIZE,
        touchEnabled: false,
    });
    entryRow.add(entryNameLabel);
    var subLine = Ti.UI.createLabel({
        text: "subtitle",
        left: 6 + '%',
        right: 5 + 'sp',
        height: Ti.UI.SIZE,
        width: Ti.UI.SIZE,
        touchEnabled: false,
    });
    entryRow.add(subLine);
    tableData.push(entryRow);
}
console.log("set data");
var tableView = Ti.UI.createTableView({
    top: 0,
    data: tableData,
    search: search,
    filterAttribute: "filter"
});
//tableData.sort(function(a, b) {
//    if (randomInt(20) > 10) return a; else return b;
//});
tableView.data = tableData;
tableView.addEventListener("click", function(e) {
    console.log(e.index);
});
win.add(tableView);
win.open();

jfalcone avatar Jul 19 '22 22:07 jfalcone

When using the 11.1.0.v20220720093658 SDK built by m1ga and changing our code such that the data property is not assigned in the createTableView call, we then flip between the TableView presenting rows in Alpha order and the rows in Dist order and then go to detail views and map views and ultimately things break - it appears that the properties of the rows get lost (clickTime is a property we add to each row). I will attempt to create a more complex example code that reproduces it. But the bottom line is that the fix in the aforementioned SDK does not work.

[ERROR] TiExceptionHandler: (main) [8288,197273] /uiUtillib.js:1003 [ERROR] TiExceptionHandler: e.source.clickTime = currentTime; [ERROR] TiExceptionHandler: ^ [ERROR] TiExceptionHandler: Error: Attempt to invoke virtual method 'void ti.modules.titanium.ui.TableViewRowProxy.onPropertyChanged(java.lang.String, java.lang.Object)' on a null object reference [ERROR] TiExceptionHandler: at TableView. (/uiUtillib.js:1003:26) [ERROR] TiExceptionHandler: at TableView.value (ti:/kroll.js:1604:27) [ERROR] TiExceptionHandler: at TableView.value (ti:/kroll.js:1656:25) [ERROR] TiExceptionHandler: at TableViewSection.value (ti:/kroll.js:1647:27) [ERROR] TiExceptionHandler: at TableViewRow.value (ti:/kroll.js:1647:27) [ERROR] TiExceptionHandler: [ERROR] TiExceptionHandler: ti.modules.titanium.ui.TableViewRowProxy.onPropertyChanged(TableViewRowProxy.java:383) [ERROR] TiExceptionHandler: org.appcelerator.kroll.runtime.v8.V8Object.nativeFireEvent(Native Method) [ERROR] TiExceptionHandler: org.appcelerator.kroll.runtime.v8.V8Object.fireEvent(V8Object.java:63) [ERROR] TiExceptionHandler: org.appcelerator.kroll.KrollProxy.doFireEvent(KrollProxy.java:985) [ERROR] TiExceptionHandler: org.appcelerator.kroll.KrollProxy.fireSyncEvent(KrollProxy.java:819) [ERROR] TiExceptionHandler: org.appcelerator.titanium.proxy.TiViewProxy.fireSyncEvent(TiViewProxy.java:1037) [ERROR] TiExceptionHandler: org.appcelerator.titanium.proxy.TiViewProxy.fireSyncEvent(TiViewProxy.java:1043) [ERROR] TiExceptionHandler: org.appcelerator.kroll.KrollProxy.fireSyncEventToParent(KrollProxy.java:863) [ERROR] TiExceptionHandler: org.appcelerator.kroll.runtime.v8.V8Object.nativeFireEvent(Native Method) [ERROR] TiExceptionHandler: org.appcelerator.kroll.runtime.v8.V8Object.fireEvent(V8Object.java:63) [ERROR] V8Exception: Exception occurred at /uiUtillib.js:1003: Uncaught Error: Attempt to invoke virtual method 'void ti.modules.titanium.ui.TableViewRowProxy.onPropertyChanged(java.lang.String, java.lang.Object)' on a null object reference

jfalcone avatar Jul 20 '22 18:07 jfalcone

Just want to clarify that the fix suggested by m1ga does not work for us (causes a red screen instead of just ignoring the clicks) and in fact may be more unstable for other users of tableView. We have a stable workaround of setting an eventListener on every tableViewRow that works with 11.0.0. I'm not sure what we should be doing now. We've done a lot of testing with 11.0.0 as well as with 2 builds by miga and the fact is that none of them work for us. The builds by m1ga seemed to work for trivial test apps but when used with our real app, they gave a red screen and you can see TableView all over the stack trace above so this is a TableView issue. Obviously, with 11.0.0, something in the tableView is corrupted when we do our tableView.data = tableViewRows assignment as the listener fails to work after that. With the m1ga sdk builds, it just takes a bit longer, but the tableView becomes corrupted and we lose tableViewRow properties fairly quickly (I have been able to get to the red screen in 2 clicks). We will continue to work on reproducing the bug with an app that we can post here.

jfalcone avatar Jul 21 '22 22:07 jfalcone