Skip to content

Commit b0da2a0

Browse files
authored
Check to see if Image Should Reload When Attached (#24023)
* Check to see if Image Should Reload When Attached * - cleanup * Update ImageSourcePartLoader.cs * - fix images
1 parent 6bfd7e1 commit b0da2a0

File tree

12 files changed

+245
-1
lines changed

12 files changed

+245
-1
lines changed
137 KB
Loading
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<Shell xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
3+
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
4+
x:Class="Maui.Controls.Sample.Issues.Issue14471"
5+
xmlns:ns="clr-namespace:Maui.Controls.Sample.Issues">
6+
<TabBar>
7+
<ShellContent Route="tab1" Title="tab1" ContentTemplate="{DataTemplate ns:Issue14471Tab1Content}"/>
8+
<ShellContent Route="tab2" Title="tab2" ContentTemplate="{DataTemplate ns:Issue14471Tab2Content}"/>
9+
</TabBar>
10+
</Shell>
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
namespace Maui.Controls.Sample.Issues
2+
{
3+
[XamlCompilation(XamlCompilationOptions.Compile)]
4+
[Issue(IssueTracker.Github, 14471, "Image can disappear when going back to the page", PlatformAffected.Android)]
5+
public partial class Issue14471 : Shell
6+
{
7+
public Issue14471()
8+
{
9+
InitializeComponent();
10+
}
11+
}
12+
13+
public class Issue14471Tab1Content : ContentPage
14+
{
15+
16+
public Issue14471Tab1Content()
17+
{
18+
var image = new Image() { HeightRequest = 100, AutomationId = "image" };
19+
var imageButton = new ImageButton() { HeightRequest = 100, AutomationId = "imageButton" };
20+
Content = new VerticalStackLayout()
21+
{
22+
new Button()
23+
{
24+
AutomationId = "switchToTab2Button",
25+
Text = "Switch to tab 2",
26+
Command = new Command(async () => await Shell.Current.GoToAsync("//tab2"))
27+
},
28+
image,
29+
imageButton
30+
};
31+
LoadImage((source) => image.Source = source);
32+
LoadImage((source) => imageButton.Source = source);
33+
}
34+
35+
async void LoadImage(Action<ImageSource> setImageSource)
36+
{
37+
await Task.Delay(1);
38+
var imageBytes = Convert.FromBase64String("iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAB+FBMVEUAAAA/mUPidDHiLi5Cn0XkNTPmeUrkdUg/m0Q0pEfcpSbwaVdKskg+lUP4zA/iLi3msSHkOjVAmETdJSjtYFE/lkPnRj3sWUs8kkLeqCVIq0fxvhXqUkbVmSjwa1n1yBLepyX1xxP0xRXqUkboST9KukpHpUbuvRrzrhF/ljbwaljuZFM4jELaoSdLtElJrUj1xxP6zwzfqSU4i0HYnydMtUlIqUfywxb60AxZqEXaoifgMCXptR9MtklHpEY2iUHWnSjvvRr70QujkC+pUC/90glMuEnlOjVMt0j70QriLS1LtEnnRj3qUUXfIidOjsxAhcZFo0bjNDH0xxNLr0dIrUdmntVTkMoyfL8jcLBRuErhJyrgKyb4zA/5zg3tYFBBmUTmQTnhMinruBzvvhnxwxZ/st+Ktt5zp9hqota2vtK6y9FemNBblc9HiMiTtMbFtsM6gcPV2r6dwroseLrMrbQrdLGdyKoobKbo3Zh+ynrgVllZulTsXE3rV0pIqUf42UVUo0JyjEHoS0HmsiHRGR/lmRz/1hjqnxjvpRWfwtOhusaz0LRGf7FEfbDVmqHXlJeW0pbXq5bec3fX0nTnzmuJuWvhoFFhm0FtrziBsjaAaDCYWC+uSi6jQS3FsSfLJiTirCOkuCG1KiG+wSC+GBvgyhTszQ64Z77KAAAARXRSTlMAIQRDLyUgCwsE6ebm5ubg2dLR0byXl4FDQzU1NDEuLSUgC+vr6urq6ubb29vb2tra2tG8vLu7u7uXl5eXgYGBgYGBLiUALabIAAABsElEQVQoz12S9VPjQBxHt8VaOA6HE+AOzv1wd7pJk5I2adpCC7RUcHd3d3fXf5PvLkxheD++z+yb7GSRlwD/+Hj/APQCZWxM5M+goF+RMbHK594v+tPoiN1uHxkt+xzt9+R9wnRTZZQpXQ0T5uP1IQxToyOAZiQu5HEpjeA4SWIoksRxNiGC1tRZJ4LNxgHgnU5nJZBDvuDdl8lzQRBsQ+s9PZt7s7Pz8wsL39/DkIfZ4xlB2Gqsq62ta9oxVlVrNZpihFRpGO9fzQw1ms0NDWZz07iGkJmIFH8xxkc3a/WWlubmFkv9AB2SEpDvKxbjidN2faseaNV3zoHXvv7wMODJdkOHAegweAfFPx4G67KluxzottCU9n8CUqXzcIQdXOytAHqXxomvykhEKN9EFutG22p//0rbNvHVxiJywa8yS2KDfV1dfbu31H8jF1RHiTKtWYeHxUvq3bn0pyjCRaiRU6aDO+gb3aEfEeVNsDgm8zzLy9egPa7Qt8TSJdwhjplk06HH43ZNJ3s91KKCHQ5x4sw1fRGYDZ0n1L4FKb9/BP5JLYxToheoFCVxz57PPS8UhhEpLBVeAAAAAElFTkSuQmCC");
39+
setImageSource.Invoke(ImageSource.FromStream(() => new MemoryStream(imageBytes)));
40+
}
41+
}
42+
43+
public class Issue14471Tab2Content : ContentPage
44+
{
45+
public Issue14471Tab2Content()
46+
{
47+
Content = new Button()
48+
{
49+
AutomationId = "switchToTab1Button",
50+
Text = "Switch to tab 1",
51+
Command = new Command(async () => await Shell.Current.GoToAsync("//tab1"))
52+
};
53+
}
54+
}
55+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
3+
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
4+
x:Class="Maui.Controls.Sample.Issues.Issue6625">
5+
<ContentPage.Content>
6+
<Grid x:Name="Container"
7+
RowDefinitions="Auto,*">
8+
<Label x:Name="Label"
9+
Grid.Row="0"
10+
LineBreakMode="CharacterWrap"
11+
HorizontalOptions="Center"
12+
VerticalOptions="Start"
13+
Padding="8,16"/>
14+
<Image x:Name="Image"
15+
Grid.Row="1"
16+
BackgroundColor="WhiteSmoke"
17+
Aspect="AspectFit"
18+
VerticalOptions="Center"
19+
HorizontalOptions="Center" />
20+
</Grid>
21+
</ContentPage.Content>
22+
</ContentPage>
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
using System;
2+
using System.Collections;
3+
using System.Linq;
4+
using Microsoft.Maui.Controls;
5+
using Microsoft.Maui.Controls.Xaml;
6+
using Microsoft.Maui.Platform;
7+
8+
namespace Maui.Controls.Sample.Issues
9+
{
10+
[Issue(IssueTracker.Github, 6625, "Changing image source on Android causes flicker between images", PlatformAffected.Android)]
11+
public partial class Issue6625 : ContentPage
12+
{
13+
readonly ImageSource[] imageSources =
14+
[
15+
"https://64.media.tumblr.com/14cb5aa197e5d9ca6479d955f68344f0/cb28a32e384437f2-07/s540x810/005508be5849eff8fda5b0ebda23f9fcbede164e.jpg",
16+
"https://64.media.tumblr.com/d66b1709639c23315d26c0a4e977c399/1fb3e31c5e63625d-81/s540x810/2e9a893ec8c1f65a4b9fb8f1264b4fb76914ced8.jpg",
17+
null,
18+
"https://64.media.tumblr.com/e7db300b8248c0a7f4c66884032c9414/8f89498ebe784d1c-4e/s540x810/90835c041547bf2936ee249c5c5e1b4eddd589a3.jpg",
19+
"https://64.media.tumblr.com/fd901060692d2c9ca6f05ddc58e28e5d/2db73c9c5730d87c-e2/s500x750/092c9dc3cff5150acf24bf22a9e61b9719d9ccd6.jpg",
20+
"https://64.media.tumblr.com/c011aa547a45ac6fe47c46beeb290596/eafc76ded66f16f9-dc/s500x750/5ed923ed086296a2bd41cab3106079eb7addc2d0.jpg",
21+
"https://this.is.a.broken.url",
22+
new FontImageSource { FontFamily = "FA", Glyph = "\uf111", Size = 200, Color = Colors.Blue },
23+
new FontImageSource { FontFamily = "FA", Glyph = "\uf192", Size = 200, Color = Colors.Blue },
24+
new FontImageSource { FontFamily = "FA", Glyph = "\uf111", Size = 200, Color = Colors.Blue },
25+
new FontImageSource { FontFamily = "FA", Glyph = "\uf192", Size = 200, Color = Colors.Blue }
26+
];
27+
28+
int imageNo = -1;
29+
30+
public Issue6625()
31+
{
32+
InitializeComponent();
33+
Container.GestureRecognizers.Add(new TapGestureRecognizer { Command = new Command(NextImage) });
34+
NextImage();
35+
}
36+
37+
void NextImage()
38+
{
39+
imageNo = (imageNo + 1) % imageSources.Length;
40+
41+
ImageSource imageSource = imageSources[imageNo];
42+
Label.Text = imageSource switch
43+
{
44+
UriImageSource uri => uri.Uri.ToString(),
45+
FontImageSource font => $"Glyph: {Convert.ToBase64String(font.Glyph.Select(c => (byte)c).ToArray())}",
46+
_ => imageSource?.GetType().Name ?? "null"
47+
};
48+
Image.Source = imageSource;
49+
}
50+
}
51+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#if !MACCATALYST // VerifyScreenshot() is not supported on MacCatalyst
2+
using NUnit.Framework;
3+
using UITest.Appium;
4+
using UITest.Core;
5+
6+
namespace Microsoft.Maui.TestCases.Tests.Issues;
7+
8+
public class Issue14471 : _IssuesUITest
9+
{
10+
public Issue14471(TestDevice device) : base(device){ }
11+
12+
public override string Issue => "Image can disappear when going back to the page";
13+
14+
[Test]
15+
[Category(UITestCategories.Image)]
16+
public void ImageDoesntDisappearWhenNavigatingBack()
17+
{
18+
App.WaitForElement("image");
19+
App.Click("switchToTab2Button");
20+
App.WaitForElement("switchToTab1Button");
21+
App.Click("switchToTab1Button");
22+
App.WaitForElement("image");
23+
24+
// The test passes if image is loaded
25+
VerifyScreenshot();
26+
}
27+
}
28+
#endif
30.3 KB
Loading
129 KB
Loading

src/Core/src/Handlers/Image/ImageHandler.Android.cs

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
using System.Threading.Tasks;
22
using Android.Graphics.Drawables;
3+
using Android.Views;
34
using Android.Widget;
45
using AndroidX.AppCompat.Widget;
5-
using Google.Android.Material.Button;
66

77
namespace Microsoft.Maui.Handlers
88
{
99
public partial class ImageHandler : ViewHandler<IImage, ImageView>
1010
{
11+
1112
protected override ImageView CreatePlatformView()
1213
{
1314
var imageView = new AppCompatImageView(Context);
@@ -21,9 +22,15 @@ protected override ImageView CreatePlatformView()
2122
return imageView;
2223
}
2324

25+
protected override void ConnectHandler(ImageView platformView)
26+
{
27+
platformView.ViewAttachedToWindow += OnPlatformViewAttachedToWindow;
28+
}
29+
2430
protected override void DisconnectHandler(ImageView platformView)
2531
{
2632
base.DisconnectHandler(platformView);
33+
platformView.ViewAttachedToWindow -= OnPlatformViewAttachedToWindow;
2734
SourceLoader.Reset();
2835
}
2936

@@ -54,10 +61,17 @@ await handler
5461
.SourceLoader
5562
.UpdateImageSourceAsync();
5663

64+
65+
// This indicates that the image has finished loading
66+
// So, now if the attached event fires again then we need to see if Glide has cleared the image out
67+
handler.SourceLoader.CheckForImageLoadedOnAttached = true;
68+
5769
// Because this resolves from a task we should validate that the
5870
// handler hasn't been disconnected
5971
if (handler.IsConnected())
72+
{
6073
handler.UpdateValue(nameof(IImage.IsAnimationPlaying));
74+
}
6175
}
6276

6377
public override void PlatformArrange(Graphics.Rect frame)
@@ -79,12 +93,44 @@ public override void PlatformArrange(Graphics.Rect frame)
7993
base.PlatformArrange(frame);
8094
}
8195

96+
internal static void OnPlatformViewAttachedToWindow(IImageHandler imageHandler)
97+
{
98+
99+
// Glide will automatically clear out the image if the Fragment or Activity is destroyed
100+
// So we want to reload the image here if it's supposed to have an image
101+
if (imageHandler.SourceLoader.CheckForImageLoadedOnAttached &&
102+
imageHandler.PlatformView.Drawable is null &&
103+
imageHandler.VirtualView.Source is not null)
104+
{
105+
imageHandler.SourceLoader.CheckForImageLoadedOnAttached = false;
106+
imageHandler.UpdateValue(nameof(IImage.Source));
107+
}
108+
}
109+
110+
void OnPlatformViewAttachedToWindow(object? sender, View.ViewAttachedToWindowEventArgs e)
111+
{
112+
if (sender is not View platformView)
113+
{
114+
return;
115+
}
116+
117+
if (!this.IsConnected())
118+
{
119+
platformView.ViewAttachedToWindow -= OnPlatformViewAttachedToWindow;
120+
return;
121+
}
122+
123+
OnPlatformViewAttachedToWindow(this);
124+
}
125+
82126
partial class ImageImageSourcePartSetter
83127
{
84128
public override void SetImageSource(Drawable? platformImage)
85129
{
86130
if (Handler?.PlatformView is not ImageView image)
131+
{
87132
return;
133+
}
88134

89135
image.SetImageDrawable(platformImage);
90136
}

src/Core/src/Handlers/ImageButton/ImageButtonHandler.Android.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ protected override void DisconnectHandler(ShapeableImageView platformView)
2323
platformView.FocusChange -= OnFocusChange;
2424
platformView.Click -= OnClick;
2525
platformView.Touch -= OnTouch;
26+
platformView.ViewAttachedToWindow -= OnPlatformViewAttachedToWindow;
2627

2728
base.DisconnectHandler(platformView);
2829

@@ -34,6 +35,7 @@ protected override void ConnectHandler(ShapeableImageView platformView)
3435
platformView.FocusChange += OnFocusChange;
3536
platformView.Click += OnClick;
3637
platformView.Touch += OnTouch;
38+
platformView.ViewAttachedToWindow += OnPlatformViewAttachedToWindow;
3739

3840
base.ConnectHandler(platformView);
3941
}
@@ -90,6 +92,22 @@ void OnClick(object? sender, EventArgs e)
9092
VirtualView?.Clicked();
9193
}
9294

95+
void OnPlatformViewAttachedToWindow(object? sender, View.ViewAttachedToWindowEventArgs e)
96+
{
97+
if (sender is not View platformView)
98+
{
99+
return;
100+
}
101+
102+
if (!this.IsConnected())
103+
{
104+
platformView.ViewAttachedToWindow -= OnPlatformViewAttachedToWindow;
105+
return;
106+
}
107+
108+
ImageHandler.OnPlatformViewAttachedToWindow(this);
109+
}
110+
93111
partial class ImageButtonImageSourcePartSetter
94112
{
95113
public override void SetImageSource(Drawable? platformImage)

0 commit comments

Comments
 (0)