如何停止点击ViewCell时短暂改变背景颜色的行为?

7

I have this XAML code:

<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()时,网格中的标签会变成链接或链接会变成标签。所以当它是一个标签时,我希望没有背景闪烁事件和不调用任何操作。
我像这样附加了一个轻拍手势识别器。请注意,它都在一行上,但覆盖了两行,因此在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网格中点击行时:
  • The deselectGridLink visibility is set to false
  • The deselectGridLabel visibility is set to 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;
        }
    }
    

这样做的效果是,网格链接文本将变为银色,并且链接成为标签。

然而,即使它是灰色标签,当标签被点击时,标签仍然会从白色短暂地背景行颜色变为深色。我认为这只是视图单元格的工作方式。

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

2个回答

14

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

编辑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>

建议二 - 使用触发器作为源与视图配合使用。
<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>

Option-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://dev59.com/OYPba4cB1Zd3GeqPmQK_
            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;
        ...
    }
}

选项3:禁用所有项的高亮和选择

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

您需要实现平台渲染器以禁用高亮颜色,并将ItemTapped处理程序添加到ListView以通过始终将SelectedItem设置为null来禁用选择。使用的参考资料:

  1. 禁用高亮项
  2. 禁用选择

代码

要开始,请创建自定义视图单元格:

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
2
@Alan2:我现在更理解了。我会更新答案并告诉你。 - Sharada Gururaj
1
嗨@Alan2:根据我的理解,我已更新答案。它有三个选项可供选择-第一个选项允许您在代码后端/视图模型中切换高亮行为,同时允许轻拍,而第二个选项基本上禁用了该行。希望能有所帮助! - Sharada Gururaj
嗨@Alan2 - 只是提醒一下..我已经更新了答案的结构和解释。我还计划稍微更新iOS渲染器代码,以获得更强大的处理逻辑。一旦我能够做到这一点,我会通知你。谢谢。 - Sharada Gururaj
谢谢,我会稍等一下。期待看到变化。 - Alan2
显示剩余5条评论

1
“G.Sharada提出的解决方案在iOS上运行得非常好,但在Android上点击时仍会出现闪烁。 将这一行添加到样式文件中可以解决问题。”
<item name="android:colorActivatedHighlight">@android:color/transparent</item>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接