Titanium JIRA Archive
Titanium SDK/CLI (TIMOB)

[TIMOB-26862] Android: TextField/TextArea within a TableView can have performance issues with some keyboards

GitHub Issuen/a
TypeBug
PriorityMedium
StatusClosed
ResolutionFixed
Resolution Date2019-07-15T19:14:24.000+0000
Affected Version/sn/a
Fix Version/sRelease 8.1.0
ComponentsAndroid
LabelsTableView, TextArea, TextField, android, en, engSchedule
ReporterHans Knöchel
AssigneeJoshua Quick
Created2019-02-26T15:07:28.000+0000
Updated2019-07-15T19:14:24.000+0000

Description

*Summary:* Entering text into a TextField/TextArea embedded within a TableView can cause performance issues if the virtual keyboard shows/hides its top suggestion bar dynamically. It can also cause the cursor to suddenly move to the end of field and cause the field to flicker. *Reason:* By default, the "windowSoftInputMode" is set to Ti.UI.Android.SOFT_INPUT_ADJUST_PAN (aka: "adjustPan"), which means that showing/hiding the virtual keyboards will cause the window to resize. The resizing of a TableView triggers its row recycling/update behavior which can cause the "contents" of each row (such as the TextField) to be re-parented to a new row container view, which causes havoc with the virtual keyboard. For virtual keyboards that have a fixed height and do not pop-in/out a suggestion bar as you type, this isn't really an issue since the above mentioned resize behavior happens once. For example, this is not an issue on a Pixel phone. But some device pop-in/out a suggestion bar as you type which causes the TableView to resize every time you type in a character. This will cause massive performance issues. *Steps to reproduce:*

Build and run [^TableOfTextFieldsTest.js] on Android.

Tap on row 1's TextArea.

Notice that the cursor is moved to end of the field. (The code is supposed to highlight the whole text instead.)

Notice the below warnings get logged.

[WARN]  IInputConnectionWrapper: getTextBeforeCursor on inactive InputConnection
[WARN]  IInputConnectionWrapper: getSelectedText on inactive InputConnection
[WARN]  IInputConnectionWrapper: getTextAfterCursor on inactive InputConnection
*Work-Around 1:* Set up the window to "adjustPan". This works-around the issue because it causes the virtual keyboard to be overlaid on top of the window instead of resizing it, which avoids the issue.
var window = Ti.UI.createWindow({
	windowSoftInputMode: Ti.UI.Android ? Ti.UI.Android.SOFT_INPUT_ADJUST_PAN : null,
});
*Work-Around 2:* Use a ScrollView instead of a TableView.

Attachments

FileDateSize
TableOfTextFieldsTest.js2019-03-09T00:24:43.000+00001117
TableViewCustomRowTest.js2019-03-09T00:23:19.000+00001554
TableViewDefaultRowTest.js2019-03-09T00:23:19.000+00001326

Comments

  1. Srinivasan Pulipakkam 2019-02-26

    [~amukherjee] [~jquick] : can you check this.
  2. Joshua Quick 2019-03-05

    Calling the Java EditText.setText() method will cause these warnings because it will replace the internal Editable Java object. It is better to call the EditText.getText().replace() method instead, which re-uses the existing Editable object just like how the keyboard does it. This gets rid of the logged warnings and should be an optimization. I've also noticed that these warnings will be logged when showing/dismissing the virtual keyboard with a TextField and TextArea *+embedded+* within a Titanium TableView or ListView. The issue [~hknoechel] is seeing might be related to [TIMOB-18903]. On an Android phone (not a tablet), these warnings only happen when in portrait orientation too, not landscape since a fullscreen edit screen is displayed. (Embedding them within a ScrollView works fine; no warnings.)
  3. Joshua Quick 2019-03-06

    Regarding the warnings caused by showing/dismissing a TextField's virtual keyboard while it is embedded within a TableView, this is only an issue when the "windowSoftInputMode" is set to SOFT_INPUT_ADJUST_SIZE, which is the Android OS' default setting (ie: "adjustSize" in the "AndroidManifest.xml"). This issue won't happen if the window is set up to use "adjustPan" as shown below.
       var window = Ti.UI.createWindow({
       	windowSoftInputMode: Ti.UI.Android.SOFT_INPUT_ADJUST_PAN,
       });
       
    Titanium's Ti.UI.TableView and Ti.UI.ListView currently use Google's Java ListView class internally. From looking at Google's code, its onLayout() method will flag all of its children/rows as dirty and forcefully reload them. This is triggering the row view recycling behavior and I believe the text field is being quickly detached and re-attached to its row container view. Since "adjustSize" is the default setting on Android, the virtual keyboard will force a resize of the app's window and ListView, which will in turn call its onLayout() method. I believe this is what's causing the issue. This would also explain why switching to "adjustPan" (as shown above) works-around the issue. [AbsListView.onLayout()](https://github.com/aosp-mirror/platform_frameworks_base/blob/master/core/java/android/widget/AbsListView.java#L2188) [~hknoechel] has shown me a video of his issue. The reason why he's seeing a performance issue is because his device's virtual keyboard pops-up a suggestion bar when typing in the field, which increases the keyboard height. And since "adjustSize" is the default, this causing the ListView to shrink in size and trigger another relayout. The relayout resets the text field's cursor state to the end of the field, which removes the suggestion bar above the keyboard, causing the keyboard height to shrink... which again resizes the app window and ListView triggering another relayout. This explains the performance issues. I'm not able to reproduce the performance issue on a Google Pixel device's virtual keyboard since its top suggestion bar never appears/disappears, meaning the keyboard height doesn't fluctuate. I only see a the logged warnings once when showing the keyboard and again when dismissing the keyboard. I don't see the warnings while typing as [~hknoechel] is seeing since his keyboard suggestion bar is added/removed dynamically. I'm not sure how to solve this issue at the moment. But we do know how to work-around it by doing one of the following: * Use a ScrollView instead of a TableView/ListView when showing text fields * Set TI.UI.Window "windowSoftInputMode" to Ti.UI.Android.SOFT_INPUT_ADJUST_PAN.
  4. Joshua Quick 2019-03-06

    I've noticed that this TableView issue happens on Android 7.0 and above. When the native Java ListView.onLayout() method is called, it triggers the recycling behavior. This calls Titanium's Adapter.getView() method to see if the given row view container can be re-used or if a new row view container needs to be created. Our current adapter code will always create a new row container for Android 7.0 (aka: API Level 24) and above as can be seen in the link below. [TiTableView.java#L218](https://github.com/appcelerator/titanium_mobile/blob/master/android/modules/ui/src/java/ti/modules/titanium/ui/widget/tableview/TiTableView.java#L218) Quick code snippet...
       // TIMOB-24560: prevent duplicate TableViewRowProxyItem on Android N
       if (Build.VERSION.SDK_INT > 23) {
       	ArrayList<Item> models = viewModel.getViewModel();
       	if (models != null && v instanceof TiTableViewRowProxyItem && models.contains(v.getRowData())) {
       		v = null;
       		sameView = true;
       	}
       }
       
    If I comment out that code, the logged warnings go away... but only if the row was already above the virtual keyboard. The reason this works is that the row container is being re-used and the text field is not being detached and re-attached to another row container. Note that I say it only solves this issue if the row is above the virtual keyboard. If the keyboard overlaps the row, then the old row container is offscreen, which causes it to be recycled, and then a new row container needs to be set up anyways... which is the correct behavior in this case and there is no getting around the logged warnings. Now we can't simply comment out that code since it's needed for a reason. Android 7.0 is much more aggressive about recycling rows as can be seen in ticket [TIMOB-24560].
  5. Joshua Quick 2019-03-09

    PR (master): https://github.com/appcelerator/titanium_mobile/pull/10745
  6. Joshua Quick 2019-06-05

    PR (8.1.x): https://github.com/appcelerator/titanium_mobile/pull/10936
  7. Lokesh Choudhary 2019-07-12

    FR's Passed. Waiting for jenkins to merge.
  8. Christopher Williams 2019-07-15

    merged to 8_1_X (had already been merged to master)
  9. Lokesh Choudhary 2019-07-15

    Verified the fix in sdk 8.1.0.v20190715100642 & 8.2.0.v20190715083607. Closing.

JSON Source