2014-09-17 1 views
27

Использование Xamarin.Forms, как я могу определить цвет выделения/фона для выбранного элемента ListView?Xamarin.Forms ListView: задайте цвет подсветки элемента с закантом

(Мой список имеет черный фон и белый цвет текста, поэтому цвет подсветки по умолчанию на прошивке слишком яркий В отличии от этого, на Android нет подсветки на всех -.. До тонкой горизонтальной серой линии)

Пример: (слева: IOS, справа: Android; при нажатии "Barn2")

ответ

19

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 это, кажется, важно создать новую UIView, а не только настройки фона цвет текущего.


Android

Решение:

Решение, которое я нашел на Android является немного более сложным:

  1. Создать новую Drawable ViewCellBackground.xml в папке 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> 
    

    Он определяет сплошные формы с разными цветами для состояния по умолчанию и «нажатого» состояния элемента пользовательского интерфейса.

  2. Используйте унаследованный класс для View вашего ViewCell, например.:

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

Результат:

+0

Если установка цвета фона не работает для существующих UIView попробуйте вызвать SetNeedsLayout ячейки (или независимо от того, какой метод был вызван) после установки цвета фона. UITableView пытается не выполнять компоновку, когда это возможно –

+0

@StenPetrov: Нет, '' SetNeedsDisplay''' или '' SetNeedsLayout''' не работает. Но это не имеет особого значения, поскольку присвоение '' '' нового UIView {...} '' 'довольно короткое временное решение. – Falko

+0

И как изменить цвет шрифта? – Edgar

46

Похоже, что есть на самом деле кросс-platfor m способ сделать это, что работает как на 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 сделайте следующее. Обратите внимание, что «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); 
} 

У меня есть скриншоты из оба прошивки и Android, если кто-то хочет, чтобы поднять меня на 10 очков, так что я могу на самом деле отправить их :)

+1

Это работает - по крайней мере, немного. Я нахожу, что когда элемент выбран, это работает, но когда я выбираю другой, первый возвращается к «старому» фону - если только я не прокручиваю его, чтобы скрыть его, а затем прокручивать его снова, чтобы показать его, и в этом случае «Новый» цвет используется при перерисовке. – Unsliced

+2

omg, спасибо! Я думал об этом сам, но когда я попробовал, это не сработало. Я не уверен, что я сделал неправильно, но копирование кода работало. –

+4

НЕ РАБОТАЕТ, вы просто рисуете наведите указатель на строку, старый цвет все еще там, и вы все еще можете видеть 1 пиксельную строку этого цвета внизу –

7

У меня есть подобный процесс, полностью кросс-платформенного, однако я отслеживаю статус выбора сам, и я сделал это в 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 Event

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 обнулить, чтобы удалить какой-либо платформы конкретных стилей выбора, которые вступают в игру.

В моей модели у меня есть

 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 
      } 
     } 
+0

Будет ли это также для контекстных меню действий? Например, длительное нажатие на андроид и прокрутка влево на iOS? – mr5

4

Для того, чтобы установить цвет выделенного элемента вам необходимо установить цвет cell.SelectionStyle в прошивке.

Этот пример предназначен для того, чтобы установить цвет прозрачного элемента в прозрачный.

Если вы хотите, вы можете изменить его другими цветами от UITableViewCellSelectionStyle. Это должно быть написано в платформенном проекте iOS, создав новый визуализатор Custom ListView в вашем проекте Forms.

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

Для андроид вы можете добавить этот стиль в своих значениях/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> 
+1

Как это ответить на вопрос?Вы не устанавливаете цвет вообще. Хотя оригинальный цвет подсветки на iOS слишком яркий, я не хотел полностью скрывать выделение. – Falko

+0

@Falko - Его настройка цветового примера. В показанном примере я настроен на прозрачность, но вы можете установить любой цвет по вашему выбору. –

+0

Мне пришлось использовать этот индивидуальный рендерер iOS в дополнение к решению Barry Sohl, чтобы предотвратить изменение шаблона списка из-за изменения цвета фона всех элементов управления в шаблоне на привязанный цвет фона. Однако мне пришлось изменить 'e.PropertyName ==" ItemsSource "' на 'e.PropertyName ==" SelectedItem ", чтобы заставить его работать правильно. – Scuzzlebutt

41

В Android просто отредактировать файл Style.xml под Resources \ Value добавления этого:

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

проблема только на Android, поэтому это лучшее/чистое решение для меня – Ateik

+0

Простое решение для меня тоже. Очень жаль, что для Xamarin Forms нет настраиваемого свойства. –

+0

будет ли это решение работать, если основной проект находится на pcl? – Pxaml

5

У меня была эта же проблема, и я решил ее также, создав собственный рендерер для iOS, как предлагает Falko, однако я избегал модификации стилей для Android, я выяснил способ использования специального рендеринга для Andr а также.

Это своего рода фанк, так как выбранный флаг всегда является ложным для ячейки зрения 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); 
      } 
     } 
    } 
} 
+0

Спасибо, связка, это спасло меня! Я могу подтвердить, что он работает для ViewCells (это то, что мне нужно) – wislon

+0

Неужели кто-нибудь заработал это, установив значение по умолчанию для SelectedItem ListView? –

3

Здесь чисто поперечные пл atform и аккуратный способ:

1) Определение триггера действие

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

2) Натяните выше экземпляра класса как действие EventTrigger в XAML как ниже

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

Примечание: поскольку ни один из ваших элементов не находится в выбранном режиме, выбор стилей не будет применяться на любой из платформ.

+0

Как это помогает «установить цвет подсветки/фона постукиваемого элемента»? Похоже, что вы сделали, это * подавить * любые изменения цвета. – ToolmakerSteve

+0

Он не меняет цвет фона любыми способами. Но полезно, когда пользователь хочет подавить выбор элемента. Это обычно полезно в сценариях, когда пользователь хочет запустить подробный вид, когда элемент в «ListView» используется, но хочет подавить выбранный элемент. –

0

Это решение работает нормально, но если вы измените стратегию кеширования ListView от значения по умолчанию, она перестанет работать. Это работает, если Вы новичок ваш ListView, как это: listView = new ListView() { ... }; Но если вы делаете это не работает (фон остается серым для выбранного элемента): listView = new ListView(cachingStrategy:ListViewCachingStrategy.RecycleElement) { ... };

Ниже решение, которое работает даже с не стандартное кэширование. Стратегия. Я предпочитаю это другим решениям, например, иметь код в методе OnItemSelected в сочетании с привязкой из ViewModel для цвета фона.

Кредит @Lang_tu_bi_dien, разместившего идею здесь: Listview Selected Item Background Color

Конечный код будет выглядеть следующим образом:

Xamarin.Формы Код:

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

Примечание: вы можете столкнуться с некоторыми проблемами из-за тот факт, что вы заменяете делегата по умолчанию, для получения дополнительной информации o n это см. Setting delegate of control in custom renderer results in lost functionality. В моем проекте все работает как надо, если я это сделать:

  • Используйте обычный ListView вместе с ListItemViewCellRenderer код, указанный в в предыдущих постах по этой теме для ListViews, которые используют стратегию кэширования по умолчанию ListViewCachingStrategy.RetainElement в ,

  • Используйте этот ListView2 вместе для ListViews, которые используют стратегию кэширования по умолчанию, то есть ListViewCachingStrategy.RecycleElement или ListViewCachingStrategy.RecycleElementAndDataTemplate.

Я также подать запрос на функцию с Xamarin, пожалуйста upvote, если вы чувствуете, что это должно быть добавлено к стандартному ListView: ListView desperately needs a SelectedItemBackgroundColor property

1

Найдено этот прекрасный вариант с использованием эффектов 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() 
     { 
     } 
    } 
} 

Android:

[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")); 
+0

Я думаю, что это не отвечает на вопрос, так как вы не устанавливаете цвет постукиваемого элемента. Вы просто избегаете выбора вообще. – Falko