<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      Xamarin自定義布局系列——ListView的一個自定義實現ItemsControl(橫向列表)

      在以前寫UWP程序的時候,了解到在ListView或者ListBox這類的列表空間中,有一個叫做ItemsPannel的屬性,它是所有列表中子元素實際的容器,如果要讓列表進行橫向排列,只需要在Xaml中如下編輯即可

          //UWP中用XAML大致實現如下
          ···
          <ListView.ItemsPannel>
              <StackPannel Orientation="Horizental"/>
          </ListView.ItemsPannel>
          ···
      

      這種讓列表元素橫向排列實際是一個很常見的場景,但是在Xamarin.Forms中,并沒有提供直接的實現方法,如果想要這種效果,有兩種解決辦法

      • Renderer:利用Renderer在各平臺實現,適用于對性能有較高要求的場景,比如大量數據展示
      • 自定義布局:實現比較簡單,但是適用于數據量比較小的場景
        實際在使用的時候,利用自定義布局會比較簡單,并且橫向的列表展示并不適合大量數據的場景。

      怎么實現呢?

      Xamarin.Forms的列表控件是直接利用Renderer實現的,沒有提供類似ItemsPannel之類的屬性,所以考慮直接自己實現一個列表控件。有以下幾個點:

      • 列表控件要支持滾動:所以在控件最外層需要一個ScrollView
      • 實現類似ItemsPannel的效果:所以需要實現一個ItemsPannel屬性,類型是StackLayout,并且它應該是ScrollView的Content
      • ItemsControl控件的基類型是View,便于使用,直接讓它繼承自ContentView,這樣就可以直接設置其Content為ScrollView

      至此,先來給出這部分的代碼,我們直接在構造函數中完成絕大多數操作

          ···
          private ScrollView _scrollView;
          private StackLayout itemsPanel = null;
          public StackLayout ItemsPanel
          {
              get { return this.itemsPanel; }
              set { this.itemsPanel = value; }
          }
          public ItemsControl()
          {
              this._scrollView = new ScrollView();
              this._scrollView.Orientation = Orientation;
      
              this.itemsPanel = new StackLayout() { Orientation = StackOrientation.Horizontal };//子元素水平排布的關鍵
      
              this.Content = this._scrollView;
              this._scrollView.Content = this.itemsPanel;
          }
          ···
      

      子元素的容器是ItemsPannel,它實際是一個水平排布的StackLayout。想要在列表控件添加子元素,實際就是對該StackLayout的Children添加子元素。

      考慮到列表控件中子元素的添加,就必須實現一個屬性ItemsSource,是集合類型,并且為了支持數據綁定等,還需要讓他是一個依賴屬性,針對ItemsSource屬性值自身的改變或者其集合中元素的添加刪除等,都需要監聽,并且將具體變化表現在ItemsControl中。實現該屬性如下:

          ···
          public static readonly BindableProperty ItemsSourceProperty = BindableProperty.Create("ItemsSource", typeof(IEnumerable), typeof(ItemsControl), defaultBindingMode: BindingMode.OneWay, defaultValue: null, propertyChanged: OnItemsSourceChanged);
          
          public IEnumerable ItemsSource
          {
              get { return (IEnumerable)this.GetValue(ItemsSourceProperty); }
              set { this.SetValue(ItemsSourceProperty, value); }
          }
          ···
          Static vid OnItemsSourceChanged(BindableObject sender,object oldValue,object newValue)
          {
              ···
          }
          ···
      

      當為ItemsSource屬性賦值之后,OnItemsSourceChanged方法被調用,在該方法中,需要干這么幾件事兒:

      • 為ItemsSource中的每一個元素,根據ItemTemplate創建相應的View,設置View的數據綁定上想問BindingContext為該元素,并且將此View添加到ItemsPannel中(ItemsPannel實際是StackLayout,他的子元素必須繼承自View或者是View)
      • 檢測ItemsSource的數據源是否實現了接口INotifyCollectionChanged,如果實現了,需要訂閱其CollectionChanged事件,注冊一個方法,便于在集合元素變動后調用我們注冊的方法,來通知ItemsControl控件,把具體的變動表現在UI層面(通常就是元素的添加和刪除)

      OnItemsSourceChanged方法實現如下:

          public static readonly BindableProperty ItemTemplateProperty = BindableProperty.Create("ItemTemplate", typeof(DataTemplate), typeof(ItemsControl), defaultValue: default(DataTemplate));
          public DataTemplate ItemTemplate
          {
              get { return (DataTemplate)this.GetValue(ItemTemplateProperty); }
              set { this.SetValue(ItemTemplateProperty, value); }
          }
      
          static void OnItemsSourceChanged(BindableObject bindable, object oldValue, object newValue)
          {
              var control = bindable as ItemsControl;
              if (control == null)
              {
                  return;
              }
              //檢測是否實現該接口,如果實現,就訂閱該事件
              var oldCollection = oldValue as INotifyCollectionChanged;
              if (oldCollection != null)
              {
                  oldCollection.CollectionChanged -= control.OnCollectionChanged;
              }
      
              if (newValue == null)
              {
                  return;
              }
      
              control.ItemsPanel.Children.Clear();
      
              //遍歷數據源中每個元素,為它創建View,并設置其BindingContext
              foreach (var item in (IEnumerable)newValue)
              {
                  object content;
                  content = control.ItemTemplate.CreateContent();
                  View view;
                  var cell = content as ViewCell;
                  if (cell != null)
                  {
                      view = cell.View;
                  }
                  else
                  {
                      view = (View)content;
                  }
      
                  //元素點擊相關事件
                  view.GestureRecognizers.Add(control._tapGestureRecognizer);
                  view.BindingContext = item;
                  control.ItemsPanel.Children.Add(view);
              }
      
              
      
              var newCollection = newValue as INotifyCollectionChanged;
              if (newCollection != null)
              {
                  newCollection.CollectionChanged += control.OnCollectionChanged;
              }
              control.SelectedItem = control.ItemsPanel.Children[control.SelectedIndex].BindingContext;
      
              //更新布局
              control.UpdateChildrenLayout();
              control.InvalidateLayout();
              
          }
      

      CollectionChanged實現方法如下:

          private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
          {
              if (e.OldItems != null)
              {
                  this.ItemsPanel.Children.RemoveAt(e.OldStartingIndex);
                  this.UpdateChildrenLayout();
                  this.InvalidateLayout();
              }
      
              if (e.NewItems == null)
              {
                  return;
              }
              foreach (var item in e.NewItems)
              {
                  var content = this.ItemTemplate.CreateContent();
      
                  View view;
                  var cell = content as ViewCell;
                  if (cell != null)
                  {
                      view = cell.View;
                  }
                  else
                  {
                      view = (View)content;
                  }
                  if (!view.GestureRecognizers.Contains(this._tapGestureRecognizer))
                  {
                      view.GestureRecognizers.Add(this._tapGestureRecognizer);
                  }
                  view.BindingContext = item;
                  this.ItemsPanel.Children.Insert(e.NewItems.IndexOf(item), view);
              }
              
      
              this.UpdateChildrenLayout();
              this.InvalidateLayout();
              
          }
      

      到目前為止,已經實現ItemsControl控件大部分的內容了,還需要實現的有

      • SelectedItem,SelectedIndex:當前列表選定項
      • ItemSelected:列表中元素被選定時觸發

      怎么判斷元素被選定呢?

      當一個元素被點擊后,認為它被選中了,所以需要監聽列表中每一個元素的點擊事件。

      列表中每一個View被點擊后,觸發OnTapped事件,事件的發送者是該View本身

              //只定義一個TapGestureRecognizer,不需要為每一個元素都創建,只需要為每一個元素的GestureRecognizers集合添加該實例即可。
              TapGestureRecognizer _tapGestureRecognizer;
      
              //在構造函數中創建一個Tap事件的GestureRecognizer,并且訂閱其Tapped事件
              public ItemsControl()
              {
                  _tapGestureRecognizer = new TapGestureRecognizer();
                  _tapGestureRecognizer.Tapped += OnTapped;
              }
              ···
              private void OnTapped(object sender, EventArgs e)
              {
                  var view = (BindableObject)sender;
                  this.SelectedItem = view.BindingContext;
      
              }
              ···
              static void OnItemsSourceChanged(BindableObject bindable, object oldValue, object newValue)
              {
                  ···
                  if (!view.GestureRecognizers.Contains(this._tapGestureRecognizer))
                  {
                          view.GestureRecognizers.Add(this._tapGestureRecognizer);
                  }
                  ···
              }
              ···
      

      一個基本的ItemsControl列表控件就完成了,至此,它的已經具備Xamarin.Forms提供的ListView的大致功能。不過還是有幾點

      • 它不支持虛擬化技術,所以在列表數據量比較大的時候,會有明顯的卡頓

      具體代碼和Demo看我的Github:

      ItemsControl源碼

      posted @ 2017-03-15 11:10  DemoApp  閱讀(5333)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 昌图县| 曰韩无码二三区中文字幕| 少妇被爽到高潮喷水久久欧美精品| 国产免费无遮挡吃奶视频| 免费观看全黄做爰大片| 国产亚洲精品岁国产精品| 国产一区二区精品自拍| 国产亚洲精品久久久网站好莱| 狠狠色噜噜狠狠狠777米奇小说| 爱色精品视频一区二区| 久9视频这里只有精品| 人妻饥渴偷公乱中文字幕| 蜜桃av无码免费看永久| 武乡县| 国产久爱免费精品视频| 韩国福利视频一区二区三区| 无码中文字幕av免费放| 国产二区三区不卡免费| 黑人巨大粗物挺进了少妇| 国产国拍亚洲精品永久软件| 爽爽精品dvd蜜桃成熟时电影院| 国产精品人妻中文字幕| 国产漂亮白嫩美女在线观看| 97人人添人澡人人爽超碰| 国产无吗一区二区三区在线欢| 久久精品亚洲精品国产区| 久久精品国产99国产精品严洲| 亚在线观看免费视频入口| 男人av无码天堂| www插插插无码视频网站| 免费国产va在线观看| bt天堂新版中文在线| 无码国内精品久久人妻蜜桃| 无码国产精品一区二区免费虚拟vr| 国产精品无遮挡猛进猛出| 福利一区二区1000| 国产成a人片在线观看视频下载| 久久99九九精品久久久久蜜桃| 亚洲尤码不卡av麻豆| 熟女人妇 成熟妇女系列视频| 国产自产av一区二区三区性色 |