Skip to content

Commit

Permalink
feat(android, ios): continuous update for ListView scrolling event (#…
Browse files Browse the repository at this point in the history
…13095)

* feat(android): continuous update for ListView scrolling event

* fix linting

* fix linting

* fix readme

* add test

* add test

* add test

* remove no-unused-vars

* still fixing linter

* feat: add ios

* update test

* remove file

* update version in readme

* update version in readme
  • Loading branch information
m1ga committed Aug 18, 2022
1 parent 24c9faf commit 74b00ce
Show file tree
Hide file tree
Showing 6 changed files with 142 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import org.appcelerator.titanium.TiDimension;
import org.appcelerator.titanium.proxy.TiViewProxy;
import org.appcelerator.titanium.view.TiUIView;
import org.appcelerator.titanium.util.TiConvert;

import java.util.ArrayList;
import java.util.HashMap;
Expand All @@ -40,6 +41,7 @@
propertyAccessors = {
TiC.PROPERTY_CAN_SCROLL,
TiC.PROPERTY_CASE_INSENSITIVE_SEARCH,
TiC.PROPERTY_CONTINUOUS_UPDATE,
TiC.PROPERTY_DEFAULT_ITEM_TEMPLATE,
TiC.PROPERTY_EDITING,
TiC.PROPERTY_FAST_SCROLL,
Expand Down Expand Up @@ -553,9 +555,13 @@ private void processProperty(String name, Object value)
}

} else if (name.equals(TiC.PROPERTY_SHOW_SELECTION_CHECK)) {

// Update and refresh list.
update(true);
} else if (name.equals(TiC.PROPERTY_CONTINUOUS_UPDATE)) {
final TiListView listView = getListView();
if (listView != null) {
listView.setContinousUpdate(TiConvert.toBoolean(value, false));
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import org.appcelerator.titanium.TiApplication;
import org.appcelerator.titanium.TiC;
import org.appcelerator.titanium.proxy.TiViewProxy;
import org.appcelerator.titanium.util.TiConvert;
import org.appcelerator.titanium.util.TiUIHelper;

import android.app.Activity;
Expand All @@ -24,7 +25,6 @@
import android.os.Parcelable;
import android.view.MotionEvent;
import android.view.View;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.selection.ItemDetailsLookup;
Expand Down Expand Up @@ -55,9 +55,12 @@ public class TiListView extends TiSwipeRefreshLayout implements OnSearchChangeLi
private boolean hasLaidOutChildren = false;
private SelectionTracker tracker = null;
private boolean isScrolling = false;
private boolean continuousUpdate = false;
private int lastScrollDeltaY;
private int scrollOffsetX = 0;
private int scrollOffsetY = 0;
private int lastVisibleItem = -1;
private int lastVisibleSection = -1;
private String filterQuery;

public TiListView(ListViewProxy proxy)
Expand Down Expand Up @@ -141,7 +144,7 @@ public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy)

// Only fire `scrolling` event upon direction change.
if (proxy.hierarchyHasListener(TiC.EVENT_SCROLLING)
&& (lastScrollDeltaY >= 0 && dy <= 0 || lastScrollDeltaY <= 0 && dy >= 0)) {
&& ((lastScrollDeltaY >= 0 && dy <= 0 || lastScrollDeltaY <= 0 && dy >= 0) || continuousUpdate)) {
final KrollDict payload = generateScrollPayload();

// Determine scroll direction.
Expand All @@ -151,7 +154,17 @@ public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy)
payload.put(TiC.PROPERTY_DIRECTION, "down");
}
payload.put(TiC.EVENT_PROPERTY_VELOCITY, 0);
proxy.fireSyncEvent(TiC.EVENT_SCROLLING, payload);
if (continuousUpdate) {
if (lastVisibleItem != TiConvert.toInt(payload.get(TiC.PROPERTY_FIRST_VISIBLE_ITEM_INDEX))
|| lastVisibleSection
!= TiConvert.toInt(payload.get(TiC.PROPERTY_FIRST_VISIBLE_SECTION_INDEX))) {
proxy.fireSyncEvent(TiC.EVENT_SCROLLING, payload);
lastVisibleItem = TiConvert.toInt(payload.get(TiC.PROPERTY_FIRST_VISIBLE_ITEM_INDEX));
lastVisibleSection = TiConvert.toInt(payload.get(TiC.PROPERTY_FIRST_VISIBLE_SECTION_INDEX));
}
} else {
proxy.fireSyncEvent(TiC.EVENT_SCROLLING, payload);
}
}

lastScrollDeltaY = dy;
Expand Down Expand Up @@ -268,6 +281,7 @@ public boolean inSelectionHotspot(@NonNull MotionEvent e)
final boolean allowsSelection = properties.optBoolean(TiC.PROPERTY_ALLOWS_SELECTION_DURING_EDITING, false);
final boolean allowsMultipleSelection
= properties.optBoolean(TiC.PROPERTY_ALLOWS_MULTIPLE_SELECTION_DURING_EDITING, false);
continuousUpdate = properties.optBoolean(TiC.PROPERTY_CONTINUOUS_UPDATE, false);

if (properties.optBoolean(TiC.PROPERTY_FIXED_SIZE, false)) {
this.recyclerView.setHasFixedSize(true);
Expand Down Expand Up @@ -789,6 +803,12 @@ public void run()
}
});
}

public void setContinousUpdate(boolean value)
{
continuousUpdate = value;
}

public void update()
{
this.update(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,7 @@ public class TiC
public static final String PROPERTY_CONTENT_INSET_START_WITH_NAVIGATION = "contentInsetStartWithNavigation";
public static final String PROPERTY_CONTENT_INTENT = "contentIntent";
public static final String PROPERTY_CONTENT_OFFSET = "contentOffset";
public static final String PROPERTY_CONTINUOUS_UPDATE = "continuousUpdate";
public static final String PROPERTY_PADDING = "padding";
public static final String PROPERTY_PADDING_BOTTOM = "paddingBottom";
public static final String PROPERTY_PADDING_LEFT = "paddingLeft";
Expand Down
9 changes: 9 additions & 0 deletions apidoc/Titanium/UI/ListView.yml
Original file line number Diff line number Diff line change
Expand Up @@ -940,6 +940,15 @@ properties:
platforms: [android, iphone, ipad, macos]
since: 3.2.0

- name: continuousUpdate
summary: Determines if the scrolling event should fire every time there is a new visible item.
description: |
When setting `continuousUpdate` to `true` the [scrolling](Titanium.UI.ListView.scrolling) event will fire every time a new item is scrolled in.
type: Boolean
default: false
platforms: [android, iphone, ipad, macos]
since: 11.1.0

- name: keepSectionsInSearch
summary: Determines if the section information is displayed in the search results when using the `searchText` property.
description: |
Expand Down
51 changes: 43 additions & 8 deletions iphone/Classes/TiUIListView.m
Original file line number Diff line number Diff line change
Expand Up @@ -1934,17 +1934,52 @@ - (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexP
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
//Events - pull (maybe scroll later)
if (![self.proxy _hasListeners:@"pull"]) {
if (![self.proxy _hasListeners:@"pull"] && ![self.proxy _hasListeners:@"scrolling"]) {
return;
}

if ((_pullViewProxy != nil) && ([scrollView isTracking])) {
if ((scrollView.contentOffset.y < pullThreshhold) && !pullActive) {
pullActive = YES;
[self.proxy fireEvent:@"pull" withObject:[NSDictionary dictionaryWithObjectsAndKeys:NUMBOOL(pullActive), @"active", nil] withSource:self.proxy propagate:NO reportSuccess:NO errorCode:0 message:nil];
} else if ((scrollView.contentOffset.y > pullThreshhold) && (pullActive)) {
pullActive = NO;
[self.proxy fireEvent:@"pull" withObject:[NSDictionary dictionaryWithObjectsAndKeys:NUMBOOL(pullActive), @"active", nil] withSource:self.proxy propagate:NO reportSuccess:NO errorCode:0 message:nil];
if ([self.proxy _hasListeners:@"pull"]) {
if ((_pullViewProxy != nil) && ([scrollView isTracking])) {
if ((scrollView.contentOffset.y < pullThreshhold) && !pullActive) {
pullActive = YES;
[self.proxy fireEvent:@"pull" withObject:[NSDictionary dictionaryWithObjectsAndKeys:NUMBOOL(pullActive), @"active", nil] withSource:self.proxy propagate:NO reportSuccess:NO errorCode:0 message:nil];
} else if ((scrollView.contentOffset.y > pullThreshhold) && (pullActive)) {
pullActive = NO;
[self.proxy fireEvent:@"pull" withObject:[NSDictionary dictionaryWithObjectsAndKeys:NUMBOOL(pullActive), @"active", nil] withSource:self.proxy propagate:NO reportSuccess:NO errorCode:0 message:nil];
}
}
}

BOOL continuousUpdate = [TiUtils boolValue:[self.proxy valueForKey:@"continuousUpdate"] def:NO];
if (continuousUpdate && [self.proxy _hasListeners:@"scrolling"]) {
if ([[self tableView] isDragging] || [[self tableView] isDecelerating]) {
dispatch_async(dispatch_get_main_queue(), ^{
NSArray *indexPaths = [[self tableView] indexPathsForVisibleRows];
NSMutableDictionary *eventArgs = [NSMutableDictionary dictionary];
TiUIListSectionProxy *section;

if ([indexPaths count] > 0) {
NSIndexPath *indexPath = [self pathForSearchPath:[indexPaths objectAtIndex:0]];
NSUInteger visibleItemCount = [indexPaths count];
section = [[self listViewProxy] sectionForIndex:[indexPath section]];

[eventArgs setValue:NUMINTEGER([indexPath row]) forKey:@"firstVisibleItemIndex"];
[eventArgs setValue:NUMUINTEGER(visibleItemCount) forKey:@"visibleItemCount"];
[eventArgs setValue:NUMINTEGER([indexPath section]) forKey:@"firstVisibleSectionIndex"];
[eventArgs setValue:section forKey:@"firstVisibleSection"];
[eventArgs setValue:[section itemAtIndex:[indexPath row]] forKey:@"firstVisibleItem"];
} else {
section = [[self listViewProxy] sectionForIndex:0];

[eventArgs setValue:NUMINTEGER(-1) forKey:@"firstVisibleItemIndex"];
[eventArgs setValue:NUMUINTEGER(0) forKey:@"visibleItemCount"];
[eventArgs setValue:NUMINTEGER(0) forKey:@"firstVisibleSectionIndex"];
[eventArgs setValue:section forKey:@"firstVisibleSection"];
[eventArgs setValue:NUMINTEGER(-1) forKey:@"firstVisibleItem"];
}

[self.proxy fireEvent:@"scrolling" withObject:eventArgs propagate:NO];
});
}
}
}
Expand Down
59 changes: 59 additions & 0 deletions tests/Resources/ti.ui.listview.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1071,6 +1071,65 @@ describe('Titanium.UI.ListView', function () {
win.open();
});

it('scrolling event', finish => {
const listView = Ti.UI.createListView({
continuousUpdate: true,
templates: {
test: {
childTemplates: [ {
type: 'Ti.UI.View',
childTemplates: [ {
type: 'Ti.UI.Label',
bindId: 'label',
properties: {
color: 'black',
bindId: 'label'
}
} ],
properties: {
width: Ti.UI.FILL,
height: 100,
cardUseCompatPadding: true,
backgroundColor: 'white',
}
} ]
}
},
defaultItemTemplate: 'test'
});
const section = Ti.UI.createListSection();
const items = [];

for (let i = 0; i < 100; i++) {
items.push({
label: { text: 'item ' + i },
template: 'test'
});
}

win = Ti.UI.createWindow({
backgroundColor: 'gray'
});

section.setItems(items);
listView.sections = [ section ];

let count = 0;
listView.addEventListener('scrolling', () => {
count++;
});
listView.addEventListener('scrollend', () => {
if (count > 50) {
finish();
}
});
setTimeout(() => {
listView.scrollToItem(0, 99);
}, 1000);
win.add(listView);
win.open();
});

it.android('listView with Ti.UI.Android.CardView', finish => {
const listView = Ti.UI.createListView({
templates: {
Expand Down

0 comments on commit 74b00ce

Please sign in to comment.