【问题标题】:Xamarin.Forms ListView: Set the highlight color of a tapped itemXamarin.Forms ListView:设置点击项的突出显示颜色
【发布时间】:2014-11-11 04:12:02
【问题描述】:

使用 Xamarin.Forms,如何定义选定/点击的 ListView 项的突出显示/背景颜色?

(我的列表是黑色背景和白色文本颜色,因此 iOS 上的默认突出显示颜色太亮。相比之下,在 Android 上根本没有突出显示 - 直到一条微妙的水平灰线。)

示例:(左:iOS,右:Android;同时按下“Barn2”)

【问题讨论】:

    标签: android ios listview xamarin xamarin.forms


    【解决方案1】:

    在 Android 中,只需在 Resources\values 下编辑您的 styles.xml 文件,添加以下内容:

    <resources>
      <style name="MyTheme" parent="android:style/Theme.Material.Light.DarkActionBar">
       <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>
      </style>
    <color name="ListViewSelected">#96BCE3</color>
    <color name="ListViewHighlighted">#E39696</color>
    </resources>
    

    【讨论】:

    • 这个问题只发生在 Android 上,所以这对我来说是最好/最干净的解决方案
    • 对我来说也是最简单的解决方案。很遗憾 Xamarin Forms 没有可自定义的属性。
    • 如果主项目在 pcl 上,这个解决方案会起作用吗?
    • 我创建了 Style.xml,因为我的 Android 项目中没有,但这对我不起作用。我需要添加其他东西吗?也许是一个特殊的主题?我正在使用 Xamarin.Forms。
    • 遗憾的是不适合我。问题中的颜色是“轻按”项目时的颜色,我认为这与轻按后进入“已选择”时的颜色不同。此解决方案适用于所选项目(点击后),但“在”点击期间,我得到与问题图像中相同的淡粉色。
    【解决方案2】:

    看起来实际上有一种跨平台的方法可以在 iOS 和 Android 上运行(不确定 Windows)。它只使用绑定,不需要自定义渲染器(这似乎很少见)。这是很多谷歌搜索的混搭,所以感谢我可能借过的任何人......

    我假设是 ViewCells,但这也适用于文本或图像单元格。除了典型的文字、图片等,我这里只包括相关代码。

    在您的页面上执行以下操作:

    MyModel model1 = new MyModel();
    MyModel model2 = new MyModel();
    
    ListView list = new ListView
    {
        ItemsSource = new List<MyModel> { model1, model2 };
        ItemTemplate = new DataTemplate( typeof(MyCell) )
    };
    

    您的自定义模型可能如下所示:

    public class MyModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
    
        private Color _backgroundColor;
    
        public Color BackgroundColor 
        { 
            get { return _backgroundColor; } 
            set 
            { 
                _backgroundColor = value; 
    
                if ( PropertyChanged != null )
                {
                    PropertyChanged( this, new PropertyChangedEventArgs( "BackgroundColor" ) );
                }
            }
        }
    
        public void SetColors( bool isSelected )
        {
            if ( isSelected )
            {
                BackgroundColor = Color.FromRgb( 0.20, 0.20, 1.0 );
            }
            else
            {
                BackgroundColor = Color.FromRgb( 0.95, 0.95, 0.95 ); 
            }
        }
    }
    

    然后,对于您的 ItemTemplate,您需要一个类似这样的自定义单元格类:

    public class MyCell : ViewCell
    {
        public MyCell() : base()
        {
            RelativeLayout layout = new RelativeLayout();
            layout.SetBinding( Layout.BackgroundColorProperty, new Binding( "BackgroundColor" ) );
    
            View = layout;
        }
    }
    

    然后在您的 ItemSelected 事件处理程序中,执行以下操作。请注意,'selected' 是 MyModel 的一个实例,用于跟踪当前选定的项目。我在这里只显示背景颜色,但我也使用这种技术来反转突出显示文本和细节文本颜色。

    private void ItemSelected( object sender, ItemTappedEventArgs args )
    {
        // Deselect previous
        if ( selected != null )
        {
            selected.SetColors( false );
        }
    
        // Select new
        selected = (list.SelectedItem as MyModel);
        selected.SetColors( true );
    }
    

    【讨论】:

    • 这行得通——至少有一点。我发现当一个项目被选中时,这是可行的,但是当我选择另一个项目时,第一个会回到“旧”背景 - 除非我滚动隐藏它,然后再次滚动以显示它,在这种情况下重绘中使用了“新”颜色。
    • 天哪,谢谢!我自己也想过这个,但是当我尝试时它不起作用。我不确定我做错了什么,但复制你的代码是可行的。
    • 它不起作用,您只需将鼠标悬停在该行上,旧颜色仍然存在,您仍然可以在底部看到该颜色的 1 像素行
    • @Greag.Deay - ListView.SeparatorVisibility = SeparatorVisibility.None; ListView.SeparatorColor= Color.Transparent; 应该可以解决您提到的问题。
    • 如果您遵循 MVVM 模式,这实际上不是最佳方法。 Color 与模型本身并没有真正的关系——它是一个严格的 UI 问题和框架依赖(在这种情况下是表单)。在 XAML 中使用转换器或样式触发器在模型中设置 IsSelected 标志将是更可移植的解决方案。
    【解决方案3】:

    iOS

    解决方案:

    在自定义ViewCellRenderer 中,您可以设置SelectedBackgroundView。只需使用您选择的背景颜色创建一个新的UIView 即可。

    public override UITableViewCell GetCell(Cell item, UITableViewCell reusableCell, UITableView tv)
    {
        var cell =  base.GetCell(item, reusableCell, tv);
    
        cell.SelectedBackgroundView = new UIView {
            BackgroundColor = UIColor.DarkGray,
        };
    
        return cell;
    }
    

    结果:

    注意:

    对于 Xamarin.Forms,创建一个 new UIView 似乎很重要,而不仅仅是设置当前的背景颜色。


    安卓

    解决方案:

    我在Android上找到的解决方案有点复杂:

    1. Resources>drawable 文件夹中创建一个新的可绘制对象ViewCellBackground.xml

      <?xml version="1.0" encoding="UTF-8" ?>
      <selector xmlns:android="http://schemas.android.com/apk/res/android">
          <item android:state_pressed="true" >
              <shape android:shape="rectangle">
                  <solid android:color="#333333" />
              </shape>
          </item>
          <item>
              <shape android:shape="rectangle">
                  <solid android:color="#000000" />
              </shape>
          </item>
      </selector>
      

      它为 UI 元素的默认状态和“按下”状态定义了具有不同颜色的实体形状。

    2. 为您的ViewCellView 使用继承类,例如:

      public class TouchableStackLayout: StackLayout
      {
      }
      
    3. 为此类设置后台资源实现自定义渲染器:

      public class ElementRenderer: VisualElementRenderer<Xamarin.Forms.View>
      {
          protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.View> e)
          {
              SetBackgroundResource(Resource.Drawable.ViewCellBackground);
      
              base.OnElementChanged(e);
          }
      }
      

    结果:

    【讨论】:

    • 如果设置背景颜色对现有 UIView 不起作用,请尝试在设置其背景颜色后调用单元格的 SetNeedsLayout(或调用该方法的任何方法)。 UITableView 尽可能不执行布局
    • @StenPetrov:不,SetNeedsDisplay 或 ``` SetNeedsLayout``` 不起作用。但这并不重要,因为分配new UIView {...} 是一个非常短的解决方法。
    • 以及如何更改字体颜色?
    • 在 Android 上,我不会保留所选颜色。我试过使用 android:state_selected.. :[
    • SelectedBackgroundView 用于选择,而不是突出显示。问题是关于突出显示
    【解决方案4】:

    要更改所选ViewCell 的颜色,有一个简单的过程,无需使用自定义渲染器。使您的ViewCellTapped 事件如下

    <ListView.ItemTemplate>
        <DataTemplate>
            <ViewCell Tapped="ViewCell_Tapped">            
            <Label Text="{Binding StudentName}" TextColor="Black" />
            </ViewCell>
        </DataTemplate>
    </ListView.ItemTemplate>
    

    在您的 ContentPage 或 .cs 文件中,实现事件

    private void ViewCell_Tapped(object sender, System.EventArgs e)
    {
        if(lastCell!=null)
        lastCell.View.BackgroundColor = Color.Transparent;
        var viewCell = (ViewCell)sender;
        if (viewCell.View != null)
        {
            viewCell.View.BackgroundColor = Color.Red;
            lastCell = viewCell;
        }
    } 
    

    在您的ContentPage 顶部声明lastCell 就像这样ViewCell lastCell;

    【讨论】:

    • 最佳解决方案!谢谢!
    • 这个解决方案应该有最高票数并且必须排在首位,因为这是最简单的一个。
    • 最好的解决方案,最简单,最漂亮。应该在顶部
    • 它仅在您从菜单中选择一个项目时起作用。第一次显示菜单时它不起作用。
    【解决方案5】:

    仅适用于 Android

    ProjectName.Android/Resources/values/styles.xml

    下添加您的自定义主题或默认主题
    <item name="android:colorActivatedHighlight">@android:color/transparent</item>
    

    【讨论】:

      【解决方案6】:

      我有一个类似的过程,完全跨平台,但是我自己跟踪选择状态,我已经在 XAML 中完成了。

      <ListView x:Name="ListView" ItemsSource="{Binding ListSource}" RowHeight="50">
        <ListView.ItemTemplate>
          <DataTemplate>
            <ViewCell>
              <ViewCell.View>
                <ContentView Padding="10" BackgroundColor="{Binding BackgroundColor}">
                  <Label Text="{Binding Name}" HorizontalOptions="Center" TextColor="White" />
                </ContentView>
              </ViewCell.View>
            </ViewCell>
          </DataTemplate>
        </ListView.ItemTemplate>
      </ListView>
      

      然后在ItemTapped事件中

      ListView.ItemTapped += async (s, e) =>
      {
          var list = ListSource;
          var listItem = list.First(c => c.Id == ((ListItem)e.Item).Id);
          listItem.Selected = !listItem.Selected;
          SelectListSource = list;
          ListView.SelectedItem = null;
      };
      

      如您所见,我只是将 ListView.SelectedItem 设置为 null 以删除任何发挥作用的平台特定选择样式。

      在我的模型中

      private Boolean _selected;
      
      public Boolean Selected
      {
          get => _selected;
          set
          {
              _selected = value;
              if (PropertyChanged != null)
                  PropertyChanged(this, new PropertyChangedEventArgs("BackgroundColor"));
          }
      }
      
      public Color BackgroundColor
      {
          get => Selected ? Color.Black : Color.Blue;
      }
      

      【讨论】:

      • 这也适用于上下文操作菜单吗?比如长按android在iOS上向左滑动?
      • 我无法调用 list.First(...) .. 它说 IEnumerable 不包含 First...的定义...
      • @Prince - 您需要使用 System.Linq 添加;在顶部。
      • 如果您使用 System.Linq,那么 IEnumerable 肯定具有扩展名 .First()。如果您无法解决此问题,请在 StackOverflow 上提出另一个问题以调试该特定问题。
      • 由于所选项目被更改,它再次触发ItemSelected事件,所以在我的情况下,进一步的代码遇到了异常。
      【解决方案7】:

      我遇到了同样的问题,我也按照 Falko 的建议通过为 iOS 创建自定义渲染器解决了这个问题,但是,我避免了对 Android 的样式修改,我也想出了一种方法来为 Android 使用自定义渲染器。

      对于 android 视图单元格,选择的标志总是为假的,这有点奇怪,这就是为什么我必须创建一个新的私有属性来跟踪它。但除此之外,如果您想为两个平台使用自定义渲染器,我认为这遵循更合适的模式,在我的情况下,我为 TextCell 做了它,但我相信它适用于其他 CellViews 的相同方式。

      Xamarin 表单

      using Xamarin.Forms;
      
      public class CustomTextCell : TextCell
          {
              /// <summary>
              /// The SelectedBackgroundColor property.
              /// </summary>
              public static readonly BindableProperty SelectedBackgroundColorProperty =
                  BindableProperty.Create("SelectedBackgroundColor", typeof(Color), typeof(CustomTextCell), Color.Default);
      
              /// <summary>
              /// Gets or sets the SelectedBackgroundColor.
              /// </summary>
              public Color SelectedBackgroundColor
              {
                  get { return (Color)GetValue(SelectedBackgroundColorProperty); }
                  set { SetValue(SelectedBackgroundColorProperty, value); }
              }
          }
      

      iOS

      public class CustomTextCellRenderer : TextCellRenderer
          {
              public override UITableViewCell GetCell(Cell item, UITableViewCell reusableCell, UITableView tv)
              {
                  var cell = base.GetCell(item, reusableCell, tv);
                  var view = item as CustomTextCell;
                  cell.SelectedBackgroundView = new UIView
                  {
                      BackgroundColor = view.SelectedBackgroundColor.ToUIColor(),
                  };
      
                  return cell;
              }
          }
      

      安卓

      public class CustomTextCellRenderer : TextCellRenderer
      {
          private Android.Views.View cellCore;
          private Drawable unselectedBackground;
          private bool selected;
      
          protected override Android.Views.View GetCellCore(Cell item, Android.Views.View convertView, ViewGroup parent, Context context)
          {
              cellCore = base.GetCellCore(item, convertView, parent, context);
      
              // Save original background to rollback to it when not selected,
              // We assume that no cells will be selected on creation.
              selected = false;
              unselectedBackground = cellCore.Background;
      
              return cellCore;
          }
      
          protected override void OnCellPropertyChanged(object sender, PropertyChangedEventArgs args)
          {
              base.OnCellPropertyChanged(sender, args);
      
              if (args.PropertyName == "IsSelected")
              {
                  // I had to create a property to track the selection because cellCore.Selected is always false.
                  // Toggle selection
                  selected = !selected;
      
                  if (selected)
                  {
                      var customTextCell = sender as CustomTextCell;
                      cellCore.SetBackgroundColor(customTextCell.SelectedBackgroundColor.ToAndroid());
                  }
                  else
                  {
                      cellCore.SetBackground(unselectedBackground);
                  }
              }
          }
      }
      

      ...然后,在 .xaml 页面中,您需要将 XMLNS 引用添加回新的 CustomViewCell...

      xmlns:customuicontrols="clr-namespace:MyMobileApp.CustomUIControls"
      

      不要忘记在 XAML 中实际使用新的自定义控件。

      【讨论】:

      • 非常感谢,这为我节省了几个小时!我可以确认它适用于 ViewCells(这是我需要的)
      • 在为 ListView 的 SelectedItem 设置默认值时,有没有人让它工作?
      • 这对于使用 RetainElement 缓存策略的 ListView 非常有效,但对于使用 RecycleElement 或 RecycleElementAndDataTemplate 策略的 ListView,IsSelected 属性永远不会改变 - 但我们确实会得到 'BindingContext' 和 'Index' 属性的更改。我仍在试图弄清楚我是否可以以某种方式使用它们。很近! :)
      • @Extragorey,您可能缺少 ExportRenderer 调用:[assembly: ExportRenderer(typeof(ExtendedViewCell), typeof(ExtendedViewCellRenderer))]
      • 我正在使用这种技术,但我不得不说就像@FilipeSilva 所说,如果所选项目在视图模型中初始化,它就不起作用。仅适用于用户交互。我已经尝试过解决,但没有找到解决方法。
      【解决方案8】:

      这是纯粹的跨平台和简洁的方式:

      1) 定义触发动作

      namespace CustomTriggers {
         public class DeselectListViewItemAction:TriggerAction<ListView> {
             protected override void Invoke(ListView sender) {
                      sender.SelectedItem = null;
             }
         }
      }
      

      2) 将上述类实例应用为 XAML 中的 EventTrigger 操作,如下所示

       <ListView x:Name="YourListView" ItemsSource="{Binding ViewModelItems}">
          <ListView.Triggers>
              <EventTrigger Event="ItemSelected">
                  <customTriggers:DeselectListViewItemAction></customTriggers:DeselectListViewItemAction>
              </EventTrigger>
          </ListView.Triggers>
      </ListView>
      

      别忘了加xmlns:customTriggers="clr-namespace:CustomTriggers;assembly=ProjectAssembly"

      注意:由于您的所有项目都未处于选定模式,因此选择样式不会在任何一个平台上应用。

      【讨论】:

      • 这如何帮助“设置点击项目的突出显示/背景颜色”?看起来你所做的是抑制任何颜色变化。
      • 它不会以任何方式改变背景颜色。但是当用户想要抑制项目选择时很有帮助。这通常在用户想要在ListView 中的项目被点击但想要禁止选定项目时启动详细视图的情况下很有用。
      【解决方案9】:

      我有 & 使用类似于 @adam-pedley 的解决方案。 没有自定义渲染器,在 xaml 中我绑定背景 ViewCell 属性

                      <ListView x:Name="placesListView" Grid.Row="2" Grid.ColumnSpan="3" ItemsSource="{Binding PlacesCollection}" SelectedItem="{Binding PlaceItemSelected}">
                      <ListView.ItemTemplate>
                          <DataTemplate>
                              <ViewCell>
                                  <Grid BackgroundColor="{Binding IsSelected,Converter={StaticResource boolToColor}}">
                                      <Grid.RowDefinitions>
                                          <RowDefinition Height="auto"/>
                                          <RowDefinition Height="auto"/>
                                      </Grid.RowDefinitions>
                                      <Grid.ColumnDefinitions>
                                          <ColumnDefinition Width="*" />
                                          <ColumnDefinition Width="*" />
                                      </Grid.ColumnDefinitions>
      
                                      <Label Grid.Row="1" Grid.ColumnSpan="2" Text="{Binding DisplayName}" Style="{StaticResource blubeLabelBlackItalic}" FontSize="Default" HorizontalOptions="Start" />
                                      <Label Grid.Row="2" Grid.ColumnSpan="2" Text="{Binding DisplayDetail}"  Style="{StaticResource blubeLabelGrayItalic}" FontSize="Small" HorizontalOptions="Start"/>
                                      <!--
                                      <Label Grid.RowSpan="2" Grid.ColumnSpan="2" Text="{Binding KmDistance}"  Style="{StaticResource blubeLabelGrayItalic}" FontSize="Default" HorizontalOptions="End" VerticalOptions="Center"/>
                                      -->
                                  </Grid>
                              </ViewCell>
                          </DataTemplate>
                      </ListView.ItemTemplate>                    
                  </ListView>
      

      在代码 (MVVM) 中,我保存由 boolToColor 转换器选择的最后一个项目,我更新背景颜色

          public class BoolToColorConverter : IValueConverter
          {
              public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
              {
                  return (bool)value ? Color.Yellow : Color.White;
              }
      
              public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
              {
                  return (Color)value == Color.Yellow ? true : false;
              }
          }
      
          PlaceItem LastItemSelected;
      
          PlaceItem placeItemSelected;
          public PlaceItem PlaceItemSelected
          {
              get
              {
                  return placeItemSelected;
              }
      
              set
              {
                  if (LastItemSelected != null)
                      LastItemSelected.IsSelected = false;
      
                  placeItemSelected = value;
                  if (placeItemSelected != null)
                  {
                      placeItemSelected.IsSelected = true;
                      LastItemSelected = placeItemSelected;
                  }
                  PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(PlaceItemSelected)));
              }
          }
      

      我的示例由 Xamarin 表单地图(相同的内容页)中的地点列表视图提取。 我希望这个解决方案对某人有用

      【讨论】:

        【解决方案10】:

        为了设置高亮项目的颜色,您需要在 iOS 中设置 cell.SelectionStyle 的颜色。

        这个例子是将被点击的项目的颜色设置为透明。

        如果您愿意,可以使用UITableViewCellSelectionStyle 中的其他颜色进行更改。这将通过在您的 Forms 项目中创建一个新的自定义 ListView 渲染器来编写在 iOS 的平台项目中。

        public class CustomListViewRenderer : ListViewRenderer
            {
                protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
                {
                    base.OnElementPropertyChanged(sender, e);
        
                    if (Control == null)
                    {
                        return;
                    }
        
                    if (e.PropertyName == "ItemsSource")
                    {
                        foreach (var cell in Control.VisibleCells)
                        {
                            cell.SelectionStyle = UITableViewCellSelectionStyle.None;
                        }
                    }
                }
            }
        

        对于 android,您可以在 values/styles.xml 中添加此样式

        <style name="ListViewStyle.Light" parent="android:style/Widget.ListView">
            <item name="android:listSelector">@android:color/transparent</item>
            <item name="android:cacheColorHint">@android:color/transparent</item>
          </style>
        

        【讨论】:

        • 如何回答这个问题?你根本没有设置颜色。虽然iOS上原来的高亮颜色太亮了,但我不想完全隐藏高亮。
        • @Falko - 它设置颜色示例,在显示的示例中,我设置为透明,但您可以设置您选择的任何颜色。
        • 除了 Barry Sohl 的解决方案之外,我还必须使用这个 iOS 自定义渲染器来防止我的模板化列表视图将模板中所有控件的背景颜色更改为绑定的背景颜色。但是,我确实必须将 e.PropertyName == "ItemsSource" 更改为 e.PropertyName == "SelectedItem" 才能使其正常工作。
        • 正如 Falko 提到的,这并不能充分控制 iOS 上的 colorUITableViewCellSelectionStyle 只提到了两种内置颜色,蓝色和灰色。
        【解决方案11】:

        此解决方案工作正常,但如果您将 ListView 的缓存策略更改为远离默认值,它将停止工作。如果您像这样新建 ListView,它会起作用: listView = new ListView() { ... }; 但是,如果您这样做,它将不起作用(所选项目的背景保持灰色): listView = new ListView(cachingStrategy:ListViewCachingStrategy.RecycleElement) { ... };

        下面是一个即使使用非标准缓存策略也能工作的解决方案。我更喜欢其他解决方案,例如在 OnItemSelected 方法中添加代码以及来自 ViewModel 的背景颜色绑定。

        感谢@Lang_tu_bi_dien,他在这里发布了这个想法:Listview Selected Item Background Color

        最终的代码如下所示:

        Xamarin.Forms 代码:

        namespace MyProject
        {
            public class ListView2 : ListView
            {
                public ListView2(ListViewCachingStrategy cachingStrategy) : base(cachingStrategy)
                {
                }
            }
        }
        

        您页面上的 XAML:

            <ListView2 x:Name="myListView" ListViewCachingStrategy="RecycleElement" ItemsSource="{Binding ListSource}" RowHeight="50">
                <ListView.ItemTemplate>
                  <DataTemplate>
                    <ViewCell>
                      <ViewCell.View>
                          <Label Text="{Binding Name}" HorizontalOptions="Center" TextColor="White" />
                        </ContentView>
                      </ViewCell.View>
                    </ViewCell>
                  </DataTemplate>
                </ListView.ItemTemplate>
            </ListView2>

        iOS 特定渲染器:

        [assembly: ExportRenderer(typeof(ListView2), typeof(ListView2Renderer))]
        namespace MyProject.iOS
        {
            public partial class ListView2Renderer : ListViewRenderer
            {
                protected override void OnElementChanged(ElementChangedEventArgs<ListView> e)
                {
                    base.OnElementChanged(e);
                    if (Control != null && e != null)
                    {
                        //oldDelegate = (UITableViewSource)Control.Delegate;
                        Control.Delegate = new ListView2Delegate(e.NewElement);
                    }
                }
            }
        
        
            class ListView2Delegate : UITableViewDelegate
            {
                private ListView _listView;
        
                internal ListView2Delegate(ListView listView)
                {
                    _listView = listView;
                }
        
                public override void WillDisplay(UITableView tableView, UITableViewCell cell, Foundation.NSIndexPath indexPath)
                {
                    cell.SelectedBackgroundView = new UIView()
                    {
                        BackgroundColor = Color.Red.ToUIColor()
                    };
                }
        
                protected override void Dispose(bool disposing)
                {
                    if (disposing)
                    {
                        _listView = null;
                    }
                    base.Dispose(disposing);
                }
            }
        }
        

        注意:由于您要替换默认委托,您可能会遇到一些问题,有关此方面的更多信息,请参阅Setting delegate of control in custom renderer results in lost functionality。在我的项目中,如果我这样做,一切都会正常工作:

        • 对于使用默认缓存策略 ListViewCachingStrategy.RetainElement 的 ListView,将普通 ListView 与该线程之前的帖子中给出的 ListItemViewCellRenderer 代码一起使用。

        • 将此 ListView2 与使用非默认缓存策略(即 ListViewCachingStrategy.RecycleElement 或 ListViewCachingStrategy.RecycleElementAndDataTemplate)的 ListView 一起使用。

        我也在向 Xamarin 提交功能请求,如果您认为应该将其添加到标准 ListView,请点赞:ListView desperately needs a SelectedItemBackgroundColor property

        【讨论】:

          【解决方案12】:

          使用效果 here 发现了这个可爱的选项。

          iOS:

          [assembly: ResolutionGroupName("MyEffects")]
          [assembly: ExportEffect(typeof(ListViewHighlightEffect), nameof(ListViewHighlightEffect))]
          namespace Effects.iOS.Effects
          {
              public class ListViewHighlightEffect : PlatformEffect
              {
                  protected override void OnAttached()
                  {
                      var listView = (UIKit.UITableView)Control;
          
                      listView.AllowsSelection = false;
                  }
          
                  protected override void OnDetached()
                  {
                  }
              }
          }
          

          安卓:

          [assembly: ResolutionGroupName("MyEffects")]
          [assembly: ExportEffect(typeof(ListViewHighlightEffect), nameof(ListViewHighlightEffect))]
          namespace Effects.Droid.Effects
          {
              public class ListViewHighlightEffect : PlatformEffect
              {
                  protected override void OnAttached()
                  {
                      var listView = (Android.Widget.ListView)Control;
          
                      listView.ChoiceMode = ChoiceMode.None;
                  }
          
                  protected override void OnDetached()
                  {
                  }
              }
          }
          

          表格:

          ListView_Demo.Effects.Add(Effect.Resolve($"MyEffects.ListViewHighlightEffect"));
          

          【讨论】:

          • 我认为这不能回答问题,因为您没有设置被点击项目的颜色。您只是完全避免了选择。
          • 这不仅不回答问题,而且还无法选择一行!
          • 公平点,一定是误读了这个问题,因为我在这里找到了一种方法来删除选定的行颜色。如果我没记错的话,我仍然可以通过点击手势来选择一行。
          • 试过了:此效果激活时无法再选择一行。回滚更改...
          【解决方案13】:

          在 android 上完成此操作的最简单方法是将以下代码添加到您的自定义样式中:

          @android:颜色/透明

          【讨论】:

            【解决方案14】:

            更改选择颜色的最简单方法是将这些添加到您的 Android.Resources.values.styles

              <item name="android:colorPressedHighlight">@android:color/holo_blue_bright</item>
              <item name="android:colorFocusedHighlight">@android:color/holo_blue_bright</item>
              <item name="android:colorActivatedHighlight">@android:color/holo_blue_bright</item>
            

            【讨论】:

              【解决方案15】:

              前面的答案要么建议自定义渲染器,要么要求您在数据对象或其他方式中跟踪所选项目。这并不是真正需要的,有一种方法可以以与平台无关的方式链接到ListView 的功能。然后可以使用它以任何需要的方式更改所选项目。可以修改颜色,根据所选状态显示或隐藏单元格的不同部分。

              让我们将IsSelected 属性添加到我们的ViewCell。无需将其添加到数据对象中;列表视图选择单元格,而不是绑定数据。

              public partial class SelectableCell : ViewCell {
              
                public static readonly BindableProperty IsSelectedProperty = BindableProperty.Create(nameof(IsSelected), typeof(bool), typeof(SelectableCell), false, propertyChanged: OnIsSelectedPropertyChanged);
                public bool IsSelected {
                  get => (bool)GetValue(IsSelectedProperty);
                  set => SetValue(IsSelectedProperty, value);
                }
              
                // You can omit this if you only want to use IsSelected via binding in XAML
                private static void OnIsSelectedPropertyChanged(BindableObject bindable, object oldValue, object newValue) {
                  var cell = ((SelectableCell)bindable);
                  // change color, visibility, whatever depending on (bool)newValue
                }
              
                // ...
              }
              

              要在列表视图中创建单元格和选择之间的缺失链接,我们需要一个转换器(最初的想法来自Xamarin Forum):

              public class IsSelectedConverter : IValueConverter {
                public object Convert(object value, Type targetType, object parameter, CultureInfo culture) =>
                  value != null && value == ((ViewCell)parameter).View.BindingContext;
              
                public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) =>
                  throw new NotImplementedException();
              }
              

              我们使用这个转换器将两者连接起来:

              <ListView x:Name="ListViewName">
                <ListView.ItemTemplate>
                  <DataTemplate>
                    <local:SelectableCell x:Name="ListViewCell"
                      IsSelected="{Binding SelectedItem, Source={x:Reference ListViewName}, Converter={StaticResource IsSelectedConverter}, ConverterParameter={x:Reference ListViewCell}}" />
                  </DataTemplate>
                </ListView.ItemTemplate>
              </ListView>
              

              这个相对复杂的绑定用于检查当前选择了哪个实际项目。它将列表视图的SelectedItem 属性与单元格中视图的BindingContext 进行比较。该绑定上下文是我们实际绑定到的数据对象。换句话说,它检查SelectedItem指向的数据对象是否真的是单元格中的数据对象。如果它们相同,我们就有选定的单元格。我们将它绑定到IsSelected 属性中,然后可以在 XAML 或后面的代码中使用该属性来查看视图单元格是否处于选定状态。

              只有一个警告:如果您想在页面显示时设置默认选中项,您需要有点聪明。不幸的是,Xamarin Forms 没有页面 Displayed 事件,我们只有 Appearing 并且设置默认值还为时过早:绑定不会被执行。所以,使用一点延迟:

              protected override async void OnAppearing() {
                base.OnAppearing();
              
                Device.BeginInvokeOnMainThread(async () => {
                  await Task.Delay(100);
                  ListViewName.SelectedItem = ...;
                });
              }
              

              【讨论】:

              • 永远不要在生产软件中使用这样的代码。它可能在您的机器上工作,但是每当您使用“稍等片刻”时,这都是问题的指示器。在非常慢的诺基亚 3 Android 手机上,100 毫秒是不够的?
              • 此外,在 ListView 的名称和 ItemTemplate 之间直接引用意味着您永远无法将 DataTemplate 提取到单独的文件中。如果 DataTemplate 变大,您最终会得到巨大的 xaml 文件(就像我们多次所做的那样)。
              • 一年多前,我离开 Xamarin 转投 Flutter,再也没有回头。尽管如此,在编写此答案时,这可能是完成它的唯一方法。如果此后情况发生了变化,并且您现在知道更好的解决方案,那么您可以进行编辑,或者更好的是,提供新的答案。
              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2018-07-08
              • 2016-07-31
              • 1970-01-01
              • 2014-03-01
              • 2013-11-14
              • 2011-03-24
              相关资源
              最近更新 更多