Images loaded into CollectionView items via ImageSource.FromResource at runtime are not visible and break the layout
See original GitHub issueDescription
Hello,
If you show images in CollectionView items and update these items at runtime, certain images may not be loaded with the corresponding exception in the output. After that, the entire application’s layout becomes broken. The issue occurs only on Android devices.
Steps to Reproduce
- Run the application on an Android simulator;
- Use the Refresh button several times. Each time two items with different icons should be displayed in the CollectionView.
- Once you see that only one image is shown - review the Output window. You will see an error (see the call stack below);
- Try to switch to a different tab. You will see a completely empty pages as the layout is broken.
The screencast below illustrates these steps and the problematic behavior:
Link to public reproduction project repository
https://github.com/AndreyMarten/maui-android-image-from-resource-issue
Version with bug
7.0 (current)
Last version that worked well
Unknown/Other
Affected platforms
Android
Affected platform versions
Android 11
Did you find any workaround?
No
Relevant log output
[0:] Microsoft.Maui.StreamImageSourceService: Warning: Unable to load image stream.
Android.Util.AndroidRuntimeException: Only the original thread that created a view hierarchy can touch its views.
at Java.Interop.JniEnvironment.StaticMethods.CallStaticVoidMethod(JniObjectReference type, JniMethodInfo method, JniArgumentValue* args) in /Users/runner/work/1/s/xamarin-android/external/Java.Interop/src/Java.Interop/obj/Release/net7.0/JniEnvironment.g.cs:line 13250
at Java.Interop.JniPeerMembers.JniStaticMethods.InvokeVoidMethod(String encodedMember, JniArgumentValue* parameters) in /Users/runner/work/1/s/xamarin-android/external/Java.Interop/src/Java.Interop/Java.Interop/JniPeerMembers.JniStaticMethods.cs:line 97
at Microsoft.Maui.PlatformInterop.LoadImageFromStream(ImageView imageView, Stream inputStream, IImageLoaderCallback callback) in D:\a\_work\1\s\src\Core\src\obj\Release
et7.0-android\generated\src\Microsoft.Maui.PlatformInterop.cs:line 443
at Microsoft.Maui.StreamImageSourceService.LoadDrawableAsync(IImageSource imageSource, ImageView imageView, CancellationToken cancellationToken) in D:\a\_work\1\s\src\Core\src\ImageSources\StreamImageSourceService\StreamImageSourceService.Android.cs:line 28
--- End of managed Android.Util.AndroidRuntimeException stack trace ---
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:8798)
at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:1606)
at android.view.View.requestLayout(View.java:25390)
at android.view.View.requestLayout(View.java:25390)
at android.view.View.requestLayout(View.java:25390)
at android.view.View.requestLayout(View.java:25390)
at android.view.View.requestLayout(View.java:25390)
at android.view.View.requestLayout(View.java:25390)
at android.view.View.requestLayout(View.java:25390)
at androidx.drawerlayout.widget.DrawerLayout.requestLayout(DrawerLayout.java:1353)
at android.view.View.requestLayout(View.java:25390)
at android.view.View.requestLayout(View.java:25390)
at android.view.View.requestLayout(View.java:25390)
at android.view.View.requestLayout(View.java:25390)
at android.view.View.requestLayout(View.java:25390)
at android.view.View.requestLayout(View.java:25390)
at androidx.recyclerview.widget.RecyclerView.requestLayout(RecyclerView.java:4586)
at android.view.View.requestLayout(View.java:25390)
at android.view.View.requestLayout(View.java:25390)
at android.view.View.requestLayout(View.java:25390)
at android.view.View.requestLayout(View.java:25390)
at android.view.View.requestLayout(View.java:25390)
at androidx.recyclerview.widget.RecyclerView.requestLayout(RecyclerView.java:4586)
at android.view.View.requestLayout(View.java:25390)
at android.view.View.requestLayout(View.java:25390)
at android.widget.ImageView.setImageDrawable(ImageView.java:597)
at androidx.appcompat.widget.AppCompatImageView.setImageDrawable(AppCompatImageView.java:112)
at com.microsoft.maui.glide.MauiCustomViewTarget.onResourceCleared(MauiCustomViewTarget.java:30)
at com.bumptech.glide.request.target.CustomViewTarget.onLoadCleared(CustomViewTarget.java:210)
at com.bumptech.glide.request.SingleRequest.clear(SingleRequest.java:337)
at com.bumptech.glide.request.ErrorRequestCoordinator.clear(ErrorRequestCoordinator.java:48)
at com.bumptech.glide.manager.RequestTracker.clearAndRemove(RequestTracker.java:70)
at com.bumptech.glide.RequestManager.untrack(RequestManager.java:660)
at com.bumptech.glide.Glide.removeFromManagers(Glide.java:913)
at com.bumptech.glide.RequestManager.untrackOrDelegate(RequestManager.java:647)
at com.bumptech.glide.RequestManager.clear(RequestManager.java:624)
at com.bumptech.glide.RequestBuilder.into(RequestBuilder.java:811)
at com.bumptech.glide.RequestBuilder.into(RequestBuilder.java:780)
at com.bumptech.glide.RequestBuilder.into(RequestBuilder.java:771)
at com.microsoft.maui.PlatformInterop.prepare(PlatformInterop.java:229)
at com.microsoft.maui.PlatformInterop.loadInto(PlatformInterop.java:234)
at com.microsoft.maui.PlatformInterop.loadImageFromStream(PlatformInterop.java:265)
--- End of managed Android.Util.AndroidRuntimeException stack trace ---
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:8798)
at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:1606)
at android.view.View.requestLayout(View.java:25390)
at android.view.View.requestLayout(View.java:25390)
at android.view.View.requestLayout(View.java:25390)
at android.view.View.requestLayout(View.java:25390)
at android.view.View.requestLayout(View.java:25390)
at android.view.View.requestLayout(View.java:25390)
at android.view.View.requestLayout(View.java:25390)
at androidx.drawerlayout.widget.DrawerLayout.requestLayout(DrawerLayout.java:1353)
at android.view.View.requestLayout(View.java:25390)
at android.view.View.requestLayout(View.java:25390)
at android.view.View.requestLayout(View.java:25390)
at android.view.View.requestLayout(View.java:25390)
at android.view.View.requestLayout(View.java:25390)
at android.view.View.requestLayout(View.java:25390)
at androidx.recyclerview.widget.RecyclerView.requestLayout(RecyclerView.java:4586)
at android.view.View.requestLayout(View.java:25390)
at android.view.View.requestLayout(View.java:25390)
at android.view.View.requestLayout(View.java:25390)
at android.view.View.requestLayout(View.java:25390)
at android.view.View.requestLayout(View.java:25390)
at androidx.recyclerview.widget.RecyclerView.requestLayout(RecyclerView.java:4586)
at android.view.View.requestLayout(View.java:25390)
at android.view.View.requestLayout(View.java:25390)
at android.widget.ImageView.setImageDrawable(ImageView.java:597)
at androidx.appcompat.widget.AppCompatImageView.setImageDrawable(AppCompatImageView.java:112)
at com.microsoft.maui.glide.MauiCustomViewTarget.onResourceCleared(MauiCustomViewTarget.java:30)
at com.bumptech.glide.request.target.CustomViewTarget.onLoadCleared(CustomViewTarget.java:210)
at com.bumptech.glide.request.SingleRequest.clear(SingleRequest.java:337)
at com.bumptech.glide.request.ErrorRequestCoordinator.clear(ErrorRequestCoordinator.java:48)
at com.bumptech.glide.manager.RequestTracker.clearAndRemove(RequestTracker.java:70)
at com.bumptech.glide.RequestManager.untrack(RequestManager.java:660)
at com.bumptech.glide.Glide.removeFromManagers(Glide.java:913)
at com.bumptech.glide.RequestManager.untrackOrDelegate(RequestManager.java:647)
at com.bumptech.glide.RequestManager.clear(RequestManager.java:624)
at com.bumptech.glide.RequestBuilder.into(RequestBuilder.java:811)
at com.bumptech.glide.RequestBuilder.into(RequestBuilder.java:780)
at com.bumptech.glide.RequestBuilder.into(RequestBuilder.java:771)
at com.microsoft.maui.PlatformInterop.prepare(PlatformInterop.java:229)
at com.microsoft.maui.PlatformInterop.loadInto(PlatformInterop.java:234)
at com.microsoft.maui.PlatformInterop.loadImageFromStream(PlatformInterop.java:265)
Issue Analytics
- State:
- Created 6 months ago
- Reactions:1
- Comments:7
Top Results From Across the Web
DXCollectionView does not show images in its items when ...
DevExpress Support Team: CLONED FROM T1154812: MAUI, Images are ... Found another issue with loosing images, now it is in DXCollectionView
Read more >Unable to Display Image Inside CollectionView Using ...
Everything is working, except being able to display the image inside the CollectionView. It seems that Xamarin.Forms won't allow using a ...
Read more >Images not displayed in collection view cell
I have a collection view cell and inside there's an imageView. On it I display array of images using sd_setImage once loaded from...
Read more >Grouping in Xamarin ListView (SfListView)
A group represents a collection of items belongs to a category. When grouping is applied, the data is organized into different groups based...
Read more >CollectionView layout gets mad after scrolling
Hi, I'm setting a collection View in order to display content as instagram Profile view with all the photos in the grid. in...
Read more >
Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free
Top Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
I was facing the same problem today, and I found that under certain cases, for example when returning to a View, this problem appears. I suspect there could be an issue in
StreamImageSourceService.Android.cs
whenPlatformInterop.LoadImageFromStream(imageView, stream, callback)
is called.The
ByteArrayToImageSourceConverter
from the toolkit uses an overload which allows you to create a stream, usingImageSource.FromStream(() => new MemoryStream(value))
which seems to tickle the thread access problem.I have solved it by modifying the Converter so it uses another overload which accepts a Task. This seems to be handled better in the underlying platform handlers of Maui:
return ImageSource.FromStream((token) => Task.FromResult((Stream)new MemoryStream(value)));
Link to my fork: https://github.com/sigged/ctk-maui/blob/bugfix/invalid-thread-bytetoimageconverter/src/CommunityToolkit.Maui/Converters/ByteArrayToImageSourceConverter.shared.cs
If you make the image buildAction as MauiImage rather than embedded resource and try loading the image as below - i see the app works without issues. private void Button_Clicked(object sender, EventArgs e) {