[TIMOB-26862] Android: TextField/TextArea within a TableView can have performance issues with some keyboards
GitHub Issue | n/a |
---|---|
Type | Bug |
Priority | Medium |
Status | Closed |
Resolution | Fixed |
Resolution Date | 2019-07-15T19:14:24.000+0000 |
Affected Version/s | n/a |
Fix Version/s | Release 8.1.0 |
Components | Android |
Labels | TableView, TextArea, TextField, android, en, engSchedule |
Reporter | Hans Knöchel |
Assignee | Joshua Quick |
Created | 2019-02-26T15:07:28.000+0000 |
Updated | 2019-07-15T19:14:24.000+0000 |
Description
*Summary:*
Entering text into a Tap on row 1's
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
File | Date | Size |
---|---|---|
TableOfTextFieldsTest.js | 2019-03-09T00:24:43.000+0000 | 1117 |
TableViewCustomRowTest.js | 2019-03-09T00:23:19.000+0000 | 1554 |
TableViewDefaultRowTest.js | 2019-03-09T00:23:19.000+0000 | 1326 |
[~amukherjee] [~jquick] : can you check this.
Calling the Java
EditText.setText()
method will cause these warnings because it will replace the internalEditable
Java object. It is better to call theEditText.getText().replace()
method instead, which re-uses the existingEditable
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 aTextField
andTextArea
*+embedded+* within a TitaniumTableView
orListView
. 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 aScrollView
works fine; no warnings.)Regarding the warnings caused by showing/dismissing a
TextField
's virtual keyboard while it is embedded within aTableView
, this is only an issue when the "windowSoftInputMode" is set toSOFT_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.Titanium's
Ti.UI.TableView
andTi.UI.ListView
currently use Google's JavaListView
class internally. From looking at Google's code, itsonLayout()
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 andListView
, which will in turn call itsonLayout()
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 theListView
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 andListView
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 aScrollView
instead of aTableView
/ListView
when showing text fields * SetTI.UI.Window
"windowSoftInputMode" toTi.UI.Android.SOFT_INPUT_ADJUST_PAN
.I've noticed that this
TableView
issue happens on Android 7.0 and above. When the native JavaListView.onLayout()
method is called, it triggers the recycling behavior. This calls Titanium'sAdapter.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...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].
PR (master): https://github.com/appcelerator/titanium_mobile/pull/10745
PR (8.1.x): https://github.com/appcelerator/titanium_mobile/pull/10936
FR's Passed. Waiting for jenkins to merge.
merged to 8_1_X (had already been merged to master)
Verified the fix in sdk 8.1.0.v20190715100642 & 8.2.0.v20190715083607. Closing.