使用 Xamarin.Forms ,如何定义所选/点击的ListView项目的高亮/背景颜色?
(我的列表有黑色背景和白色文字颜色,因此iOS上的默认高亮颜色太亮了。相比之下,在Android上根本没有突出显示 - 直到细微的水平灰线。)
示例:(左:iOS,右:Android;按“Barn2”时)
答案 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)
<强>解决方案:强>
在自定义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上找到的解决方案有点复杂:
在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元素的“按下”状态。
为View
的{{1}}使用继承的类,例如:
ViewCell
为此类设置自定义渲染器,设置后台资源:
public class TouchableStackLayout: StackLayout
{
}
<强>结果:强>
答案 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:
namespace MyProject
{
public class ListView2 : ListView
{
public ListView2(ListViewCachingStrategy cachingStrategy) : base(cachingStrategy)
{
}
}
}
&#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 = ...;
});
}