简体   繁体   中英

Infinite scrolling WP8 LongListSelector with Images

I have a LongListSelector that I am using to display a bunch of images as like thumbnails. At a certain point when scrolling (an infinite scrolling scenario) the images are downloading and the app slows down (ie scrolling becomes slow on LongListSelector) after the images show up the app is back snappy again.

I am using xaml that looks like this:

<Image Stretch="UniformToFill" 
      Margin="-3,0,0,0" 
      Source="{Binding video_thumbnail}" 
      Opacity="1" 
      Height="200" Width="480" />

video_thumbnail is a string.

Should I be creating the image this way or is there a better way to optimize my code?

I had similar problem. I have solved it by implementing my own queue for downloading images. Just download one image at the time, so you will have just one thread for it.

You may think that images will be downloaded slower, but it's not true. Downloading 5 images one by one takes the same amount of time when downloading all 5 at the same time. I tested it in my own app .

Here's my example. First, I have created a class to store basic info about videos: url to a thumbnail and the thumbnail itself as ImageSource , so you can easily bind it to Source property of Image in XAML.

public class VideoItem
{
    public string Url { get; private set; }
    public ImageSource Thumbnail { get; set; }

    public VideoItem(string url)
    {
        this.Url = url;
    }
}

The next class will store all the videos in one observable list, so you can use it in binding. The list will grow as thumbnails will be downloaded, one by one.

public class VideoLibrary
{
    private WebClient thumbnailDownloader = new WebClient();
    private Queue<VideoItem> downloadQueue = new Queue<VideoItem>();
    private bool isBusy = false;

    public ObservableCollection<VideoItem> Videos = new ObservableCollection<VideoItem>();

    public VideoLibrary()
    {
        thumbnailDownloader.OpenReadCompleted += OnThumbnailDownloaded;
    }

    // It will not start downloading process but only add a new video item (without thumbnail) to download queue.
    public void Download(string url)
    {
        downloadQueue.Enqueue(new VideoItem(url)); // Just add to queue.
        CheckQueue();
    }

    // Check whether there are new thumbnails to download. If so, start downloading just one.
    private void CheckQueue()
    {
        if (isBusy) // Stop! Downloading in progress...
            return;

        if (downloadQueue.Count > 0)
        {
            isBusy = true;
            VideoItem item = downloadQueue.Peek();
            thumbnailDownloader.OpenReadAsync(new Uri(item.Url));
        }
    }

    // One thumbnail has been downloaded. We can add it to the list of videos and check the queue again.
    private void OnThumbnailDownloaded(object sender, OpenReadCompletedEventArgs e)
    {
        isBusy = false;

        if (e.Cancelled)
        {
            CheckQueue(); // Just try again.
        }
        else if (e.Error != null)
        {
            downloadQueue.Dequeue(); // Skip this video (thumbnail), check the next one.
            CheckQueue();
        }
        else if (downloadQueue.Count > 0)
        {
            VideoItem item = downloadQueue.Dequeue(); // Remove the video from queue.
            WriteableBitmap bitmap = new WriteableBitmap(null); 
            bitmap.SetSource(e.Result);
            item.Thumbnail = bitmap; // Thumbnail is ready to use.

            Videos.Add(item); // Add to list.

            CheckQueue();
        }
    }
}

According to this article:

  • Never bind server-hosted images directly to the control, because Silverlight runtime will use the UI thread (using WebClient) to fetch that image from the server, that can make the UI unresponsive for some time.

  • Use a background thread and HttpWebRequest class based implementation to download the image data in an efficient way which finally creates BitmapImage and sets that as the source. A clean MVVM wrapper around this would make your entire Image management pretty easy.

Also, go through Best Practices for Windows Phone development `

  • set BitMapCredtiOptions value to BackGroundCreation.This will allow it to use a cached image if one already exists for the UriSource.

  • If the image is a large size, such as a high resolution image captured by many phone cameras, then a considerable amount of CPU processing can be used up in decoding all the pixels of the image. If you know the size of the image frame you intend to render, you can use that information to set the pixel size to decode.

  <Image ..>
    <Image.Source> 
         <BitmapImage
             UriSource="{Binding video_thumbnail}"
             CreateOptions="BackgroundCreation"/> 
   </Image.Source> 
 </Image>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM