You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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>

问题原因

  1. 触摸事件拦截错误:自定义渲染器中重写OnInterceptTouchEvent和OnTouchEvent时,当IsScrollEnabled为false直接返回false,导致触摸事件无法传递到列表项,选中状态的触发逻辑被阻断。
  2. 视觉状态目标错误:现有样式设置的是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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 09:55:01