Issue
Goal: I have 10 images, 4 of which are special. I want the 4 special images to fit within the entire view of the screen. I think I've actually done this, but it does not account for the tabs across the bottom of the app from AppShell.xaml. So it appears to fit properly, but the fourth image is partially behind the tabs and forces the user to scroll.
I had two collection views on the page and it caused the second one to scroll within its own frame, which was strange. So to solve it, I put the 4 special images in the header template of the collection view.
Here's what I have, followed with what I want it to do:
<RefreshView x:DataType="local:SpotlightViewModel" Command="{Binding LoadItemsCommand}" IsRefreshing="{Binding IsBusy, Mode=TwoWay}" Margin="0, 0, 0, 0">
<ScrollView InputTransparent="True" >
<Grid >
<CollectionView x:Name="SpotletsListView"
ItemsSource="{Binding Spotlets}"
EmptyView=""
SelectionMode="None">
<CollectionView.ItemsLayout>
<LinearItemsLayout SnapPointsAlignment="Start" SnapPointsType="Mandatory" Orientation="Vertical"></LinearItemsLayout>
</CollectionView.ItemsLayout>
<CollectionView.Header>
<CollectionView ItemsSource="{Binding SpotlightSpotlets}">
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout Padding="0, .1" x:DataType="model:Spotlet" HorizontalOptions="Center" VerticalOptions="FillAndExpand">
<Image Aspect="AspectFit" HorizontalOptions="Center" Source="{Binding Url, Converter={StaticResource ImageSourceConverter}}" ></Image>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</CollectionView.Header>
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout Padding="0, .1" x:DataType="model:Spotlet" HorizontalOptions="Center">
<Image Aspect="AspectFit" HorizontalOptions="Center" Source="{Binding Url, Converter={StaticResource ImageSourceConverter}}" ></Image>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</Grid>
</ScrollView>
</RefreshView>
What I want is simple. Making it happen for a newbie to Xamarin like myself is not so simple.
- First four images to fill the screen but account for the tabs at the bottom
- All subsequent images to fill the screen
- When scrolling, snap the image into place so two images cannot be seen at the same time
- When I reach the bottom allow continued scrolling to go back to the top
Maybe I need to use carousel for part of this, which I tried, but also ran into some strange behavior. Could someone give me a boilerplate that may help?
Page 1:
[ -- this is an image -- ]
[ -- this is an image -- ]
[ -- this is an image -- ]
[ -- this is an image -- ]
Page 2:
[ -- one image, fills -- ]
Page 3:
[ -- one image, fills --]
When I hit the bottom, I want it to infinitely scroll the same items again. I have two separate collections in the code. One which holds the 4, another which holds the rest.
Solution
Some of the answers were staring at me from the examples and I didn't even realize it. I can't full take credit for the answer, but I'll add the relevant snippets to give someone else a head start, with hopefully more precise understanding of what is needed.
Source: https://github.com/xamarin/xamarin-forms-samples/tree/main/Templates/DataTemplateSelector
Although you can abstract these custom templates into complex views with their own .xaml file, this example is all that I needed in my particular case.
First, I created a class which was in charge of selecting the right template for a particular item:
public class SpotlightTemplateSelector : DataTemplateSelector
{
public DataTemplate SingleItemTemplate { get; set; }
public DataTemplate SpotlightItemTemplate { get; set; }
protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
{
return ((Spotlet) item).IsSpotlight ? SpotlightItemTemplate : SingleItemTemplate;
}
}
My SpotlightItemTemplate is the one where I wanted to display multilple items within a single frame of the carousel. Now this might not be the best way to go about it, but here's what I did.
My model:
public class Spotlet
{
public string Url { get; set; }
public bool IsSpotlight { get; set; }
public List<Spotlet> Spotlets { get; set; }
}
The key here is the last prop Spotlets. When I fetched all my data, I created a new instance of Spotlet class to represent the first carousel item, and pushed every item that had IsSpotlight to that items Spotlets. So yes, a meaningless instance other than its child spotlets.
I then pushed to my main list the remaining items where IsSpotlight is false. Now there could be a much better way to handle this, but I'm new to Xamarin this is what worked for me (hint: Tell me a better way).
So at this point I have a list of spotlets, with the first one having nested spotlets and its IsSpotlight prop set to true.
Now, within my XAML file, I have my carousel view:
<CarouselView x:Name="SpotletsCarousel"
ItemsSource="{Binding Spotlets}"
ItemTemplate="{StaticResource spotlightDataTemplateSelector}">
</CarouselView>
It's referencing a static resource, which is local to the same file and a child of the wrapping <ContentPage />
<ContentPage.Resources>
<ResourceDictionary>
<DataTemplate x:Key="SpotletItemViewTemplate">
<StackLayout x:DataType="model:Spotlet">
<Image Aspect="AspectFill" Source="{Binding Url, Converter={StaticResource ImageSourceConverter}}" ></Image>
</StackLayout>
</DataTemplate>
<DataTemplate x:Key="SpotletSpotlightItemViewTemplate" x:DataType="model:Spotlet">
<CollectionView x:Name="SpotletsListView" ItemsSource="{Binding Spotlets }">
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout Padding="0, .1" x:DataType="model:Spotlet" HorizontalOptions="Center" VerticalOptions="FillAndExpand">
<Image Aspect="AspectFit" HorizontalOptions="Center" Source="{Binding Url, Converter={StaticResource ImageSourceConverter}}" ></Image>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</DataTemplate>
<local:SpotlightTemplateSelector x:Key="spotlightDataTemplateSelector" SingleItemTemplate="{StaticResource SpotletItemViewTemplate}" SpotlightItemTemplate="{StaticResource SpotletSpotlightItemViewTemplate}" />
</ResourceDictionary>
</ContentPage.Resources>
My carousel item template is referencing the spotlightDataTemplateSelector defined in local resources (i.e. x:Key="spotlightDataTemplateSelector").
This calls the method in the SpotlightTemplateSelector class I created, which does a simple ternary to return which template to use.
Notice in my multiple items template, I have a nested collection view within it. The rest, just a single image.
Answered By - user1447679
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.