1
votes

I am displaying a list of all phone book contacts using ImageCell in my Xamarin android application.

The list shows contact image, contact name and contact number. All of the information is displayed correctly but as soon as I scroll down, all of the contact images disappears and is never reloaded unless and until I rerun the application.

Below is the code to get all contacts:

var contactList = new List < ContactsModel > ();

var ContactDetailURI = ContactsContract.Contacts.ContentUri;

string[] ContactDetailProjection = {
    ContactsContract.Contacts.InterfaceConsts.DisplayName,
    ContactsContract.Contacts.InterfaceConsts.PhotoId,
    //ContactsContract.Contacts.InterfaceConsts.PhotoThumbnailUri
};

var ContactDetailCursor = Forms.Context.ContentResolver.Query(ContactDetailURI, ContactDetailProjection, null, null, null);

if (ContactDetailCursor.MoveToFirst()) {
    do {
        var imageURI = Android.Net.Uri.Parse(new System.Uri(ContactDetailCursor.GetString(ContactDetailCursor.GetColumnIndex(ContactDetailProjection[1]))).ToString());
        var stream = Android.App.Application.Context.ContentResolver.OpenInputStream(imageURI);
        var imageSource = Xamarin.Forms.ImageSource.FromStream(() => stream);

        var contact = new ContactsModel();
        contact.DisplayName = ContactDetailCursor.GetString(ContactDetailCursor.GetColumnIndex(ContactDetailProjection[0]));
        contact.PhotoId = imageSource;

        contactList.Add(contact);

    } while (ContactDetailCursor.MoveToNext());
}

return contactList;

Below is the XAML code for the page which will display all contacts:

<ContentPage.Content>
    <ListView x:Name="ContactList">
        <ListView.ItemTemplate>
            <DataTemplate>
                <ImageCell
                    Text="{Binding DisplayName}"
                    Detail="{Binding PhoneNumber}"
                    ImageSource="{Binding PhotoId}">
                </ImageCell>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
</ContentPage.Content>

P.S. I am not using any type pf component or package.

1
Sounds like the stream is never closed... Android recycles the views so the images won't show up after some scrolling and the stream is only closed once you close the app so it works when you rerun it. Have you tried wrapping the stream part into a using statement?Malte Goetz
May be you are right. Can you please guide me as where to close the stream or wrap the stream part ? I think I should code the close stream part after IF statement closing tag. Right ?Zain SMJ
Pay attention, the "stream" is always disposed by the ImageSource.FromStream method. If the ImageSource is called twice by the recycler view for the same image, maybe your stream was previously release ...hugo

1 Answers

1
votes

I had the same problem. To resolve this, I had to convert the Stream to a byte array and then back to a stream from that new byte array.

Here is my code

Stream stream = Android.App.Application.Context.ContentResolver.OpenInputStream(PhotoUri);
byte[] imageBytes = ReadFully(stream);
contact.Photo = Xamarin.Forms.ImageSource.FromStream(() => new MemoryStream(imageBytes));

public static byte[] ReadFully(Stream input)
    {
        byte[] buffer = new byte[16 * 1024];
        using (MemoryStream ms = new MemoryStream())
        {
            int read;
            while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
            {
                ms.Write(buffer, 0, read);
            }
            return ms.ToArray();
        }
    }

You can read more about creating a byte array from a stream in this question Creating a byte array from a stream