[英]Xamarin.Forms ListView: Set the highlight color of a tapped item
使用Xamarin.Forms ,如何定義選定/點擊的 ListView 項目的突出顯示/背景顏色?
(我的列表有黑色背景和白色文本顏色,所以 iOS 上的默認突出顯示顏色太亮了。相比之下,Android 上根本沒有突出顯示 - 直到一條微妙的水平灰線。)
示例:(左:iOS,右:Android;同時按“Barn2”)
在 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>
看起來實際上有一種跨平台的方式可以在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 );
}
解決方案:
在自定義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 創建一個新的UIView
而不是僅僅設置當前UIView
的背景顏色似乎很重要。
解決方案:
我在 Android 上找到的解決方案有點復雜:
在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 元素的默認狀態和“按下”狀態定義了具有不同顏色的實心形狀。
為ViewCell
的View
使用繼承的類,例如:
public class TouchableStackLayout: StackLayout { }
為此類設置背景資源實現自定義渲染器:
public class ElementRenderer: VisualElementRenderer<Xamarin.Forms.View> { protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.View> e) { SetBackgroundResource(Resource.Drawable.ViewCellBackground); base.OnElementChanged(e); } }
結果:
要更改所選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;
}
}
在ContentPage
的頂部聲明lastCell
就像這個ViewCell lastCell;
僅適用於安卓
在ProjectName.Android/Resources/values/styles.xml下添加自定義主題或默認主題
<item name="android:colorActivatedHighlight">@android:color/transparent</item>
我有一個類似的過程,完全跨平台,但是我自己跟蹤選擇狀態,並且我已經在 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;
}
我遇到了同樣的問題,我也按照 Falko 的建議通過為 iOS 創建自定義渲染器來解決它,但是,我避免了對 Android 的樣式修改,我還想出了一種為 Android 使用自定義渲染器的方法。
對於 android 視圖單元格,所選標志始終為 false 的方式有點奇怪,這就是為什么我必須創建一個新的私有屬性來跟蹤它。 但除此之外,如果您想為兩個平台使用自定義渲染器,我認為這遵循更合適的模式,在我的情況下,我為 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 中實際使用新的自定義控件。
這是純粹的跨平台和簡潔的方式:
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"
注意:由於您的所有項目均未處於選定模式,因此選擇樣式將不會應用於任一平台。
我有並使用了類似於@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 Forms Maps(相同內容頁面)中的地點列表視圖提取的。 我希望這個解決方案對某人有用
為了設置突出顯示項的顏色,您需要在 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>
此解決方案工作正常,但如果您將 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);
}
}
}
注意:由於您要替換默認委托,您可能會遇到一些問題,有關這方面的更多信息,請參閱在自定義渲染器中設置控制委托導致功能丟失。 在我的項目中,如果我這樣做,一切都會正常工作:
對於使用默認緩存策略 ListViewCachingStrategy.RetainElement 的 ListView,使用普通 ListView 和本線程之前帖子中給出的 ListItemViewCellRenderer 代碼。
將此 ListView2 一起用於使用非默認緩存策略的 ListView,即 ListViewCachingStrategy.RecycleElement 或 ListViewCachingStrategy.RecycleElementAndDataTemplate。
我還向 Xamarin 提交了一個功能請求,如果您認為應該將其添加到標准 ListView 中,請點贊: ListView 迫切需要 SelectedItemBackgroundColor 屬性
在這里使用效果找到了這個可愛的選項。
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"));
在 android 上完成此操作的最簡單方法是將以下代碼添加到您的自定義樣式:
@android:顏色/透明
更改選擇顏色的最簡單方法是將這些添加到您的 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>
先前的答案要么建議自定義渲染器,要么要求您在數據對象或其他方式中跟蹤所選項目。 這並不是真正必需的,有一種方法可以以與平台無關的方式鏈接到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 論壇):
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 = ...;
});
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.