Xamarin.Forms自定义CollectionView渲染器:选中项背景色失效
问题与解决方案:自定义CollectionViewExt选中项背景色不生效
问题描述
在Xamarin.Forms开发中,为实现特定场景下禁用滚动的需求,我创建了带自定义渲染器的CollectionViewExt控件,但该控件无法自动修改选中项的背景色,而原生CollectionView的选中项着色功能正常。
现有代码
平台专属资源文件(Android)全局样式
<style name="MainTheme" parent="MainTheme.Base"> <item name="colorAccent">@color/colorAccent</item> <item name="android:colorPrimaryDark">#00FF00</item> <item name="android:colorPressedHighlight">@color/ListViewSelected</item> <item name="android:colorLongPressedHighlight">@color/ListViewHighlighted</item> <item name="android:colorFocusedHighlight">@color/ListViewSelected</item> <item name="android:colorActivatedHighlight">@color/ListViewSelected</item> <item name="android:activatedBackgroundIndicator">@color/ListViewSelected</item> <item name="android:datePickerDialogTheme">@style/CustomDatePickerDialog</item> </style>
自定义渲染器代码
[assembly: ExportRenderer(typeof(CollectionViewExt), typeof(CollectionViewCustomRenderer))] namespace Vendo.Android.Renderers { internal class CollectionViewCustomRenderer: CollectionViewRenderer { public CollectionViewCustomRenderer(Context context) : base(context) { } public override bool OnInterceptTouchEvent(MotionEvent ev) { if (Element is CollectionViewExt collectionView && collectionView.IsScrollEnabled) return base.OnInterceptTouchEvent(ev); else return false; } public override bool OnTouchEvent(MotionEvent ev) { if (Element is CollectionViewExt collectionView && collectionView.IsScrollEnabled) return base.OnTouchEvent(ev); else return false; } } }
XAML页面代码
<controls:CollectionViewExt x:Name="cvArticoli" VerticalScrollBarVisibility="Always" VerticalOptions="FillAndExpand" IsScrollEnabled="{Binding IsScrollEnabled}" Style="{StaticResource CollExtStyle}" ItemsSource="{Binding ListaArticoli}" SelectedItem="{Binding ArticoloModelCorrente, Mode=TwoWay}" IsVisible="{Binding VisualizzaArticoli}" ClassId="{Binding CatalogoVisualizzato}" ItemTemplate="{StaticResource ArticoliTemplateSelector}" RemainingItemsThreshold="2" RemainingItemsThresholdReachedCommand="{Binding LoadMoreArticoliCommand}"> <controls:CollectionViewExt.EmptyView> <StackLayout> <Label Margin="0,10" HorizontalOptions="Center" Text="Nessun articolo in lista." TextColor="Gray"/> </StackLayout> </controls:CollectionViewExt.EmptyView> <controls:CollectionViewExt.ItemsLayout> <MultiBinding> <Binding Path="CatalogoVisualizzato"/> <Binding Path="ColonneCatalogo"/> <MultiBinding.Converter> <converters:BoolToItemsLayoutConverter/> </MultiBinding.Converter> </MultiBinding> </controls:CollectionViewExt.ItemsLayout> </controls:CollectionViewExt>
App.xaml视觉状态样式
<Style x:Key="CollExtStyle" TargetType="controls:CollectionViewExt"> <Setter Property="VisualStateManager.VisualStateGroups"> <VisualStateGroupList> <VisualStateGroup Name="CommonStates"> <VisualState Name="Normal"> <VisualState.Setters> <Setter Property="BackgroundColor" Value="White" /> </VisualState.Setters> </VisualState> <VisualState Name="Selected"> <VisualState.Setters> <Setter Property="BackgroundColor" Value="{StaticResource Highlight}" /> </VisualState.Setters> </VisualState> <VisualState Name="Disabled"> <VisualState.Setters> <Setter Property="BackgroundColor" Value="{StaticResource Disabled}"/> </VisualState.Setters> </VisualState> </VisualStateGroup> </VisualStateGroupList> </Setter> </Style>
问题原因
- 触摸事件拦截错误:自定义渲染器中重写
OnInterceptTouchEvent和OnTouchEvent时,当IsScrollEnabled为false直接返回false,导致触摸事件无法传递到列表项,选中状态的触发逻辑被阻断。 - 视觉状态目标错误:现有样式设置的是
CollectionViewExt本身的BackgroundColor,而原生CollectionView的选中着色是针对列表项的,而非控件容器。
解决方案
1. 修复自定义渲染器的滚动禁用逻辑
移除触摸事件拦截的重写,改为直接控制底层RecyclerView的滚动状态,确保触摸事件能传递到列表项:
[assembly: ExportRenderer(typeof(CollectionViewExt), typeof(CollectionViewCustomRenderer))] namespace Vendo.Android.Renderers { internal class CollectionViewCustomRenderer : CollectionViewRenderer { public CollectionViewCustomRenderer(Context context) : base(context) { } protected override void OnElementChanged(ElementChangedEventArgs<ItemsView> e) { base.OnElementChanged(e); if (e.NewElement is CollectionViewExt collectionView) { UpdateScrollEnabled(collectionView.IsScrollEnabled); collectionView.PropertyChanged += CollectionView_PropertyChanged; } } private void CollectionView_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) { if (e.PropertyName == nameof(CollectionViewExt.IsScrollEnabled)) { if (sender is CollectionViewExt collectionView) { UpdateScrollEnabled(collectionView.IsScrollEnabled); } } } private void UpdateScrollEnabled(bool isEnabled) { if (Control is AndroidX.RecyclerView.RecyclerView recyclerView) { // 禁用RecyclerView的滚动 recyclerView.LayoutManager?.SetScrollEnabled(isEnabled); // 保持控件可交互,确保触摸事件能传递到子项 recyclerView.SetEnabled(isEnabled); } } } }
2. 为列表项设置选中状态样式
修改视觉状态目标为列表项,可通过以下两种方式实现:
方式一:在ItemTemplate中添加视觉状态
在ArticoliTemplateSelector对应的模板里添加VisualStateManager:
<DataTemplate x:Key="ArticoloTemplate"> <Grid> <!-- 列表项内容 --> <VisualStateManager.VisualStateGroups> <VisualStateGroup Name="CommonStates"> <VisualState Name="Normal"> <VisualState.Setters> <Setter Property="BackgroundColor" Value="White" /> </VisualState.Setters> </VisualState> <VisualState Name="Selected"> <VisualState.Setters> <Setter Property="BackgroundColor" Value="{StaticResource Highlight}" /> </VisualState.Setters> </VisualState> </VisualStateGroup> </VisualStateManager.VisualStateGroups> </Grid> </DataTemplate>
方式二:设置全局ItemStyle
在App.xaml中定义列表项的样式,然后绑定到CollectionViewExt:
<Style x:Key="CollExtItemStyle" TargetType="ViewCell"> <Setter Property="VisualStateManager.VisualStateGroups"> <VisualStateGroupList> <VisualStateGroup Name="CommonStates"> <VisualState Name="Normal"> <VisualState.Setters> <Setter Property="BackgroundColor" Value="White" /> </VisualState.Setters> </VisualState> <VisualState Name="Selected"> <VisualState.Setters> <Setter Property="BackgroundColor" Value="{StaticResource Highlight}" /> </VisualState.Setters> </VisualState> </VisualStateGroup> </VisualStateGroupList> </Setter> </Style>
然后在XAML页面的CollectionViewExt中添加:
<controls:CollectionViewExt ... ItemStyle="{StaticResource CollExtItemStyle}">
内容的提问来源于stack exchange,提问作者Innova
相关产品推荐
相关产品推荐

