【问题标题】:How can I stop the clicking on a ViewCell from changing the background color for a brief time?如何在短时间内停止单击 ViewCell 更改背景颜色?
【发布时间】:2018-09-25 04:42:22
【问题描述】:

我有这个 XAML 代码:

<TableView x:Name="tableView" Intent="Settings" HasUnevenRows="True">
   <TableSection>
      <TableSection.Title>
         Card Selection
      </TableSection.Title>
      <ViewCell Height="50">
         <Grid>
            <Grid x:Name="deselectGridLink" VerticalOptions="CenterAndExpand" Padding="20, 0">
               <Label TextColor="Blue" Style="{DynamicResource ListItemTextStyle}" x:Name="deselectLink" HorizontalOptions="StartAndExpand" VerticalOptions="Center" Text="Deselect All" />
            </Grid>
            <Grid x:Name="deselectGridLabel" VerticalOptions="CenterAndExpand" Padding="20, 0">
               <Label TextColor="Silver" Style="{DynamicResource ListItemTextStyle}" x:Name="deselectLabel" HorizontalOptions="StartAndExpand" VerticalOptions="Center" Text="Deselect All" />
            </Grid>
         </Grid>
       </ViewCell>
       <ViewCell Height="50">
          <Grid x:Name="selectGridLink" VerticalOptions="CenterAndExpand" Padding="20, 0">
             <Label TextColor="Blue" Style="{DynamicResource ListItemTextStyle}" x:Name="selectLink" HorizontalOptions="StartAndExpand" VerticalOptions="Center" Text="Select All" />
           </Grid>
        </ViewCell>
   </TableSection>
</TableView>

当我的代码的其他部分调用:SetPageDetails() 然后网格中的标签更改为链接或链接更改为标签。因此,当它是一个标签时,我希望没有背景 flash 事件并且没有调用任何操作。

我像这样附加了一个点击手势识别器。请注意,它全部在一行上,但涵盖了两行,因此在 SO 问题中更明显:

deselectGridLink.GestureRecognizers
      .Add(NewTapGestureForUpdateCategories(false));

    private TapGestureRecognizer NewTapGestureForUpdateCategories(bool val)
    {
        return new TapGestureRecognizer()
        {
            Command = new Command(() =>
            {
                App.DB.UpdateAllCategoryGroups(val);
                App.DB.UpdateAllCategories(val);
                GetPageData();
                RemoveTableViewClickSection();
                tableView.Root.Add(CreateTableSection());
            })
        };
    }

当用户在取消选择网格链接网格可见时单击该行时:

  • deselectGridLink 可见性设置为 false
  • deselectGridLabel 可见性设置为 true

    private void SetPageDetails()
    {
        Title = App.cardCountForSelectedCategories + (App.cardCountForSelectedCategories == 1 ? " Card Selected" : " Cards Selected");
        if (App.cardCountForSelectedCategories == 0)
        {
            deselectGridLink.IsVisible = false;
            deselectGridLabel.IsVisible = true;
        }
        else
        {
            deselectGridLink.IsVisible = true;
            deselectGridLabel.IsVisible = false;
        }
    }
    

这样做的效果是网格链接文字会变成银色,链接变成标签。

但是,即使在单击标签时它是灰色标签,单击标签时仍然会出现从白色到深色的短暂背景行颜色变化。我认为这只是视图单元的工作方式。

有没有办法阻止这种情况发生?

【问题讨论】:

    标签: xamarin xamarin.forms


    【解决方案1】:

    编辑 1 - 根据问题更新更新答案。即添加对在启用/禁用高亮模式之间切换的支持。

    EDIT 2 - 重组答案并添加更多细节。

    选项 1:通过IsEnabled 启用/禁用视图单元

    最简单的选择是使用IsEnabled 属性,它反过来启用/禁用背景闪光行为。这种方法的唯一缺点是它还会禁用子控件上的点击,即如果父视图单元的IsEnabledfalse,则不会触发点击事件/手势识别器。

    例如:

    XAML

    <!-- Add name attribute to view-cell -->
    <ViewCell x:Name="deselectCell" ..>
        <Grid>
            <Grid x:Name="deselectGridLink" ..
        ....
    </ViewCell>
    

    代码隐藏

    private void SetPageDetails()
    {
        if (App.cardCountForSelectedCategories == 0)
        {
            deselectCell.IsEnabled = false; //disable background flash
            ...
        }
        else
        {
            deselectCell.IsEnabled = true;
            ...
        }
    }
    

    建议 1 - 使用数据绑定和触发器

    您可以使用触发器和数据绑定,而不是在代码隐藏中控制每个标签的可见性(视图模型将具有IsDeselectEnabled 属性):

    <ViewCell IsEnabled="{Binding IsDeselectEnabled}" Height="50">
        <Label Margin="20,0,20,0" Style="{DynamicResource ListItemTextStyle}" HorizontalOptions="StartAndExpand" VerticalOptions="Center" Text="Deselect All">
            <Label.Triggers>
                <DataTrigger TargetType="Label" Binding="{Binding IsDeselectEnabled}" Value="true">
                    <Setter Property="TextColor" Value="Blue" />
                </DataTrigger>
                <DataTrigger TargetType="Label" Binding="{Binding IsDeselectEnabled}" Value="false">
                    <Setter Property="TextColor" Value="Silver" />
                </DataTrigger>
            </Label.Triggers>
        </Label>
    </ViewCell>
    

    建议 2 - 使用以视图为源的触发器

    <ViewCell x:Name="deselectCell" Height="50">
        <Label Margin="20,0,20,0" Style="{DynamicResource ListItemTextStyle}" HorizontalOptions="StartAndExpand" VerticalOptions="Center" Text="Deselect All">
            <Label.Triggers>
                <DataTrigger TargetType="Label" Binding="{Binding IsEnabled, Source={x:Reference deselectCell}}" Value="true">
                    <Setter Property="TextColor" Value="Blue" />
                </DataTrigger>
                <DataTrigger TargetType="Label" Binding="{Binding IsEnabled, Source={x:Reference deselectCell}}" Value="false">
                    <Setter Property="TextColor" Value="Silver" />
                </DataTrigger>
            </Label.Triggers>
        </Label>
    </ViewCell>
    

    选项 2:启用/禁用突出显示,但允许点击

    要在切换ViewCell 的背景突出显示行为时允许点击,我们需要实现平台渲染器。

    对于 iOS,我们可以使用 SelectionStyle 来切换此行为,而对于 android,我们可以使用 Clickable 属性。

    共享控制:

    public class CustomViewCell : ViewCell
    {
        public static readonly BindableProperty AllowHighlightProperty =
            BindableProperty.Create(
                "AllowHighlight", typeof(bool), typeof(CustomViewCell),
                defaultValue: true);
    
        public bool AllowHighlight
        {
            get { return (bool)GetValue(AllowHighlightProperty); }
            set { SetValue(AllowHighlightProperty, value); }
        }
    }
    

    iOS 渲染器:

    [assembly: ExportRenderer(typeof(CustomViewCell), typeof(CustomViewCellRenderer))]
    namespace SampleApp.iOS
    {
        public class CustomViewCellRenderer : ViewCellRenderer
        {
            UITableViewCell _nativeCell;
    
            //get access to the associated forms-element and subscribe to property-changed
            public override UITableViewCell GetCell(Cell item, UITableViewCell reusableCell, UITableView tv)
            {
                _nativeCell = base.GetCell(item, reusableCell, tv);
    
                var formsCell = item as CustomViewCell;
    
                if (formsCell != null)
                {
                    formsCell.PropertyChanged -= OnPropertyChanged;
                    formsCell.PropertyChanged += OnPropertyChanged;
                }
    
                //and, update the style 
                SetStyle(formsCell);
    
                return _nativeCell;
            }
    
            void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
            {
                var formsCell = sender as CustomViewCell;
                if (formsCell == null)
                    return;
                //TODO: Trying to find a nicer and more robust way to dispose and unsubscribe :(
                if (_nativeCell == null)
                    formsCell.PropertyChanged -= OnPropertyChanged;
    
                if (e.PropertyName == CustomViewCell.AllowHighlightProperty.PropertyName)
                {
                    SetStyle(formsCell);
                }
            }
    
            private void SetStyle(CustomViewCell formsCell)
            {
                //added this code as sometimes on tap, the separator disappears, if style is updated before tap animation finishes 
                //https://stackoverflow.com/questions/25613117/how-do-you-prevent-uitableviewcellselectionstylenone-from-removing-cell-separato
                Device.StartTimer(TimeSpan.FromMilliseconds(50), () => {
                    Device.BeginInvokeOnMainThread(() =>
                    {
                        if (formsCell.AllowHighlight)
                            _nativeCell.SelectionStyle = UITableViewCellSelectionStyle.Default;
                        else
                            _nativeCell.SelectionStyle = UITableViewCellSelectionStyle.None;
                    });
                    return false;
                });
            }
        }
    }
    

    Android 渲染器:

    [assembly: ExportRenderer(typeof(CustomViewCell), typeof(CustomViewCellRenderer))]
    namespace SampleApp.Droid
    {
        public class CustomViewCellRenderer : ViewCellRenderer
        {
            Android.Views.View _nativeCell;
    
            protected override Android.Views.View GetCellCore(Cell item, Android.Views.View convertView, Android.Views.ViewGroup parent, Android.Content.Context context)
            {
                _nativeCell = base.GetCellCore(item, convertView, parent, context);
    
                SetStyle();
    
                return _nativeCell;
            }
    
            // this one is simpler as the base class has a nice override-able method for our purpose - so we don't need to subscribe 
            protected override void OnCellPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
            {
                base.OnCellPropertyChanged(sender, e);
    
                if(e.PropertyName == CustomViewCell.AllowHighlightProperty.PropertyName)
                {
                    SetStyle();
                }
            }
    
            private void SetStyle()
            {
                var formsCell = Cell as CustomViewCell;
                if (formsCell == null)
                    return;
    
                _nativeCell.Clickable = !formsCell.AllowHighlight;
            }
        }
    }
    

    示例用法 1 - 通过数据绑定

    <local:CustomViewCell  AllowHighlight="{Binding IsHighlightEnabled}" ..>
        <Grid>
            <Grid x:Name="deselectGridLink" ..
        ...
    </local:CustomViewCell>
    

    示例用法 2 - 通过代码隐藏

    XAML

    <!-- Add name attribute to view-cell -->
    <local:CustomViewCell x:Name="deselectCell" ..>
        <Grid>
            <Grid x:Name="deselectGridLink" ..
        ...
    </local:CustomViewCell>
    

    代码隐藏

    private void SetPageDetails()
    {
        if (App.cardCountForSelectedCategories == 0)
        {
            deselectCell.AllowHighlight= false; //disable background flash
            ...
        }
        else
        {
            deselectCell.AllowHighlight= true;
            ...
        }
    }
    

    Option-3:禁用突出显示,选择所有项目

    这尤其适用于ListView更新后的问题现在指定单元格是TableView 的一部分,因此该选项在当前问题上下文中不再有效

    您将需要实现平台渲染器以禁用突出显示颜色,并将ItemTapped 处理程序添加到ListView 以通过将SelectedItem 始终设置为空来禁用选择。参考文献:

    1. Disable highlight item
    2. Disable selection

    代码

    首先,创建一个自定义视图单元:

    public class NoSelectViewCell : ViewCell { }
    

    实现iOS渲染器为:

    [assembly: ExportRenderer(typeof(NoSelectViewCell), typeof(NoSelectViewCellRenderer))]
    namespace SampleApp.iOS
    {
        public class NoSelectViewCellRenderer : ViewCellRenderer
        {
            public override UITableViewCell GetCell(Cell item, UITableViewCell reusableCell, UITableView tv)
            {
                var nativeCell = base.GetCell(item, reusableCell, tv);
                nativeCell.SelectionStyle = UITableViewCellSelectionStyle.None;
                return nativeCell;
            }
        }
    }
    

    实现android渲染器为:

    [assembly: ExportRenderer(typeof(NoSelectViewCell), typeof(NoSelectViewCellRenderer))]
    namespace SampleApp.Droid
    {
        public class NoSelectViewCellRenderer : ViewCellRenderer
        {
            protected override Android.Views.View GetCellCore(Cell item, Android.Views.View convertView, Android.Views.ViewGroup parent, Android.Content.Context context)
            {
                var cell = base.GetCellCore(item, convertView, parent, context);
    
                cell.Focusable = false;
                cell.FocusableInTouchMode = false;
    
                var listView = parent as Android.Widget.ListView;
                if (listView != null)
                {
                    listView.SetSelector(Android.Resource.Color.Transparent);
                    listView.CacheColorHint = Xamarin.Forms.Color.Transparent.ToAndroid();
                }
                return cell;
            }
        }
    }
    

    示例用法:

    XAML

    <ListView ItemTapped="Handle_ItemTapped">
        <ListView.ItemTemplate>
            <DataTemplate>
                <local:NoSelectViewCell Height="50">
                   <Grid>
                      <Grid x:Name="deselectGridLink" VerticalOptions="CenterAndExpand" Padding="20, 0">
                         <Label TextColor="Blue" Style="{DynamicResource ListItemTextStyle}" x:Name="deselectLink" HorizontalOptions="StartAndExpand" VerticalOptions="Center" Text="Deselect All" />
                      </Grid>
                      <Grid x:Name="deselectGridLabel" VerticalOptions="CenterAndExpand" Padding="20, 0">
                         <Label TextColor="Silver" Style="{DynamicResource ListItemTextStyle}" x:Name="deselectLabel" HorizontalOptions="StartAndExpand" VerticalOptions="Center" Text="Deselect All" />
                      </Grid>
                   </Grid>
                </local:NoSelectViewCell>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
    

    代码隐藏

    void Handle_ItemTapped(object sender, Xamarin.Forms.ItemTappedEventArgs e)
    {
        // don't do anything if we just de-selected the row
        if (e.Item == null) return;
        // do something with e.SelectedItem
        ((ListView)sender).SelectedItem = null; // de-select the row
    }
    

    【讨论】:

    • 嗨@G。 Sharada - 对不起,我认为我的问题不够清楚。如果查看单元格内容用于标签而不是链接,我正在寻找的只是能够禁用背景点击效果。使用代码,我将它从标签动态切换为链接,这样一旦用户单击删除所有行,取消选择就会变成链接。你能看看修改后的问题,如果你认为你的解决方案可以调整,请告诉我。换句话说,我认为我需要某种方式来指定 ViewCell 是否具有背景点击。
    • @Alan2 :我想我现在理解得更好了。我会更新答案并让你知道。
    • 嗨@Alan2:我已经根据我的理解更新了答案。它有三个选项可供选择 - 第一个选项允许您在允许点击的同时从代码隐藏/视图模型切换突出显示行为,而第二个选项基本上禁用该行。希望对您有所帮助!
    • 谢谢,我今天会检查这些并接受您的回答或向 cmets 添加一些内容。
    • 嗨@Alan2 - 仅供参考。我已经在结构和解释方面更新了答案。我还计划稍微更新 iOS 渲染器代码以获得更强大的处置逻辑。一旦我能够做到这一点,我会更新你。谢谢。
    【解决方案2】:

    G.Sharada 的建议在 iOS 上运行得非常好,但在 Android 上我仍然会在点击时闪烁。 将此行添加到样式文件中即可解决问题。

    <item name="android:colorActivatedHighlight">@android:color/transparent</item>
    

    【讨论】:

      猜你喜欢
      • 2019-09-01
      • 2014-06-12
      • 2020-12-06
      • 1970-01-01
      • 1970-01-01
      • 2014-10-09
      • 2011-06-05
      • 2013-09-27
      相关资源
      最近更新 更多