This is a migrated thread and some comments may be shown as answers.

RadListView - images rendering incorrectly

4 Answers 216 Views
ListView
This is a migrated thread and some comments may be shown as answers.
This question is locked. New answers and comments are not allowed.
Stan
Top achievements
Rank 1
Stan asked on 03 Jul 2017, 10:42 PM

I have a folder with 57 image files (no dups): 

  • On an Android real device (Samsung Note3) the pics are duplicated randomly
  • On an iOS simulator some pics are overlapping it's boundaries

Please see photos. Is there a better way to do this to fix the problems?

Thanks.

package.json

{
  "description": "NativeScript Application",
  "license": "SEE LICENSE IN <your-license-filename>",
  "readme": "NativeScript Application",
  "repository": "<fill-your-repository-here>",
  "nativescript": {
    "id": "org.nativescript.FolderList",
    "tns-android": {
      "version": "3.0.0"
    }
  },
  "dependencies": {
    "nativescript-telerik-ui": "^2.0.1",
    "nativescript-theme-core": "~1.0.2",
    "tns-core-modules": "^3.0.0"
  },
  "devDependencies": {
    "babel-traverse": "6.4.5",
    "babel-types": "6.4.5",
    "babylon": "6.4.5",
    "lazy": "1.0.11",
    "nativescript-dev-android-snapshot": "^0.*.*"
  }
}

main-page.xml

<Page xmlns="http://schemas.nativescript.org/tns.xsd" xmlns:lv="nativescript-telerik-ui/listview" loaded="pageLoaded">
    <ActionBar title="RadListView FolderList" />
    <lv:RadListView loaded="lvloaded" items="{{ myItems }}" itemLoading="func">
        <lv:RadListView.listViewLayout>
            <lv:ListViewGridLayout scrollDirection="Vertical" spanCount="3"/>
        </lv:RadListView.listViewLayout>
        <lv:RadListView.itemTemplate>
            <Image id="pic" decodeWidth="100" decodeHeight="100" loadMode="async" height="100" width="100" class="item-template-style" />
        </lv:RadListView.itemTemplate>
    </lv:RadListView>
</Page>

main-page.js

var observable_1 = require("data/observable");
var fs = require("file-system");
var listView_1 = require("nativescript-telerik-ui/listview");

var myImages = [];

var viewModel = new observable_1.Observable();
    viewModel.set("myItems", myImages);

exports.pageLoaded = function(args) {
    readFiles();
    var page = args.object;
    page.bindingContext = viewModel;
}

exports.func = function(args) {
    var layout = args.view;
    var image = layout.getViewById("pic");
    image.src = myImages[args.itemIndex];
}

function lvloaded(args) {
    var listview = args.object;
    listview.on(listView_1.RadListView.itemTapEvent, function (args) {
        console.log("selected item index " + args.itemIndex);
    });
}
exports.lvloaded = lvloaded;

function readFiles() {
    var documents = fs.knownFolders.currentApp();
    var myFolder = documents.getFolder("img");
    var array = myFolder.getEntitiesSync();
    array.forEach(function (element) {
        myImages.push(element._path);
    });
}

4 Answers, 1 is accepted

Sort by
0
Nick Iliev
Telerik team
answered on 04 Jul 2017, 08:27 AM
Hi Stan,

Thank you for contacting us and providing good snippets.
There are two main issues that can be resolved to improve both the duplicating images on Android and the overlapping in iOS.In the lines below I am going to cover both problems separately. For your convenience, I have created this test project to demonstrate the solutions based on your snippets.

Regarding the duplicated images on Android

The issue here is that you are assigning the image src property using itemLoading event. This is not a good practice and in fact, is breaking the main purposes of ListView and RadListView - the virtualization and recycling. As the recycling is trying to reuse cells when possible but you are assigning the image src in an event called after the recycling it leads to the unwanted duplicated photos.More about fast list-views, virtualization, and recycling can be found in this blog post

The solution to this problem is assigning the src of your images (inside each list view item template) in advance or even better - use binding model with observed just as you are attaching the source array itself.
.e.g
<Image src="{{ path }}"  />

Where path is just a key-value pair I have created when pushing your paths. Notice that now I am using ObservableArray so that if you need to change the array (add/remove items) the source array will be notified and the UI will be updated. The example in my test application is using 4 different photos loaded locally but the principle would be the same when using photos from a different location.

Regarding the overlapping images in iOS.

The issue here is because when using Item Layouts for RadListView you need to use itemWidth and itemHeight for iOS. So the solution would be to set your itemWidth and itemHeight instead of setting width and height of your image or item template.
e.g.
<lv:ListViewGridLayout scrollDirection="Vertical" spanCount="3" itemHeight="100" itemWidth="100"/>

You can still use decodeWidth and decodeHeight for  Android (they won't make difference for iOS) but you will have to set explicitly the itemWidth and itemHeight to overcome the dynamically assigned size of your templates.

Regards,
Nikolay Iliev
Progress Telerik
Did you know that you can open private support tickets which are reviewed and answered within 24h by the same team who built the components? This is available in our UI for NativeScript Pro + Support offering.
0
Stan
Top achievements
Rank 1
answered on 10 Jul 2017, 11:18 PM

Hi Nikolay,

Thanks so much for your insights! I'm new to NativeScript and I really appreciate it.
My images no longer overlap in the iOS simulator, I would have never figured out to use itemHeight and itemWidth for iOS. Thanks for that!
My images are in a folder called ~/img and the number of images it has changes. So I have to use knownFolders.currentApp() to fill the array.
The following code:
Works in the iOS simulator and Android emulator (same Android version as my real device)
Still not working on my Android real device (Samsung Note3 Android ver 5.0). Still duplicating images (please see attached).
I've also noticed a new problem now, when I make any changes to the code all the images load again (appended to the bottom) so I have multiple sets. Is there a way to stop that? My next step is listViewItemTap to detail page and display a full image, I don't want to navigate back and get another set of images appended to the bottom. Any help you can give me will be greatly appreciated.
Thanks so much...

package.json
{
  "description": "NativeScript Application",
  "license": "SEE LICENSE IN <your-license-filename>",
  "readme": "NativeScript Application",
  "repository": "<fill-your-repository-here>",
  "nativescript": {
    "id": "org.nativescript.FolderList",
    "tns-android": {
      "version": "3.0.0"
    }
  },
  "dependencies": {
    "nativescript-telerik-ui": "^2.0.1",
    "nativescript-theme-core": "~1.0.2",
    "tns-core-modules": "^3.0.0"
  },
  "devDependencies": {
    "babel-traverse": "6.4.5",
    "babel-types": "6.4.5",
    "babylon": "6.4.5",
    "lazy": "1.0.11",
    "nativescript-dev-android-snapshot": "^0.*.*"
  }
}

main-page.xml

<Page xmlns="http://schemas.nativescript.org/tns.xsd" xmlns:lv="nativescript-telerik-ui/listview" loaded="pageLoaded">
    <ActionBar title="RadListView FolderList" />
    <lv:RadListView loaded="lvloaded" items="{{ myItems }}">
        <lv:RadListView.listViewLayout>
            <lv:ListViewGridLayout scrollDirection="Vertical" itemHeight="100" itemWidth="100" spanCount="3"/>
        </lv:RadListView.listViewLayout>
        <lv:RadListView.itemTemplate>
            <!-- Need loadMode="async" for Android emulator or get OutOfMemory error  -->
            <Image src="{{ path }}" loadMode="async" class="item-template-style" height="100" width="100"/>
        </lv:RadListView.itemTemplate>
    </lv:RadListView>
</Page>

main-page.js

var observable_1 = require("data/observable");
var fs = require("file-system");
var listView_1 = require("nativescript-telerik-ui/listview");
var ObservableArray = require("data/observable-array").ObservableArray;

var myImages = new ObservableArray();
var viewModel = new observable_1.Observable();
    viewModel.set("myItems", myImages);

exports.pageLoaded = function(args) {
    readFiles();
    var page = args.object;
    page.bindingContext = viewModel;
}

exports.lvloaded = function(args) {
    var listview = args.object;
    listview.on(listView_1.RadListView.itemTapEvent, function (args) {
        console.log("selected item index " + args.itemIndex);
    });
}

function readFiles() {
    var documents = fs.knownFolders.currentApp();
    var myFolder = documents.getFolder("img");
    var array = myFolder.getEntitiesSync();
    array.forEach(function (element) {
        myImages.push({path: element._path});
    });
}

 

0
Accepted
Nick Iliev
Telerik team
answered on 11 Jul 2017, 11:23 AM
Hello Stan,

Regarding the issue with duplicating images

We have modified my test application so that it will use the same workflow for loading images (via local folder using the file-system module) but still was not able to reproduce the issue with duplicated images. We have tested on few different phones (including some old models like LG g2 and Samsung S3) + multiple emulators but the issue was not observed on any of them,
Would it be possible to retest the case using the provided test application we have created? Also, try to completely uninstall the app from your test Samsung Note and then rebuild and redeploy the application just to exclude any local caching issues.

Regarding the issue with images loading again in multiple sets

The issue is caused by the fact that in the project the initialization of the array of image paths in the loaded callback. Each time the user navigates, the loaded event will be triggered and the method readFiles will be called again.
There are multiple solutions to this problem. The first one would be to check if an image path is unique and if not no to push it again (this means that you have to know the name of files). Another possible solution is to reset the observable array and push the array of paths in a brand new empty array.
e.g.
function readFiles() {
    var documents = fs.knownFolders.currentApp();
    var myFolder = documents.getFolder("images");
    var arr = myFolder.getEntitiesSync();
 
    myImagePaths = new ObservableArray(); // reset the array so now it will contain 0 iamges
 
    arr.forEach(function (element) {
        myImagePaths.push({path: element._path}); // push each image again
           // if you prefer the first approach you can do the check here
           // (e.g. pseudo code if(!pathExists) push; else don't push)
    });
 
    viewModel.set("myItems", myImagePaths); // set the array in the view-model
}



Regards,
Nikolay Iliev
Progress Telerik
Did you know that you can open private support tickets which are reviewed and answered within 24h by the same team who built the components? This is available in our UI for NativeScript Pro + Support offering.
0
Stan
Top achievements
Rank 1
answered on 18 Jul 2017, 10:16 PM

Thanks for your help. The rebuild and redeploy the application did the trick on my real device.

Your suggestions also took care of my images loading in multiple sets.

A big thanks to you!

Tags
ListView
Asked by
Stan
Top achievements
Rank 1
Answers by
Nick Iliev
Telerik team
Stan
Top achievements
Rank 1
Share this question
or