Xamarin.Forms ListView:设置点击项的突出显示颜色

时间:2014-09-17 07:59:54

标签: android ios listview xamarin xamarin.forms

使用 Xamarin.Forms ,如何定义所选/点击的ListView项目的高亮/背景颜色?

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

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

13 个答案:

答案 0 :(得分:71)

在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>

答案 1 :(得分:60)

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

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

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

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事件处理程序中,执行以下操作。请注意,选择了&#39;是用于跟踪当前所选项目的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 );
}

我有来自iOS和Android的屏幕截图,如果有人想让我达到10分,那么我可以实际发布它们:)

答案 2 :(得分:24)

的iOS

<强>解决方案:

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

public override UITableViewCell GetCell(Cell item, UITableView tv)
{
    var cell = base.GetCell(item, tv);

    cell.SelectedBackgroundView = new UIView {
        BackgroundColor = UIColor.DarkGray,
    };

    return cell;
}

<强>结果:

注意:

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


的Android

<强>解决方案:

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

  1. ViewCellBackground.xml&gt; Resources文件夹中创建新的可绘制drawable

    <?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. View的{​​{1}}使用继承的类,例如:

    ViewCell
  3. 为此类设置自定义渲染器,设置后台资源:

    public class TouchableStackLayout: StackLayout
    {
    }
    
  4. <强>结果:

答案 3 :(得分:10)

我有一个类似的过程,完全跨平台,但我自己跟踪选择状态,我在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
            {
                return _selected;
            }
            set
            {
                _selected = value;
                if (PropertyChanged != null)
                    PropertyChanged(this, new PropertyChangedEventArgs("BackgroundColor"));
            }
        }                 

        public Color BackgroundColor
        {
            get
            {
                if (Selected)
                    return Color.Black;
                else
                    return Color.Blue
            }
        }

答案 4 :(得分:9)

我有同样的问题,我也通过为iOS创建自定义渲染器来解决它,正如Falko建议的那样,但是,我避免了Android的样式修改,我想出了一种方法来为Android使用自定义渲染器。

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

Xamarin表格

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;
        }
    }

<强>的Android

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);
            }
        }
    }
}

答案 5 :(得分:6)

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

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"

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

答案 6 :(得分:2)

要设置突出显示项目的颜色,您需要在iOS中设置cell.SelectionStyle的颜色。

此示例是将点按的项目的颜色设置为透明。

如果您愿意,可以使用UITableViewCellSelectionStyle中的其他颜色进行更改。这将通过在Forms项目中创建一个新的Custom 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>

答案 7 :(得分:2)

使用效果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"));

答案 8 :(得分:1)

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

下面是一个即使使用非标准的cachingStrategy也能解决的解决方案。我更喜欢其他解决方案,比如在OnItemSelected方法中使用代码,再加上ViewModel中用于背景颜色的绑定。

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

最终代码如下所示:

Xamarin.Forms代码:

listView = new ListView(cachingStrategy:ListViewCachingStrategy.RecycleElement) { ... };

页面上的XAML:

&#13;
&#13;
namespace MyProject
{
    public class ListView2 : ListView
    {
        public ListView2(ListViewCachingStrategy cachingStrategy) : base(cachingStrategy)
        {
        }
    }
}
&#13;
&#13;
&#13;

特定于iOS的渲染器:

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

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

  • 使用普通的ListView以及此线程早期帖子中给出的ListItemViewCellRenderer代码,用于使用默认缓存策略ListViewCachingStrategy.RetainElement的ListViews。

  • 将ListView2一起用于使用非默认缓存策略的ListView,即ListViewCachingStrategy.RecycleElement或ListViewCachingStrategy.RecycleElementAndDataTemplate。

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

答案 9 :(得分:1)

我有&amp;使用类似于@ 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 Converter选择的lastitem我更新背景颜色

    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 Forms Maps(同一内容页面)中的地点的列表视图提取的。 我希望这个解决方案对某人有用

答案 10 :(得分:1)

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

<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;
    }
} 

lastCell顶部ContentPage声明ViewCell lastCell;

答案 11 :(得分:1)

仅适用于 Android

添加自定义主题

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

答案 12 :(得分:0)

先前的答案要么建议使用自定义渲染器,要么要求您跟踪数据对象或其他对象中的选定项目。确实不是必须这样做,可以通过一种与平台无关的方式链接到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 = ...;
  });
}