Xamarin.ListView des formes: définissez la couleur de mise en évidence d'un élément tapé

en utilisant Xamarin.Les formes, Comment puis-je définir la couleur de surbrillance/arrière-plan d'un élément ListView sélectionné/tapé?

(Ma liste a un fond noir et une couleur de texte blanc, donc la couleur de mise en évidence par défaut sur iOS est trop brillante. En revanche, sur Android il n'y a pas de mise en évidence du tout - jusqu'à une ligne grise horizontale subtile.)

Exemple: (gauche: iOS, droite: Android; en appuyant "Barn2")

35
demandé sur Falko 2014-09-17 11:59:53

11 réponses

il semble qu'il existe en fait un moyen multiplateformes de faire cela qui fonctionne à la fois sur iOS et Android (pas sûr de Windows). Il n'utilise que la reliure et ne nécessite pas de rendus personnalisés (ce qui semble rare). C'est un mash-up de beaucoup de googler, merci donc à toute personne à qui j'ai emprunté...

je suppose que les cellules de vue, mais cela devrait fonctionner pour les cellules de texte ou D'Image aussi bien. Je ne suis que le code ici-delà de l'habituelle texte, image, etc.

sur votre page faites quelque chose comme ceci:

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

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

votre modèle personnalisé pourrait ressembler à quelque chose comme ceci:

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

alors pour votre ItemTemplate vous avez besoin d'une classe de cellules personnalisée quelque chose comme ceci:

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

        View = layout;
    }
}

puis dans votre itemselected event handler, faites ce qui suit. Notez que 'selected' est une instance de MyModel utilisée pour suivre l'élément actuellement sélectionné. Je veux seulement montrer la couleur de fond ici, mais j'utilise aussi cette technique pour inverser mettez en surbrillance le texte et les couleurs de texte de détail.

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

j'ai des captures d'écran de fois sur iOS et Android, si quelqu'un veut bosse moi à 10 points, de sorte que je peux les poster :)

53
répondu Barry Sohl 2017-08-19 03:33:23

dans Android éditez simplement votre Style.fichier xml en vertu de Ressources\Valeur en ajoutant ceci:

<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>
51
répondu mfranc28 2016-07-19 11:56:51

iOS

Solution:

dans le cadre d'une coutume ViewCellRenderer vous pouvez définir l' SelectedBackgroundView. Créez simplement un nouveau UIView avec une couleur de fond de votre choix et vous êtes prêt.

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

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

    return cell;
}

Résultat:

Remarque:

Avec De La Xamarine.Il semble important de créer un UIView plutôt que de simplement définir le contexte la couleur de l'actuel.


Android

Solution:

la solution que J'ai trouvée sur Android est un peu plus compliquée:

  1. Créer un nouveau drawable ViewCellBackground.xml dans le Resources>drawable dossier:

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

    il définit les formes solides avec des couleurs différentes pour l'état par défaut et l'état "pressé" d'un élément UI.

  2. utilisez une classe héritée pour l' View de votre ViewCell, e.g.:

    public class TouchableStackLayout: StackLayout
    {
    }
    
  3. implémenter un renderer personnalisé pour cette classe paramétrer la ressource d'arrière-plan:

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

Résultat:

21
répondu Falko 2014-09-17 16:20:46

j'ai un processus similaire, complètement multiplate-forme, cependant je suis le statut de sélection moi-même et je l'ai fait dans 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>

puis dans L'événement 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;

            };

comme vous pouvez le voir, je viens de configurer le ListView.SelectedItem to null pour supprimer l'un des styles de sélection spécifiques à la plate-forme qui entrent en jeu.

Dans mon modèle, j'ai

        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
            }
        }
9
répondu Adam Pedley 2015-03-19 04:49:35

j'ai eu ce même problème et je l'ai résolu aussi bien en créant un renderer personnalisé pour iOS comme Falko suggère, cependant, j'ai évité la modification de styles pour Android, j'ai trouvé un moyen d'utiliser un renderer personnalisé pour Android aussi bien.

C'est le genre de funky comment l'indicateur sélectionné est toujours faux pour l'androïde vue de la cellule c'est pourquoi j'ai dû créer une nouvelle propriété privée à la suivre. mais à part cela, je pense que cela suit un modèle plus approprié si vous voulez utiliser locataires pour les deux plateformes, dans mon cas je l'ai fait pour TextCell mais je crois qu'il s'applique de la même façon pour les autres CellViews.

Formes De Xamarine

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);
            }
        }
    }
}
6
répondu alexcons 2017-01-20 01:40:55

Voici la plate-forme purement croisée et la manière soignée:

1) Définissez une action de déclenchement

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

2) Appliquer l'instance de classe ci-dessus comme une action EventTrigger dans XAML comme ci-dessous

 <ListView x:Name="YourListView" ItemsSource="{Binding ViewModelItems}">
    <ListView.Triggers>
        <EventTrigger Event="ItemSelected">
            <customTriggers:DeselectListViewItemAction></customTriggers:DeselectListViewItemAction>
        </EventTrigger>
    </ListView.Triggers>
</ListView>

N'oubliez pas d'ajouter xmlns:customTriggers="clr-namespace:CustomTriggers;assembly=ProjectAssembly"

Note: Comme aucun de vos articles n'est en mode sélectionné, le style de sélection ne sera pas appliqué sur l'une ou l'autre des plateformes.

5
répondu Zafar Ameem 2017-03-03 09:14:53

pour définir la couleur de l'élément mis en surbrillance, vous devez définir la couleur de cell.SelectionStyle dans iOS.

cet exemple est pour définir la couleur de l'article tapé à transparent.

si vous voulez vous pouvez le changer avec d'autres couleurs de UITableViewCellSelectionStyle. Ceci doit être écrit dans le projet de plate-forme d'iOS en créant un nouveau renderer ListView personnalisé dans votre projet de formulaires.

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

pour android, vous pouvez ajouter ce style dans vos valeurs/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>
3
répondu Sawan Kumar Bundelkhandi 2016-03-23 11:09:23

trouvé cette belle option en utilisant des effets ici.

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()
        {
        }
    }
}

Formulaires:

ListView_Demo.Effects.Add(Effect.Resolve($"MyEffects.ListViewHighlightEffect"));
2
répondu Paul Charlton 2018-02-07 20:21:05

cette solution fonctionne très bien, mais si vous changez la stratégie de mise en cache de ListView par rapport à la valeur par défaut, elle cesse de fonctionner. Cela fonctionne si vous vous connectez à votre ListView comme ceci: listView = new ListView() { ... }; Mais si vous faites cela, il ne fonctionne pas (le fond reste le gris de l'élément sélectionné): listView = new ListView(cachingStrategy:ListViewCachingStrategy.RecycleElement) { ... };

ci-dessous est une solution qui fonctionne même avec une stratégie de caching non standard. Je préfère cela à d'autres solutions comme avoir du code dans la méthode OnItemSelected couplé avec une liaison de la ViewModel pour la couleur de fond.

crédit à @Lang_tu_bi_dien qui a posté l'idée ici:Listview Élément Sélectionné La Couleur De Fond

Le code final ressemble alors à ceci:

Xamarin.Code des formulaires:

namespace MyProject
{
    public class ListView2 : ListView
    {
        public ListView2(ListViewCachingStrategy cachingStrategy) : base(cachingStrategy)
        {
        }
    }
}

XAML sur votre page:

    <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 spécifiques au moteur de rendu:

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

Note: Vous pouvez rencontrer des problèmes dus au fait que vous remplacez le délégué par défaut, pour plus d'info sur ce point, voir définir délégué de contrôle dans le renderer personnalisé entraîne une perte de fonctionnalité. Dans mon projet, tout fonctionne comme il se doit si je fais ceci:

  • utilisez la ListView normale avec le code ListItemViewCellRenderer donné dans les messages précédents sur ce thread pour les ListViews qui utilisent la stratégie de cache par défaut ListViewCachingStrategy.RetainElement.

  • utilisez cette ListView2 ensemble pour les ListViews qui utilisez une stratégie de mise en cache non par défaut, C'est-à-dire ListViewCachingStrategy.Recyclage ou ListViewCachingStrategy.Recycleelementanddatemplate.

je dépose également une demande de fonctionnalité avec Xamarin, veuillez l'annuler si vous pensez qu'elle devrait être ajoutée à ListView standard: ListView a désespérément besoin d'une propriété SelectedItemBackgroundColor

1
répondu user2935274 2017-11-01 23:12:31

la façon la plus facile d'accomplir ceci sur android est d'ajouter le code suivant à votre style personnalisé:

@android:couleur/transparent

1
répondu Damien Doumer 2018-05-12 23:48:02

j'AI et j'utilise une solution similaire à @adam-pedley. Pas de rendus personnalisés, dans xaml je lie la propriété background 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>

Dans le code (MVVM) j'ai enregistrer le lastitemselected par un boolToColor Convertisseur-je mettre à jour couleur d'arrière-plan

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

mon exemple est extrait par un listview des lieux qui sont dans un Xamarin forme des cartes (même page de contenu). J'espère que cette solution sera utile pour quelqu'un

0
répondu Luigino De Togni 2018-03-27 13:51:19