Hello,
I have set up a ListView for Android with a LinearLayout scrolling horizontally. Each item takes up the complete width of the screen.
Now, whenever the user finishes scrolling, I'd like the item to be smoothly centered in the ListView just like an image slider.
I suppose for iOS setting "scrollPosition" to "ListViewScrollPosition.CenteredHorizontally" would exactly get me the result, I'm looking for.
But how do I implement this behaviour for Android?
Greetings,
John
16 Answers, 1 is accepted
Thank you for contacting us and for your interest in nativescript-telerik-ui plugins.
Indeed at this very moment, there is no option to scrollToPosition by pixels on Android,
The only scrolling opportunity at this moment is to scrollByIndex where you can pass the index of your item.
The good news is that our developer's team is already planning this feature. For you convenience, I have opened this feature request issue where you can track the updated info on that matter (the issue is a linked to the internal developer's side issue). Once we have a stable solution we will update the info and the release notes for RadListView and also the article for scrollTo Position as well.
Regards,
Nikolay Iliev
Progress Telerik

Hi Nikolay,
Thanks for your reply and opening an issue for me. Happy to hear the feature is already planned.
So just to clarify, this feature would also include the following behaviour:
After releasing 'scroll' the list item would move automatically to the desired position, like ListViewScrollPosition for iOS?
And one more point. I tested scrollToIndex(). Just in case I missed it in the documentation: There is no way to have a smooth transition, is there? Using this function it directly jumps to the index.
Regards,
John
The actual implementation will depend on multiple factors including selecting the most stable and reversible solution. I would suggest appending your question to the opened issue where the developers would be able to look at it and decide if that is doable functionality.
Regarding the question about smooth scroll on Android
There is a solution that will allow you to create smooth scrolling for Android; However, the first thing I want to underline that the solution is using private properties (usually with names starting with underscore _) which can not be considered as stable workaround for future upcoming releases (all private properties and methods can be changed or removed in future releases). That said here is the solution:
You will need to get reference to your list
e.g.
let list;
export
function
onPageLoaded(args) {
let page = args.object;
list = <RadListView>(page.getViewById(
"listView"
));
page.bindingContext =
new
viewModel.ViewModel();
}
And from there you can access the private Android implementation with _android on which you can access the method smoothScrollToPosition
export
function
makeSmoothScroll() {
list._android.smoothScrollToPosition(5);
}
Regards,
Nikolay Iliev
Progress Telerik

Hi Nikolay,
Thanks for your help. Smooth scrolling works like a charm. I'll keep the risk with private members and future updates in mind.
Regarding my other question: I posted it on GitHub as suggested.
Regards,
John
Thanks for writing.
The upcoming NS UI 3.0 release will contain API for smooth-scrolling to a given position and by a given amount of pixels. The snapping feature is something we need more time to implement and thus will postpone it for the next (post-June) iteration. You will still be able to implement it on your side by calculating the amount of pixels to scroll by to center a given item though.
Please do not hesitate to write back in case you have further questions or need assistance.
Regards,
Deyan
Progress Telerik

Hello,
I waited for the 3.0 Release and switched from the private member _android to the new function scrollToIndex(index, true) and got it all up and running as desired.
I have two remaining questions though. My setup is roughly as follows:
The html:
<
RadListView
#listView (itemLoading)="onItemLoading($event)" (scrollEnded)="onScrollEnded($event)" [items]="itemData">
<
ListViewLinearLayout
tkListViewLayout
scrollDirection
=
"Horizontal"
></
ListViewLinearLayout
>
<
ng-template
tkListTemplate
let-itemData
=
"item"
>
<
GridLayout
rows
=
"*,*,*,*,*"
columns
=
"*,*,*,*,*"
>
<
StackLayout
*
ngFor
=
"let firstFieldItem of itemData.firstField; let i = index"
[row]="(i / 5) | trunc" [col]="i % 5">
<
Label
[text]="firstFieldItem"></
Label
>
</
Stacklayout
>
</
GridLayout
>
<
GridLayout
rows
=
"*,*,*,*,*"
columns
=
"*,*,*,*,*"
>
<
StackLayout
*
ngFor
=
"let secondFieldItem of itemData.secondField; let i = index"
[row]="(i / 5) | trunc" [col]="i % 5">
<
Label
[text]="secondFieldItem"></
Label
>
</
Stacklayout
>
</
GridLayout
>
<
GridLayout
rows
=
"*,*,*,*,*"
columns
=
"*,*,*,*,*"
>
<
StackLayout
*
ngFor
=
"let thirdFieldItem of itemData.thirdField; let i = index"
[row]="(i / 5) | trunc" [col]="i % 5">
<
Label
[text]="thirdFieldItem"></
Label
>
</
Stacklayout
>
</
GridLayout
>
</
ng-template
>
</
RadListView
>
The typescript:
export class MyListViewComponent {
@ViewChild("listView) listViewComponent: RadListViewComponent;
private screenWidth: number = screen.mainScreen.widthDIPs;
protected itemData: Array...
protected onItemLoading(event: ListViewEventData) {
event.view.width =
this
.screenWidth;
}
protected onScrollEnded(event: ListViewScrollEventData) {
currentListItem = Math.round(event.scrollOffset /
this
.screenWidth);
this
.listViewComponent.listView.scrollToIndex(currentListItem ,
true
);
}
}
So my questions are:
1. Instead of waiting for the scrolling to end before I manually scroll to an index, I'd like to already do that as soon as the mouse/finger goes up, thus only scrolling to the next/previous item. I just couldn't find a hook for it. Because currently when I do a really quick swipe, it fastly scrolls to the end of the list.
2. My current setup with three nested GridLayout with ngFor-loops inside takes some time to initialize and stutters on the first pass. Is there a way to optimize it for performance?
Regards,
John
Thanks for writing back. Good to hear that the new APIs are working.
1. We will need to further extend the scrolling events to notify you when the drag gesture has ended. This will be added in the TODO list but there's no an adequate way to do this as of now.
2. Can you please elaborate a bit further what your exact scenario with the template is? If you have a predefined number of rows and columns (5) you may simply create them all at once in your template and bind multiple labels to the separate items from the array on your business object without using ngFor loops.
Let us know should you have additional questions.
Regards,
Deyan
Progress Telerik

Hello,
thank you for your help and sorry I couldn't reply any sooner.
About my questions from above:
1. Ok, so there is no "dragEnded" event yet. Alternativly is it possible to adjust the scrolling speed? In my case scrolling at a much slower pace would already help.
2. The number of items is indeed predefined. I got rid of the ngFor-Loops and hardcoded all the labels. This did improve the performance to some amount.
I'll try to describe the scenario in more detail. After the changes the GridLayouts inside the RadListView look like:
<
GridLayout
rows
=
"*,*,*,*,*"
columns
=
"*,*,*,*,*"
>
<
StackLayout
row
=
"0"
col
=
"0"
(tap)="onTap("1") [class.selected]="isSelected("1")>
<
Label
text
=
"1"
></
Label
>
</
StackLayout
>
<
StackLayout
row
=
"0"
col
=
"1"
(tap)="onTap("2") [class.selected]="isSelected("2")>
<
Label
text
=
"2"
></
Label
>
</
StackLayout
>
...
</
GridLayout
>
I put every Label inside its own StackLayout to center the text vertically; the fields have no absolute height. Maybe there is a better way? The result looks like in the attachment.
So with a StackLayout and two event listeners per Label there is perhaps some overhead which could be handled better?
Anyway, as soon as I have scrolled for the first time, the template is preloaded and moves fluently. But it takes some time to initialize and stutters when i drag for the first time. Is there a way to optimize the preloading?
Regards,
John
Thanks for writing back.
1. I am happy to inform you that we've just added the scrollDragEnded event on RadListView which is fired each time the user raises their finger off the device's screen as a result of a scrolling gesture. You will be able to access the new API in a day or two when the nativescript-telerik-ui-pro@next and nativescript-telerik-ui@next versions are updated on npmjs.com. This new API will also make it into the next official version of the plugin.
2. Just thinking about the case, I cannot immediately propose a solution that will improve the performance. It might help if you are able to attach your project here so that we can directly take a look and see how we can help.
Thanks for your time.
Regards,
Deyan
Progress Telerik
On a side-note, we have prepared a very thorough blogpost on the performance of NativeScript apps and the different approaches to finding bottlenecks:
https://www.nativescript.org/blog/deep-dive-into-nativescript-3.1-performance-improvements
There are several tools you might find useful in determining the reasons for various performance issues.
Regards,
Deyan
Progress Telerik

Hello Deyan,
those are great news! Both, the next release and the blogpost. I'll have a thorough read, thanks.
I will attach the project as soon as I finish to create a trimmed-down version.
Regards,
John

I created a basic version of my app just consisting of the page containing the list view and a service to manage some data.
You can find it here: https://github.com/JohnMller/performance-test
As described earlier, I use the list view as some kind of image slider which you can drag sideways.
On each slide you can select up to five numbers, deselect them, fill it randomly or clear it.
The issue is that the list view stutters when dragging the slides to the side. This occurs after the page is initialized. As soon as the list items are cached it runs more smoothly.
When you navigate to another page ('next' button) and back again the list items have to be cached again.
Kind regards,
John
Thanks for writing back.
I have reviewed the behaviour mentioned by you and have managed to reproduce the unacceptable scrolling experience up until views are properly cached. Unfortunately this scenario would bring performance degradation even if you implemented it using native Android, i.e. without NativeScript and Angular on top. To improve this scenario a bit of custom coding would be required.
One possible option would be to implement a custom component that visualises these numbers in a grid without using elements like Labels and StackLayouts. This will improve the performance since the layout system will be excluded from the equation. Moreover, creation of the Labels and StackLayouts will also not happen. You will simply have one element that will receive size and will manually draw the cells and the numbers. It will also react to the user input and will change the state of the selected cells. This will be quite fast. This will also require native iOS and Android development but is not a complicated task to do. You will be able to easily reuse this native component in your NativeScript app then.
Another possible option would be to use a component that we plan to expose in NativeScript UI - a TabView which may also work as a SlideView, i.e. without showing tabs and allowing for browsing through items by sliding a finger on each of them. This is on our TODO list for the next couple of months.
I have also tried to reduce the amount of visual elements within your item template to improve performance although no considerable results were achieved - see the attached file for details. It might give you some direction how to optimize further.
I hope this is helpful.
Regards,
Deyan
Progress Telerik

Hi Deyan,
could you please elaborate your suggestion to implement a custom component a little bit? Just a pointer to some examples to get the idea, since I haven't developed native Android/iOS so far.
By the way, I'm really happy with the new scrollDragEnded event. It does improve the handling quite a bit.
Is it possible to enable/disable scrolling for the ListView?
Thank you,
Regards,
John
Of course.
My point was to implement a custom Android View and wrap it in a NativeScript View which can be used in your XML that represents the List template. Instead of using tens of StackLayout and Label instances, you can simply draw them without building a complex hierarchy. Here's the official Google documentation on drawing in a View:
https://developer.android.com/training/custom-views/custom-drawing.html
You may also find this article useful:
http://www.vogella.com/tutorials/AndroidCustomViews/article.html
This should not take much time to implement and the Java APIs for Android are quite straightforward.
I hope this is helpful.
Regards,
Deyan
Progress Telerik
I have just noticed that I missed your last question:
Currently it is not possible to block the scrolling in RadListView. We will put this on our TODO list for implementation.
Here's a useful link on how to integrate a native Android library for usage in NativeScript: https://docs.nativescript.org/runtimes/android/plugins/plugins#directory-structure
Regards,
Deyan
Progress Telerik