Xamarin.Forms ListView: establezca el color de resaltado de un elemento tocado

10 minutos de lectura

Xamarin.Forms ListView: establezca el color de resaltado de un elemento tocado
Falko

Utilizando Xamarin.Forms, ¿cómo puedo definir el color de fondo/resaltado de un elemento de ListView seleccionado/pulsado?

(Mi lista tiene un fondo negro y un color de texto blanco, por lo que el color de resaltado predeterminado en iOS es demasiado brillante. Por el contrario, en Android no hay resaltado en absoluto, solo una sutil línea gris horizontal).

Ejemplo: (izquierda: iOS, derecha: Android; mientras presiona “Barn2”)

Xamarin.Forms ListView: establezca el color de resaltado de un elemento tocado
Xamarin.Forms ListView: establezca el color de resaltado de un elemento tocado

Xamarin.Forms ListView: establezca el color de resaltado de un elemento tocado
mfranc28

En Android, simplemente edite su archivo styles.xml en Recursosvalores agregando esto:

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

  • el problema solo ocurre en Android, por lo que esta es la solución mejor/más limpia para mí

    – Ateik

    15 mar. 17 en 13:29

  • La solución más fácil para mí también. Es una pena que Xamarin Forms no tenga una propiedad personalizable para eso.

    – Marín Shalamanov

    12 jun.

  • ¿Funcionará esta solución si el proyecto principal está en pcl?

    – Pxaml

    26 ene.

  • Creé Style.xml ya que no había ninguno en mi proyecto de Android, pero esto no funciona para mí. ¿Necesito agregar algo más? ¿Quizás un tema especial? Estoy usando Xamarin.Forms.

    – Denis Vitez

    12 oct.

  • Lamentablemente no me funciona. El color en la pregunta es cuando se “toca” el elemento, lo que creo que es diferente a después del toque cuando luego pasa a “seleccionado”. Esta solución funciona para mí en el elemento seleccionado (después del toque) pero “durante” el toque obtengo el mismo rosa desvanecido que en la imagen de la pregunta.

    – cine mal escrito

    30 mar. 21 a las 23:29

Xamarin.Forms ListView: establezca el color de resaltado de un elemento tocado
barry sohl

Parece que en realidad hay una forma multiplataforma de hacer esto que funciona tanto en iOS como en Android (no estoy seguro acerca de Windows). Solo usa enlaces y no requiere renderizadores personalizados (lo que parece raro). Esta es una mezcla de muchas búsquedas en Google, así que gracias a cualquiera de quien pueda haber tomado prestado…

Estoy asumiendo ViewCells, pero esto también debería funcionar para celdas de texto o imagen. Aquí solo incluyo el código relevante más allá del típico texto, imagen, etc.

En tu página haz algo como esto:

MyModel model1 = new MyModel();
MyModel model2 = new MyModel();

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

Su modelo personalizado podría verse así:

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

Luego, para su ItemTemplate, necesita una clase de celda personalizada como esta:

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

        View = layout;
    }
}

Luego, en su controlador de eventos ItemSelected, haga lo siguiente. Tenga en cuenta que ‘seleccionado’ es una instancia de MyModel utilizada para rastrear el elemento seleccionado actualmente. Aquí solo muestro el color de fondo, pero también uso esta técnica para resaltar el texto y detallar los colores del texto.

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

  • Esto funciona, al menos un poco. Descubro que cuando se selecciona un elemento, esto funciona, pero cuando luego selecciono otro, el primero vuelve al fondo ‘antiguo’, a menos que me desplace para ocultarlo, luego me desplazo nuevamente para revelarlo, en cuyo caso el El color ‘nuevo’ se usa en el redibujado.

    – Sin cortar

    27 nov.

  • Dios mío, ¡gracias! Yo mismo pensé en eso, pero cuando lo intenté no funcionó. No estoy seguro de lo que hice mal, pero la copia de su código funcionó.

    – Mono espacial

    15 mar. 15 a las 18:39

  • NO funciona, solo dibujas sobre la fila, el color anterior todavía está allí y aún puedes ver una fila de 1 píxel de ese color en la parte inferior

    – animekun

    14 mayo 2015 en 03:12

  • @Greag.Deay – ListView.SeparatorVisibility = SeparatorVisibility.None; ListView.SeparatorColor= Color.Transparent; debería solucionar el problema que mencionas.

    – Rohit Vipin Mathews

    26 jun.

  • Si está siguiendo el patrón MVVM, este no es el mejor enfoque. Color no está realmente relacionado con el modelo en sí, es estrictamente una cuestión de interfaz de usuario y depende del marco (formularios en este caso). Ajuste es decir IsSelected marcar en modelo con convertidor o disparador de estilo en XAML sería una solución más portátil.

    –Marek

    14 mar. 18 en 9:31

Xamarin.Forms ListView: establezca el color de resaltado de un elemento tocado
Falko

iOS

Solución:

dentro de una costumbre ViewCellRenderer puedes configurar el SelectedBackgroundView. Simplemente cree una nueva UIView con un color de fondo de su elección y listo.

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

Resultado:

Xamarin.Forms ListView: establezca el color de resaltado de un elemento tocado

Nota:

Con Xamarin.Forms parece importante crear un nuevo UIView en lugar de simplemente establecer el color de fondo del actual.


Androide

Solución:

La solución que encontré en Android es un poco más complicada:

  1. Crear un nuevo dibujable ViewCellBackground.xml dentro de Resources>drawable carpeta:

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

    Define formas sólidas con diferentes colores para el estado predeterminado y el estado “presionado” de un elemento de la interfaz de usuario.

  2. Usar una clase heredada para el View de tu ViewCell, p.ej:

    public class TouchableStackLayout: StackLayout
    {
    }
    
  3. Implemente un renderizador personalizado para esta clase configurando el recurso de fondo:

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

Resultado:

Xamarin.Forms ListView: establezca el color de resaltado de un elemento tocado

  • Si configurar el color de fondo no funciona para la UIView existente, intente llamar al SetNeedsLayout de la celda (o como se llame ese método) después de configurar su color de fondo. UITableView intenta no realizar el diseño siempre que sea posible

    – Sten Petrov

    17 sep.


  • @StenPetrov: No, SetNeedsDisplay o “` SetNeedsLayout“` no funciona. Pero en realidad no importa, ya que asignar new UIView {...} es una solución bastante corta.

    – Falco

    17 sep.

  • ¿Y cómo cambiar el color de la fuente?

    – Édgar

    31 mar. 16 a las 18:37


  • En Android, el color seleccionado no se conserva para mí. He intentado usar android:state_selected.. :[

    – masterwok

    May 2 ’17 at 20:38

  • SelectedBackgroundView is for selection, not highlightning. Question is about highlightning

    – Softlion

    Apr 1 ’21 at 13:31


To change color of selected ViewCell, there is a simple process without using custom renderer. Make Tapped event of your ViewCell as below

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

In your ContentPage or .cs file, implement the event

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

Declare lastCell at the top of your ContentPage like this ViewCell lastCell;

Xamarin.Forms ListView: establezca el color de resaltado de un elemento tocado
JeePakaJP

Only for Android

Add in your custom theme or your default theme under ProjectName.Android/Resources/values/styles.xml

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

Xamarin.Forms ListView: establezca el color de resaltado de un elemento tocado
PoLáKoSz

I have a similar process, completely cross platform, however I track the selection status myself and I have done this in 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>

Then in the 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;
};

As you can see I just set the ListView.SelectedItem to null to remove any of the platform specific selection styles that come into play.

In my model I have

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

Xamarin.Forms ListView: establezca el color de resaltado de un elemento tocado
Brett Rigby

I had this same issue and I solved it as well by creating a custom renderer for iOS as Falko suggests, however, I avoided the styles modification for Android, I figured out a way to use a custom renderer for Android as well.

It is kind of funky how the selected flag is always false for the android view cell that’s why I had to create a new private property to track it. but other than that I think this follows a more appropriate pattern if you want to use custom renderers for both platforms, In my case I did it for TextCell but I believe it applies the same way for other CellViews.

Xamarin Forms

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

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

…then, in the .xaml page, you need to add an XMLNS reference back to the new CustomViewCell…

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

And don’t forget to make actual use of the new Custom comtrol in your XAML.

  • Thanks a bunch, this saved me hours! I can confirm it works for ViewCells (which is what I needed)

    – wislon

    Mar 28 ’17 at 4:23


  • Did anyone get this to work while setting a default value for the ListView’s SelectedItem?

    – Filipe Silva

    May 13 ’17 at 17:56

  • this works great for ListViews using the RetainElement caching strategy, but for those using the RecycleElement or RecycleElementAndDataTemplate strategy, the IsSelected property never changes – But we do get ‘BindingContext’ and ‘Index’ property changes. I’m still trying to figure out if I can use those somehow instead. So close! 🙂

    – wislon

    Apr 30 ’18 at 21:45

  • @Extragorey, you’re probably missing the ExportRenderer call: [assembly: ExportRenderer(typeof(ExtendedViewCell), typeof(ExtendedViewCellRenderer))]

    – Thomas Galliker

    15 nov.

  • Estaba usando esta técnica, pero debo decir que, como dijo @FilipeSilva, no funciona si el elemento seleccionado se inicializa en el modelo de vista. Trabaja solo con la interacción del usuario. Intenté solucionarlo pero no encontré una solución.

    – Nk54

    10 abr.

.

¿Ha sido útil esta solución?