Performance and layout in ListView with DataTemplate containing images

LoossSSLoossSS FRMember
edited November 9 in Xamarin.Forms

Hi everyone,

I’m currently making an app for iOS and Android using Xamarin Forms. The whole app is working fine except one menu which is giving me some hard time. It’s a list of pictures, shown in a card view style, with a title and a text. Nothing really crazy.
The goal is to get this (it's a screenshot from my app using random pictures). I just registered to the forum so I can't post links but it's there if you remove the spaces :
us.v-cdn.net/5019960/uploads/editor/e8/vnuwb14gedoy .jpeg

Doing this menu, I faced many problems. Every time I tried a new approach, I faced new problems.

Performance :
On iOS, my list view is fine, and never lags, even with many pictures (approximately 30 right now, but I plan to add more). But on Android, it starts to lag and the picture stop showing after I add "too many" (more than 10 is enough to have the menu completely not working). The pictures are embedded in the app, I thought maybe reducing size would help. I've made them "800" max (height or width) but that didn't help. I then thought that the layout was "too complicated". I made a test with a ListView containing only this in the data template :

<StackLayout Orientation="Vertical">
       <Image HorizontalOptions="Fill" Source="{Binding ImageSource}" Margin="0" Aspect="AspectFit"/>
</StackLayout>

And it still lags.. I guess that the layout is not an issue. So my question is : is really Android not capable of showing a list of 10/20 pictures side by side using a data template ?!
I've then tried using a third party (FFImageLoading). I have better results. But it's completely destroying the layout on iOS...

Bugs
I have many bugs (render bugs). I tried to solve all of them but still, in that screenshot above, there are already 2 I can see directly :

  • Title 3 is fine, when Title 4 is few pixels above the picture when it shouldn't
  • The place reserved for the description is bigger (2/3 lines) when it should be like the description of image 4

Layout
How I did, and 2 main ways I tried :
The current way (from the screenshot) is this :

<Frame Style="{StaticResource CardStyle}" Padding="0">
    <StackLayout>
        <Image HorizontalOptions="Fill" Source="{Binding ImageSource}" Margin="0" Aspect="AspectFit"/>
        <Frame HasShadow="false" Padding="0" Margin="0,-50,0,0" HeightRequest="45" CornerRadius="0" BackgroundColor="{StaticResource TranslucidFrameBackgroundColor}"
               HorizontalOptions="FillAndExpand">
            <Label Text="{Binding Title}" Margin="10" Style="{StaticResource LightLabelStyle}" FontSize="18" VerticalTextAlignment="Center"/>
        </Frame>
        <Label Text="{Binding Text}" Margin="5"/>
    </StackLayout>
</Frame>

I hate the "-50" margin which I had to put on the title's frame. That's why I sometimes get few pixel up or down and it's not always perfectly aligned with the photo.
I can't explain myself why the description (last label) is not always having a perfect size (or at least always the same, when the text is the same)...

Of course you will say "you should use a Grid and not a StackLayout, the render will be better, you won't need the -50, and the performance is better with Grid than StackLayout when you use images on Xamarin" (I read that everywhere in the forum at least).
Well, guess what :smiley: that's what I did first and it was a disaster !

I give you the code I used :

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="auto"/>
        <RowDefinition Height="auto"/>
    </Grid.RowDefinitions>

    <Image Grid.Row="0" HorizontalOptions="Fill" Source="{Binding ImageSource}" Margin="0" Aspect="AspectFit"/>
    <Frame Grid.Row="0" VerticalOptions="End" HasShadow="false" Padding="0" Margin="0" HeightRequest="45" CornerRadius="0" BackgroundColor="{StaticResource TranslucidFrameBackgroundColor}"
           HorizontalOptions="FillAndExpand">
        <Label Text="{Binding Title}" Margin="10" Style="{StaticResource LightLabelStyle}" FontSize="18" VerticalTextAlignment="Center"/>
    </Frame>
    <Label Grid.Row="1" Text="{Binding Text}" Margin="5"/>
</Grid>

I very much prefer this version of the template. It's much more clear and precise and doesn't need some weird margin to make it work.
But look at the result :
us.v-cdn.net/5019960/uploads/editor/cy/v2ifoy4xpy2k .jpeg

From what I understand, iOS (not Android), using Xamarin Forms at least, gets the size of the grid using the original picture, not the "resized to fit the screen" size of the picture... So my grids are huge and we don't even see the cards anymore. The gaps between 2 pictures gets bigger and bigger every time you add a picture...
I saw some tweaks about forcing resize using code behind but I really don't want to do that as I find it plain ugly.

Can anyone tell me, I'm "new" to Xamarin Forms, I come from many years developing using WPF / xaml : is there no way to do such a simple template using Xamarin Forms ? I saw maaaaany threads on the forums (here and stackoverflow) concerning this particular subject (pictures in data template). I tried many other solutions but didn't want to pollute this already huge question. I tried forcing a fixed size, but I hope you guessed it, that's not what I want to do as my pictures can be portrait or landscape, big or small..

Please don't ask me if I put "HasUnevenRows" to true (yes I did :wink: ).

Please, please, please help me :blush: Find a solution, tell me "omg you're so stupid you just forgot * add something here * and it would work" : that would make me so happy :smiley:

Best Answer

  • LoossSSLoossSS FRMember
    Accepted Answer

    Thank you guys for your answers. So at the end, I had to settle down for putting a grid with fixed height which is solving both problems :

    • Layout stays consistent
    • Android doesn't lag anymore as it doesn't have to calculate the height for each row

    It's really sad that Xamarin is not that mature about listviews. It's such a powerful layout, it should be much better handled...

Answers

  • LewisKLewisK USUniversity ✭✭
    edited November 10

    <Grid.RowDefinitions> <RowDefinition Height="auto"/> <RowDefinition Height="auto"/> </Grid.RowDefinitions>

    If you are using a listview you can specify the height for the images, this helps prevent more layout cycles being triggered by android having to calculate the proper values.

    Are these images shared between the list items or unique per item? If they are shared then if you use recycle element you should be able to prevent this from being reloaded per item and reuse them.

    Listviews generally have bad preformance on xamarin forms, there is a page dedicated to it here - https://developer.xamarin.com/guides/xamarin-forms/user-interface/listview/performance/

  • CharwakaCharwaka INMember ✭✭✭

    Hi @LoossSS

    Here are few Guidelines to optimize Listview performance in Xamarin Forms
    ->Make sure you kept all image in respective folders like
    /drawable-ldpi For low density screens
    /drawable-mdpi For medium density screens
    /drawable-hdpi For high resolution screens
    /drawable-xhdpi For extra high resolution screens

    ->Make sure you added

    ->Its better to use C# Datatemplate Rather than Writing in XAML for ListView

    ->if you are using please use https://github.com/luberda-molinet/FFImageLoading ,it really a great Nuget you must have

    ->Please have a look at https://github.com/georgejecook/xamarinFastCell

    Please Mark as Answer if this work for you
    
  • LoossSSLoossSS FRMember
    Accepted Answer

    Thank you guys for your answers. So at the end, I had to settle down for putting a grid with fixed height which is solving both problems :

    • Layout stays consistent
    • Android doesn't lag anymore as it doesn't have to calculate the height for each row

    It's really sad that Xamarin is not that mature about listviews. It's such a powerful layout, it should be much better handled...

  • DanielLDanielL PLInsider ✭✭✭✭

    I've then tried using a third party (FFImageLoading). I have better results. But it's completely destroying the layout on iOS...

    FFImageLoading has a regression since 2.2.4 which caused CachedImage to always fill entire space available (different behaviour than Image. It's now fixed, but as it was included in many versions it may be a breaking change for some. That's why I didn't enable that fix by default. You can do it manually with:

    CachedImage.FixedOnMeasureBehavior = true;

    Also for Xamarin.Forms >= 2.4 please set: CachedImage.FixedAndroidMotionEventHandler = true if experiencing any gesture recognizers problems on Android.

    I'll include it as a default in next major version.

    Xamarin.Forms (Android) now uses fast renderers by default if possible (correct XF version). You can enable / disable fast renderers by CachedImageRenderer.Init(enableFastRenderer: [true/false] override.

Sign In or Register to comment.