Titanium JIRA Archive
Titanium SDK/CLI (TIMOB)

[TIMOB-15092] Android: Multiple PickerViews inside a TableView shuffling options / disappearing

GitHub Issuen/a
TypeBug
PriorityLow
StatusOpen
ResolutionUnresolved
Affected Version/sRelease 3.1.2
Fix Version/sn/a
ComponentsAndroid
Labelsandroid, classname, picker, pickers, tableView, tableviewrow
ReporterYgor Lemos
AssigneeUnknown
Created2013-09-05T22:23:30.000+0000
Updated2018-02-28T20:03:15.000+0000

Description

Problem Description

When you add multiple Pickers inside a TableView (one picker per row), they get very buggy after scrolling the screen. What I have noticed is that some previously selected pickerView options just disappear, or the options are changed randomly. In the testcase I'm submitting I'm using the same options for different pickerviews, but if the options are different the problem get even worse. I have also found out that If I set a className to each different pickerView ROW the problem vanishes. Problem is that it seems that there is a limit of 32 classNames, past that and the app crashes (which is a big problem as I'm building dynamic forms and some forms reach 50+ questions). Also, by setting a different className to each row, you lose the tableView optimization.

Testcase


var w = Ti.UI.createWindow({
    backgroundColor: "#dddcdc",
    navBarHidden: true,
    fullscreen: false,
    orientationModes: [Ti.UI.PORTRAIT],
    windowSoftInputMode: Ti.UI.Android.SOFT_INPUT_ADJUST_PAN,
});


/*
 *  QUESTIONS
 */


var rows = [],
    fields = [],
    multiOpts = {};

for (var i = 0; i <= 50; i++) {

    var r = Ti.UI.createTableViewRow({
        backgroundColor: "#dedede",
        height: 153,
        width: Ti.UI.FILL,
        className: "rowQuestion"
    });

    var qNum = Ti.UI.createLabel({
        text: i + 1,
        top: 15,
        left: -5,
        width: "10%",
        height: 31,
        color: "#fff",
        textAlign: "center",
        backgroundColor: "#82b52a",
        borderRadius: 15,
        touchEnabled: false,
        font: {
            fontSize: 14,
            fontFamily: "Roboto-Regular"
        }
    });

    var qTitle = Ti.UI.createLabel({
        text: "This is a question title asking something for the user...",
        top: 0,
        left: "10%",
        width: "90%",
        height: 70,
        color: "#333",
        verticalAlign: Ti.UI.TEXT_VERTICAL_ALIGNMENT_TOP,
        font: {
            fontSize: 14,
            fontFamily: "Roboto-Regular"
        }
    });

    // Field
    fields[i] = Ti.UI.createPicker({
        width: "80%",
        height: 60,
        top: 85,
        font: {
            fontSize: 14,
            fontWeight: "bold"
        },
        color: "#888"
    });

    
    fields[i].add([
    	Ti.UI.createPickerRow({title: ""}),
    	Ti.UI.createPickerRow({title: "Option Test Option 1"}),
    	Ti.UI.createPickerRow({title: "Option Test Option 2"}),
    	Ti.UI.createPickerRow({title: "Option Test Option 3"}),
    	Ti.UI.createPickerRow({title: "Option Test Option 4"}),
    	Ti.UI.createPickerRow({title: "Option Test Option 5"}),
    	Ti.UI.createPickerRow({title: "Option Test Option 6"}),
    	Ti.UI.createPickerRow({title: "Option Test Option 7"}),
    	Ti.UI.createPickerRow({title: "Option Test Option 8"}),
    	Ti.UI.createPickerRow({title: "Option Test Option 9"}),
    	Ti.UI.createPickerRow({title: "Option Test Option 10"}),
    	Ti.UI.createPickerRow({title: "Option Test Option 11"}),
    	Ti.UI.createPickerRow({title: "Option Test Option 12"}),
    	Ti.UI.createPickerRow({title: "Option Test Option 13"}),
    	Ti.UI.createPickerRow({title: "Option Test Option 14"}),
    	Ti.UI.createPickerRow({title: "Option Test Option 15"}),
    	Ti.UI.createPickerRow({title: "Option Test Option 16"}),
    	Ti.UI.createPickerRow({title: "Option Test Option 17"}),
    	Ti.UI.createPickerRow({title: "Option Test Option 18"}),
    	Ti.UI.createPickerRow({title: "Option Test Option 19"}),
    	Ti.UI.createPickerRow({title: "Option Test Option 20"}),
    	Ti.UI.createPickerRow({title: "Option Test Option 21"}),
    	Ti.UI.createPickerRow({title: "Option Test Option 22"}),
    	Ti.UI.createPickerRow({title: "Option Test Option 23"}),
    	Ti.UI.createPickerRow({title: "Option Test Option 24"}),
    	Ti.UI.createPickerRow({title: "Option Test Option 25"}),
    	Ti.UI.createPickerRow({title: "Option Test Option 26"}),
    	Ti.UI.createPickerRow({title: "Option Test Option 27"}),
    	Ti.UI.createPickerRow({title: "Option Test Option 28"}),
    	Ti.UI.createPickerRow({title: "Option Test Option 29"}),
    	Ti.UI.createPickerRow({title: "Option Test Option 30"}),
    ]);

    r.add(qNum);
    r.add(qTitle);
    r.add(fields[i]);

    rows.push(r);

}



var t = Ti.UI.createTableView({
	separatorColor: "#a6a6a6",
	data: rows
});

w.add(t);

w.open();

Instructions to reproduce

1. Create a new mobile Project (Classic Titanium) 2. Paste the code into app.js 3. Run it in a device 4. In question 0, put answer 0, in question 1, answer 1 and so on. 5. Scroll down and up. 6. You will see your answers changed.

Extra info

In the logs I can see this:
09-09 13:28:05.914: W/InputMethodManagerService(523): Window already focused, ignoring focus gain of: com.android.internal.view.IInputMethodClient$Stub$Proxy@429b75c8 attribute=null, token = android.os.BinderProxy@42a76558
when scrolling the tableview.

Comments

  1. Ingo Muschenetz 2013-09-06

    Ygor, could you update with a few items?

    Can you add a screenshot?

    Do you have an example use case? We're not arguing that it's invalid, we're just curious how you are using this.

    There was a suggestion to use a ScrollView or something besides a TableView. Have you tried that?

  2. Ygor Lemos 2013-09-06

    Hi Ingo, 1 - Sure, I'll make a quick video so you guys can see the problem happening as its nature is dynamic and can't really be expressed on a screenshot. 2 - Yes. I already have an app published on the App Store with over 20k users here in Brazil. Basically this is part of my dynamic form generator, where missions which are basically mobile forms with several kinds of fields are rendered on the device from a JSON specification that comes from the network. The app is similar to Gigwalk / Field Agent / iPoll and others, the name of the app is PiniOn. 3 - Yes, I saw that and I've tried it, but the rows structure of a table view seriously optimize the way we assembly those dynamic forms as the field types and amount of fields change severely from one mission to another... Imagine all kinds of forms being rendered natively on mobile devices. (The same codebase works both on iOS and Android). I'll send a video showing the current behaviour ASAP. Also, I've sent the rows with lots of formatting, feel free to remove them from the testcase, the error reproduces even with bare rows.
  3. Ingo Muschenetz 2013-09-06

    Is there a way you could change to use a ListView? ListView, in general, is the preferred approach over TableView.
  4. Ygor Lemos 2013-09-06

    Moving to a ListView is on our Roadmap but it is way in the future since it requires a lot of rewriting. Since the tableViews are quite good for our current needs right now, we haven't prioritized moving the form rendered to a ListView, we have tested the dynamic forms with 100+ fields of all kinds (text, boolean, numeric, pickers, multi-select, photo capture with thumbnails, QR code scanning, etc...), all mixed inside the same form and the performance is quite good, even on slow entry-level android devices. On iOS the performance is even greater. I'd rather keep the tableViews for a couple more months since we are near our public Android release (we are Beta testing on the Play Store for a week now).
  5. Ygor Lemos 2013-09-06

    This is the video showing the current TC behavior on a Nexus 7. The same repeats on every device we tested. http://www.youtube.com/watch?v=8ap8uaPOoxE
  6. Ygor Lemos 2013-09-09

    If you apply a different className to each row containing a Picker, the problem vanishes. The problem (apart the obvious lost of any kind of optimization by cell reusing) is that it seems there is some kind of limit on how many classNames you can use... when you reach around 32 classnames, you get an error like this:
       java.lang.ArrayIndexOutOfBoundsException: length=32; index=32
       at android.widget.AbsListView$RecycleBin.addScrapView(AbsListView.java:7009)
       at android.widget.AbsListView.trackMotionScroll(AbsListView.java:5532)
       at android.widget.AbsListView.scrollIfNeeded(AbsListView.java:3404)
       at android.widget.AbsListView.onTouchEvent(AbsListView.java:3748)
       at android.view.View.dispatchTouchEvent(View.java:7146)
       at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2238)
       at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1935)
       at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2244)
       at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1949)
       at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2244)
       at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1949)
       at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2244)
       at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1949)
       at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2244)
       at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1949)
       at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2244)
       at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1949)
       at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2244)
       at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1949)
       at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchTouchEvent(PhoneWindow.java:1938)
       at com.android.internal.policy.impl.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1392)
       at android.app.Activity.dispatchTouchEvent(Activity.java:2408)
       at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.java:1886)
       at android.view.View.dispatchPointerEvent(View.java:7332)
       at android.view.ViewRootImpl.deliverPointerEvent(ViewRootImpl.java:3520)
       at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:3454)
       at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:4543)
       at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:4522)
       at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:4618)
       at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:171)
       at android.view.InputEventReceiver.nativeConsumeBatchedInputEvents(Native Method)
       at android.view.InputEventReceiver.consumeBatchedInputEvents(InputEventReceiver.java:163)
       at android.view.ViewRootImpl.doConsumeBatchedInput(ViewRootImpl.java:4597)
       at android.view.ViewRootImpl$ConsumeBatchedInputRunnable.run(ViewRootImpl.java:4637)
       at android.view.Choreographer$CallbackRecord.run(Choreographer.java:747)
       at android.view.Choreographer.doCallbacks(Choreographer.java:567)
       at android.view.Choreographer.doFrame(Choreographer.java:534)
       at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:733)
       at android.os.Handler.handleCallback(Handler.java:615)
       at android.os.Handler.dispatchMessage(Handler.java:92)
       at android.os.Looper.loop(Looper.java:153)
       at android.app.ActivityThread.main(ActivityThread.java:4987)
       at java.lang.reflect.Method.invokeNative(Native Method)
       at java.lang.reflect.Method.invoke(Method.java:511)
       at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:821)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:584)
       at dalvik.system.NativeStart.main(Native Method)
       
  7. Ygor Lemos 2013-09-09

    Can anybody give me a light on how to get a PickerView inside a ListItem on a ListView? I'm trying to migrate this TC to a ListView to see if this problem also happens within ListViews, but I'm stuck on how to add the PickerRows to the Pickers since I have no reference to their instance thus preventing me to access .add() on the PickerViews. I have created the templates (for the same rows of the TC), please point me out on how to add the Rows and I will finish this TC using ListViews:
       
       var w = Ti.UI.createWindow({
           backgroundColor: "#dddcdc",
           navBarHidden: true,
           fullscreen: false,
           orientationModes: [Ti.UI.PORTRAIT],
           windowSoftInputMode: Ti.UI.Android.SOFT_INPUT_ADJUST_PAN,
       });
       
       
       
       var myTemplate = {
           childTemplates: [
               {                           
                   type: 'Ti.UI.Label',
                   bindId: 'qNum',
                   properties: {
                       top: 15,
       		        left: -5,
       		        width: "10%",
       		        height: 31,
       		        color: "#fff",
       		        textAlign: "center",
       		        backgroundColor: "#82b52a",
       		        borderRadius: 15,
       		        touchEnabled: false,
       		        font: {
       		            fontSize: 14,
       		            fontFamily: "Roboto-Regular"
       		        }
                   }
               },
               {                            
                   type: 'Ti.UI.Label',
                   bindId: 'qTitle',
                   properties: {
                       top: 0,
       		        left: "10%",
       		        width: "90%",
       		        height: 70,
       		        color: "#333",
       		        verticalAlign: Ti.UI.TEXT_VERTICAL_ALIGNMENT_TOP,
       		        font: {
       		            fontSize: 14,
       		            fontFamily: "Roboto-Regular"
               		}
                   }
               },
               {
                   type: 'Ti.UI.Picker',
                   bindId: 'qPicker',
                   properties: {
                       width: "80%",
       		        height: 60,
       		        top: 85,
       		        font: {
       		            fontSize: 14,
       		            fontWeight: "bold"
       		        },
       		        color: "#888"
                   }
               }
           ]
       };
       
       
       var sections = [];
       
       var questions = Ti.UI.createListSection({ headerTitle: 'Questions'});
       var questionsData = [];
       
       
       /*
        *  QUESTIONS
        */
       
       
       var rows = [],
           fields = [],
           multiOpts = {};
       
       for (var i = 0; i <= 50; i++) {
       
           questionsData.push({
       		qNum: {text: i+1},
       		qTitle: {text: "This is a whatever question " + i}
           });
           
           /*
            * 
            * HOW DO I ADD THE PickerRows (options) TO THE PICKER qPicker here ????
            * 
            * 
            */
       
       }
       
       var listView = Ti.UI.createListView({
           templates: { 'template': myTemplate },
           defaultItemTemplate: 'template'
       });
       
       questions.setItems(questionsData);
       sections.push(questions);
       
       listView.setSections(sections);
       
       
       w.add(listView);
       
       w.open();
       
       
       
    Thanks.
  8. Ygor Lemos 2013-09-24

    Any news on this one? This still prevents devs from using Pickers inside tableViews as of 3.1.3.GA...

JSON Source