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

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

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

      Xamarin自定義布局系列——瀑布流布局

      Xamarin.Forms以Xamarin.Android和Xamarin.iOS等為基礎,自己實現了一整套比較完整的UI框架,包含了絕大多數常用的控件,如下圖
      來源不詳,感謝作者

      雖然XF(Xamarin.Forms簡稱XF,下同)為我們提供大這么多的控件,但在實際使用中,會發現這些控件的可定制性特別差,基本上都需要里利用Renderer來做一些修改。為了實現我們的需求,有兩種辦法:

      1. Renderer
      2. 自定義控件/布局

      1.Renderer

      XF中的所有控件,實際都是通過Renderer來實現的,利用Renderer,直接實例化相應的原生控件,每一個XF控件在各個平臺都對應一個原生控件,具體可以查看這兒:RendererBase
      利用Renderer,需要你了解原生控件的使用,所以引用一句話就是:

      跨平臺不代表不用學各個平臺

      筆者也是對安卓和iOS了解不多,正在摸索學習中

      2.自定義控件/布局

      這種相對來說比較簡單,卻比較繁瑣,并且最終效果不會太好,包括性能和UI兩方面。但是還是能適應一些常用場景。
      關于布局基礎知識方面可以查看這位作者的一片文章:Xamarin.Forms自定義布局基礎
      在使用中會發現XF的自定義布局和UWP的非常相似,常用的方法有兩個

      public SizeRequest Measure(double widthConstraint, double heightConstraint, MeasureFlags flags = MeasureFlags.None); //計算元素大小
      public void Layout(Rectangle bounds);//為元素實際布局,確定其位置和大小

      Measure方法的兩個參數,表示父元素能為子元素提供的空間大小,返回值則表示子元素計算出自己實際需要的空間大小。
      Layout方法的參數表示父元素給子元素提供的布局位置,包含XY坐標和大小四個參數。

      現在考慮瀑布流布局的特點:
      1. 父元素大小確定,至少寬度和高度中有一個值確定(通常表現為整個頁面大小)
      2. 子元素排列表現為按行排列或者按列排列
      • 按行排列時:子元素的高是一個定值,寬度跟具具體情況可變

      • 按列排列時:子元素的寬是一個定值,高度跟具具體情況可變

      瀑布流的常用場景
      1. 圖片展示

      下面以的Demo展示一個按列布局的圖片展示瀑布流布局
      主要有兩個方法

          private double _maxHeight;
      
          /// <summary>
          /// 計算父元素需要的空間大小
          /// </summary>
          /// <param name="widthConstraint">可供布局的寬度</param>
          /// <param name="heightConstraint">可供布局的高度</param>
          /// <returns>實際需要的布局大小</returns>
          protected override SizeRequest OnMeasure(double widthConstraint, double heightConstraint)
          {   
              double[] colHeights = new double[Column];
              double allColumnSpacing = ColumnSpacing * (Column - 1);
              columnWidth = (widthConstraint - allColumnSpacing) / Column;
              foreach (var item in this.Children)
              {
                  var measuredSize = item.Measure(columnWidth, heightConstraint, MeasureFlags.IncludeMargins);
                  int col = 0;
                  for (int i = 1; i < Column; i++)
                  {
                      if (colHeights[i] < colHeights[col])
                      {
                          col = i;
                      }
                  }
                  colHeights[col] += measuredSize.Request.Height + RowSpacing;
              }
              _maxHeight = colHeights.OrderByDescending(m => m).First();
              return new SizeRequest(new Size(widthConstraint, _maxHeight));
          }
      

      OnMeasured方法在布局開始前被調用,在這個方法中,我們遍歷所有的子元素,通過調用子元素的Measure方法,計算出所有子元素需要的布局大小,然后按列累加所有的高度,最后選取高度的最大值,這個最大值就是父元素的布局高度,在按列布局中,寬度是確定的。

          protected override void LayoutChildren(double x, double y, double width, double height)
          {
              
              double[] colHeights = new double[Column];
              double allColumnSpacing = ColumnSpacing * (Column - 1);
              columnWidth = (width- allColumnSpacing )/ Column;
              foreach (var item in this.Children)
              {
                  var measuredSize=item.Measure(columnWidth, height, MeasureFlags.IncludeMargins);
                  int col = 0;
                  for (int i = 1; i < Column; i++)
                  {
                      if (colHeights[i] < colHeights[col])
                      {
                          col = i;
                      }
                  }
                  item.Layout(new Rectangle(col * (columnWidth + ColumnSpacing), colHeights[col], columnWidth, measuredSize.Request.Height));
      
      
                  colHeights[col] += measuredSize.Request.Height+RowSpacing;
              }
          }
      

      LayoutChildren方法在OnMeasured方法后調用,通過調用子元素的Layou方法,用于對所有子元素布局。

      至此,瀑布流和的新邏輯基本完成了,實際很簡單。接下來就是讓瀑布流支持數據綁定,實現動態添加刪除子元素。
      為了支持數據綁定,實現一個依賴屬性ItemsSource,當ItemsSource被賦值或者值發生變化的時候,重新布局,根據ItemsSource的內容重新布局

          public static readonly BindableProperty ItemsSourceProperty = BindableProperty.Create("ItemsSource", typeof(IList), typeof(FlowLayout), null,propertyChanged: ItemsSource_PropertyChanged);
          public IList ItemsSource
          {
              get { return (IList)this.GetValue(ItemsSourceProperty); }
              set { SetValue(ItemsSourceProperty, value); }
          }
          
          private static void ItemsSource_PropertyChanged(BindableObject bindable, object oldValue, object newValue)
          {
              var flowLayout = (FlowLayout)bindable;
              var newItems = newValue as IList;
              var oldItems = oldValue as IList;
              var oldCollection = oldValue as INotifyCollectionChanged;
              if (oldCollection != null)
              {
                  oldCollection.CollectionChanged -= flowLayout.OnCollectionChanged;
              }
      
              if (newValue == null)
              {
                  return;
              }
      
              if (newItems == null)
                  return;
              if(oldItems == null||newItems.Count!= oldItems.Count)
              {
                  flowLayout.Children.Clear();
                  for (int i = 0; i < newItems.Count; i++)
                  {
                      var child = flowLayout.ItemTemplate.CreateContent();
                      ((BindableObject)child).BindingContext = newItems[i];
                      flowLayout.Children.Add((View)child);
                  }
                  
              }
      
              var newCollection = newValue as INotifyCollectionChanged;
              if (newCollection != null)
              {
                  newCollection.CollectionChanged += flowLayout.OnCollectionChanged;
              }
      
              flowLayout.UpdateChildrenLayout();
              flowLayout.InvalidateLayout();
          }
        
      
          protected override void OnBindingContextChanged()
          {
              base.OnBindingContextChanged();
          }
      
          private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
          {
              if (e.OldItems != null)
              {
                  this.Children.RemoveAt(e.OldStartingIndex);
                  this.UpdateChildrenLayout();
                  this.InvalidateLayout();
              }
      
              if (e.NewItems == null)
              {
                  return;
              }
              for (int i = 0; i < e.NewItems.Count; i++)
              {
                  var child = this.ItemTemplate.CreateContent();
                  ((BindableObject)child).BindingContext = e.NewItems[i];
                  this.Children.Add((View)child);
              }
      
              this.UpdateChildrenLayout();
              this.InvalidateLayout();
          }
      }
      

      ItemsSource_PropertyChanged方法在ItemsSource屬性被賦值的時候調用,在此方法中,根據自定義的DataTemplate,創建一個視圖(View),設置其數據綁定上下文為對應的Item,然后添加到瀑布流布局的Children中。

      var child = this.ItemTemplate.CreateContent(); ((BindableObject)child).BindingContext = e.NewItems[i]; this.Children.Add((View)child);

      注意到,在數據綁定中,更加常見的場景是:ItemsSource只賦值一次,以后ItemsSource中的值修改,直接能在布局中表現出來。
      這就要求ItemsSource的數據源必須實現INotifyCollectionChanged這個接口,在.Net中,ObservableCollection是已經封裝好的,實現了這個接口的一個開箱即用的集合類。所以在ItemsSource的值改變的時候,需要訂閱對數據源CollectionChanged事件,以便于在集合中元素添加或刪除的時候重新布局。

      瀑布流布局

      項目地址:Github

      posted @ 2017-03-13 19:14  DemoApp  閱讀(2025)  評論(8)    收藏  舉報
      主站蜘蛛池模板: 久久久亚洲欧洲日产国码606| 日韩精品卡一卡二卡三卡四| 国产精品一区二区性色av| 高清无码在线视频| 青神县| 亚洲精品一区二区二三区| 97久久综合亚洲色hezyo| 国产综合久久久久久鬼色| 亚洲欧美人成人让影院| 久久夜色撩人国产综合av| 亚洲中文一区二区av| 欧美极品色午夜在线视频| 久久成人国产精品免费软件| 久久精品这里热有精品| 亚洲亚洲人成综合网络| 美女内射毛片在线看3d| 一边吃奶一边做动态图| 丁香花成人电影| 国产AV无码专区亚洲AV漫画| 国产成人精品亚洲精品日日| 最新中文字幕国产精品| 精品国产午夜福利理论片| 国产一区二区日韩在线| 国产日本一区二区三区久久| 在线高清免费不卡全码| 日本少妇自慰免费完整版| 国产欧美日韩精品第二区| 亚洲AV蜜桃永久无码精品| 国产午夜A理论毛片| 国产成人精品亚洲精品日日| 无码人妻黑人中文字幕| 免费无码午夜理论电影| 亚洲蜜臀av乱码久久| 免费观看欧美猛交视频黑人| 欧美一区二区三区成人久久片| 国产精品久久久久9999| 18禁亚洲一区二区三区| 福利一区二区1000| 精品久久久久久亚洲综合网| 国产精品亚洲а∨天堂2021| 无码精品人妻一区二区三区老牛|