├── .gitignore ├── Binwell.Controls ├── FastGrid.Android │ ├── FastGrid.Android.csproj │ └── FastGrid │ │ ├── EndlessRecyclerViewScrollListener.cs │ │ ├── FastGridAdapter.cs │ │ ├── FastGridViewRenderer.cs │ │ ├── FormsToNativeDroid.cs │ │ ├── SmoothGridLayoutManager.cs │ │ ├── SwipeRefreshLayoutWithDisabling.cs │ │ └── ViewHolders.cs ├── FastGrid.iOS │ ├── FastGrid.iOS.csproj │ └── FastGrid │ │ ├── FastCollectionView.cs │ │ ├── FastCollectionViewCell.cs │ │ ├── FastCollectionViewDataSource.cs │ │ ├── FastCollectionViewDelegate.cs │ │ ├── FastGridViewRenderer.cs │ │ └── LeftFlowCollectionViewLayout.cs └── FastGrid │ ├── FastGrid.csproj │ └── FastGrid │ ├── FastGridCell.cs │ ├── FastGridDataTemplate.cs │ ├── FastGridEventArgs.cs │ ├── FastGridTemplateSelector.cs │ ├── FastGridView.cs │ └── IScrollAwareElement.cs ├── FastGrid.sln ├── LICENSE ├── README.md └── Sample ├── FastGridSample.Android ├── Assets │ └── AboutAssets.txt ├── FastGridSample.Android.csproj ├── MainActivity.cs ├── Properties │ ├── AndroidManifest.xml │ └── AssemblyInfo.cs └── Resources │ ├── AboutResources.txt │ ├── Resource.Designer.cs │ ├── layout │ ├── Tabbar.axml │ └── Toolbar.axml │ ├── mipmap-anydpi-v26 │ ├── icon.xml │ └── icon_round.xml │ ├── mipmap-hdpi │ ├── Icon.png │ └── launcher_foreground.png │ ├── mipmap-mdpi │ ├── icon.png │ └── launcher_foreground.png │ ├── mipmap-xhdpi │ ├── Icon.png │ └── launcher_foreground.png │ ├── mipmap-xxhdpi │ ├── Icon.png │ └── launcher_foreground.png │ ├── mipmap-xxxhdpi │ ├── Icon.png │ └── launcher_foreground.png │ └── values │ ├── colors.xml │ └── styles.xml ├── FastGridSample.iOS ├── AppDelegate.cs ├── Assets.xcassets │ └── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── Icon1024.png │ │ ├── Icon120.png │ │ ├── Icon152.png │ │ ├── Icon167.png │ │ ├── Icon180.png │ │ ├── Icon20.png │ │ ├── Icon29.png │ │ ├── Icon40.png │ │ ├── Icon58.png │ │ ├── Icon60.png │ │ ├── Icon76.png │ │ ├── Icon80.png │ │ └── Icon87.png ├── Entitlements.plist ├── FastGridSample.iOS.csproj ├── Info.plist ├── Main.cs ├── Properties │ └── AssemblyInfo.cs └── Resources │ ├── Default-568h@2x.png │ ├── Default-Portrait.png │ ├── Default-Portrait@2x.png │ ├── Default.png │ ├── Default@2x.png │ └── LaunchScreen.storyboard └── FastGridSample ├── App.cs ├── Cells ├── CategoryCell.xaml ├── CategoryCell.xaml.cs └── ProductCell.cs ├── DataObjects ├── CategoryObject.cs └── ProductObject.cs ├── FastGridSample.csproj ├── MainPage.xaml ├── MainPage.xaml.cs └── MainViewModel.cs /.gitignore: -------------------------------------------------------------------------------- 1 | # Xamarin Components 2 | **/Components/* 3 | 4 | # Created by https://www.gitignore.io 5 | 6 | ### Windows ### 7 | # Windows image file caches 8 | Thumbs.db 9 | ehthumbs.db 10 | 11 | # Folder config file 12 | Desktop.ini 13 | 14 | # Recycle Bin used on file shares 15 | $RECYCLE.BIN/ 16 | 17 | # Windows Installer files 18 | *.cab 19 | *.msi 20 | *.msm 21 | *.msp 22 | 23 | # Windows shortcuts 24 | *.lnk 25 | 26 | 27 | ### Xcode ### 28 | build/ 29 | *.pbxuser 30 | !default.pbxuser 31 | *.mode1v3 32 | !default.mode1v3 33 | *.mode2v3 34 | !default.mode2v3 35 | *.perspectivev3 36 | !default.perspectivev3 37 | xcuserdata 38 | *.xccheckout 39 | *.moved-aside 40 | DerivedData 41 | *.xcuserstate 42 | 43 | 44 | ### VisualStudio ### 45 | ## Ignore Visual Studio temporary files, build results, and 46 | ## files generated by popular Visual Studio add-ons. 47 | 48 | # User-specific files 49 | *.suo 50 | *.user 51 | *.userosscache 52 | *.sln.docstates 53 | 54 | # User-specific files (MonoDevelop/Xamarin Studio) 55 | *.userprefs 56 | 57 | # Build results 58 | [Dd]ebug/ 59 | [Dd]ebugPublic/ 60 | [Rr]elease/ 61 | [Rr]eleases/ 62 | x64/ 63 | x86/ 64 | build/ 65 | bld/ 66 | [Bb]in/ 67 | [Oo]bj/ 68 | 69 | # Visual Studo 2015 cache/options directory 70 | .vs/ 71 | 72 | # JetBrains IDE cache/options directory 73 | .idea/ 74 | 75 | # MSTest test Results 76 | [Tt]est[Rr]esult*/ 77 | [Bb]uild[Ll]og.* 78 | 79 | # NUNIT 80 | *.VisualState.xml 81 | TestResult.xml 82 | 83 | # Build Results of an ATL Project 84 | [Dd]ebugPS/ 85 | [Rr]eleasePS/ 86 | dlldata.c 87 | 88 | *_i.c 89 | *_p.c 90 | *_i.h 91 | *.ilk 92 | *.meta 93 | *.obj 94 | *.pch 95 | *.pdb 96 | *.pgc 97 | *.pgd 98 | *.rsp 99 | *.sbr 100 | *.tlb 101 | *.tli 102 | *.tlh 103 | *.tmp 104 | *.tmp_proj 105 | *.log 106 | *.vspscc 107 | *.vssscc 108 | .builds 109 | *.pidb 110 | *.svclog 111 | *.scc 112 | 113 | # Chutzpah Test files 114 | _Chutzpah* 115 | 116 | # Visual C++ cache files 117 | ipch/ 118 | *.aps 119 | *.ncb 120 | *.opensdf 121 | *.sdf 122 | *.cachefile 123 | 124 | # Visual Studio profiler 125 | *.psess 126 | *.vsp 127 | *.vspx 128 | 129 | # TFS 2012 Local Workspace 130 | $tf/ 131 | 132 | # Guidance Automation Toolkit 133 | *.gpState 134 | 135 | # ReSharper is a .NET coding add-in 136 | _ReSharper*/ 137 | *.[Rr]e[Ss]harper 138 | *.DotSettings.user 139 | 140 | # JustCode is a .NET coding addin-in 141 | .JustCode 142 | 143 | # TeamCity is a build add-in 144 | _TeamCity* 145 | 146 | # DotCover is a Code Coverage Tool 147 | *.dotCover 148 | 149 | # NCrunch 150 | _NCrunch_* 151 | .*crunch*.local.xml 152 | 153 | # MightyMoose 154 | *.mm.* 155 | AutoTest.Net/ 156 | 157 | # Web workbench (sass) 158 | .sass-cache/ 159 | 160 | # Installshield output folder 161 | [Ee]xpress/ 162 | 163 | # DocProject is a documentation generator add-in 164 | DocProject/buildhelp/ 165 | DocProject/Help/*.HxT 166 | DocProject/Help/*.HxC 167 | DocProject/Help/*.hhc 168 | DocProject/Help/*.hhk 169 | DocProject/Help/*.hhp 170 | DocProject/Help/Html2 171 | DocProject/Help/html 172 | 173 | # Click-Once directory 174 | publish/ 175 | 176 | # Publish Web Output 177 | *.[Pp]ublish.xml 178 | *.azurePubxml 179 | # TODO: Comment the next line if you want to checkin your web deploy settings 180 | # but database connection strings (with potential passwords) will be unencrypted 181 | *.pubxml 182 | *.publishproj 183 | 184 | # NuGet Packages #Disable for store local nupkg 185 | # *.nupkg 186 | # The packages folder can be ignored because of Package Restore 187 | **/packages/* 188 | # except build/, which is used as an MSBuild target. 189 | !**/packages/build/ 190 | # Uncomment if necessary however generally it will be regenerated when needed 191 | !**/packages/repositories.config 192 | *.lock.json 193 | *.nuget.targets 194 | *.nuget.props 195 | 196 | 197 | # Windows Azure Build Output 198 | csx/ 199 | *.build.csdef 200 | 201 | # Windows Store app package directory 202 | AppPackages/ 203 | 204 | # Others 205 | *.[Cc]ache 206 | ClientBin/ 207 | [Ss]tyle[Cc]op.* 208 | ~$* 209 | *~ 210 | *.dbmdl 211 | *.dbproj.schemaview 212 | *.pfx 213 | *.publishsettings 214 | node_modules/ 215 | bower_components/ 216 | 217 | # RIA/Silverlight projects 218 | Generated_Code/ 219 | 220 | # Backup & report files from converting an old project file 221 | # to a newer Visual Studio version. Backup files are not needed, 222 | # because we have git ;-) 223 | _UpgradeReport_Files/ 224 | Backup*/ 225 | UpgradeLog*.XML 226 | UpgradeLog*.htm 227 | 228 | # SQL Server files 229 | *.mdf 230 | *.ldf 231 | 232 | # Business Intelligence projects 233 | *.rdl.data 234 | *.bim.layout 235 | *.bim_*.settings 236 | 237 | # Microsoft Fakes 238 | FakesAssemblies/ 239 | 240 | # Node.js Tools for Visual Studio 241 | .ntvs_analysis.dat 242 | 243 | # Visual Studio 6 build log 244 | *.plg 245 | 246 | # Visual Studio 6 workspace options file 247 | *.opt 248 | 249 | 250 | ### XamarinStudio ### 251 | bin/ 252 | obj/ 253 | *.userprefs 254 | 255 | 256 | ### Objective-C ### 257 | # Xcode 258 | # 259 | build/ 260 | *.pbxuser 261 | !default.pbxuser 262 | *.mode1v3 263 | !default.mode1v3 264 | *.mode2v3 265 | !default.mode2v3 266 | *.perspectivev3 267 | !default.perspectivev3 268 | xcuserdata 269 | *.xccheckout 270 | *.moved-aside 271 | DerivedData 272 | *.hmap 273 | *.ipa 274 | *.xcuserstate 275 | 276 | # CocoaPods 277 | # 278 | # We recommend against adding the Pods directory to your .gitignore. However 279 | # you should judge for yourself, the pros and cons are mentioned at: 280 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 281 | # 282 | #Pods/ 283 | 284 | 285 | ### Eclipse ### 286 | *.pydevproject 287 | .metadata 288 | .gradle 289 | bin/ 290 | tmp/ 291 | *.tmp 292 | *.bak 293 | *.swp 294 | *~.nib 295 | local.properties 296 | .settings/ 297 | .loadpath 298 | 299 | # Eclipse Core 300 | .project 301 | 302 | # External tool builders 303 | .externalToolBuilders/ 304 | 305 | # Locally stored "Eclipse launch configurations" 306 | *.launch 307 | 308 | # CDT-specific 309 | .cproject 310 | 311 | # JDT-specific (Eclipse Java Development Tools) 312 | .classpath 313 | 314 | # PDT-specific 315 | .buildpath 316 | 317 | # sbteclipse plugin 318 | .target 319 | 320 | # TeXlipse plugin 321 | .texlipse 322 | 323 | 324 | ### OSX ### 325 | .DS_Store 326 | .AppleDouble 327 | .LSOverride 328 | 329 | # Icon must end with two \r 330 | Icon 331 | 332 | 333 | # Thumbnails 334 | ._* 335 | 336 | # Files that might appear in the root of a volume 337 | .DocumentRevisions-V100 338 | .fseventsd 339 | .Spotlight-V100 340 | .TemporaryItems 341 | .Trashes 342 | .VolumeIcon.icns 343 | 344 | # Directories potentially created on remote AFP share 345 | .AppleDB 346 | .AppleDesktop 347 | Network Trash Folder 348 | Temporary Items 349 | .apdisk 350 | 351 | 352 | ### Android ### 353 | # Built application files 354 | *.apk 355 | *.ap_ 356 | 357 | # Files for the Dalvik VM 358 | *.dex 359 | 360 | # Java class files 361 | *.class 362 | 363 | # Generated files 364 | bin/ 365 | gen/ 366 | 367 | # Gradle files 368 | .gradle/ 369 | build/ 370 | /*/build/ 371 | 372 | # Local configuration file (sdk path, etc) 373 | local.properties 374 | 375 | # Proguard folder generated by Eclipse 376 | proguard/ 377 | 378 | # Log Files 379 | *.log 380 | 381 | ### Android Patch ### 382 | gen-external-apklibs 383 | 384 | 385 | *.stackdump 386 | *.exe 387 | *.lock.json.stamp 388 | -------------------------------------------------------------------------------- /Binwell.Controls/FastGrid.Android/FastGrid.Android.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | AnyCPU 6 | {A25A0002-A767-4554-A6D5-31D91E7F3545} 7 | {EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 8 | {c9e5eea5-ca05-42a1-839b-61506e0a37df} 9 | Library 10 | Binwell.Controls.FastGrid.Android 11 | Binwell.Controls.FastGrid.Android 12 | True 13 | Resources 14 | Assets 15 | false 16 | v8.1 17 | Xamarin.Android.Net.AndroidClientHandler 18 | 19 | 20 | 21 | 22 | true 23 | portable 24 | false 25 | bin\Debug 26 | DEBUG; 27 | prompt 28 | 4 29 | None 30 | 31 | 32 | true 33 | pdbonly 34 | true 35 | bin\Release 36 | prompt 37 | 4 38 | true 39 | false 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | C:\Users\slava\.nuget\packages\xamarin.android.support.v7.recyclerview\27.0.2.1\lib\MonoAndroid81\Xamarin.Android.Support.v7.RecyclerView.dll 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 27.0.2.1 59 | 60 | 61 | 3.4.0.1008975 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | {7b749930-552f-429f-b303-18878fdb264c} 76 | FastGrid 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /Binwell.Controls/FastGrid.Android/FastGrid/EndlessRecyclerViewScrollListener.cs: -------------------------------------------------------------------------------- 1 | // Based on https://github.com/ardok/codepath/blob/master/TwitterClient/app/src/main/java/com/codepath/twitterclient/listeners/EndlessRecyclerViewScrollListener.java 2 | 3 | using System; 4 | using Android.Support.V7.Widget; 5 | using Android.Widget; 6 | using Binwell.Controls.FastGrid.FastGrid; 7 | 8 | namespace Binwell.Controls.FastGrid.Android.FastGrid 9 | { 10 | public class EndlessRecyclerViewScrollListener : RecyclerView.OnScrollListener 11 | { 12 | readonly int _visibleThreshold = 5; 13 | bool _loading = true; 14 | int _previousTotalItemCount; 15 | int _startScrollPosition; 16 | ScrollState _lastState = ScrollState.Idle; 17 | 18 | readonly RecyclerView.LayoutManager _layoutManager; 19 | readonly FastGridView _fastGridView; 20 | readonly ScrollRecyclerView _recyclerView; 21 | readonly float _density; 22 | 23 | public bool EnableLoadMore { get; set; } 24 | 25 | public event Action LoadMore; 26 | 27 | public EndlessRecyclerViewScrollListener(LinearLayoutManager layoutManager, 28 | FastGridView fastGridView, ScrollRecyclerView recyclerView) 29 | { 30 | _layoutManager = layoutManager; 31 | _fastGridView = fastGridView; 32 | _recyclerView = recyclerView; 33 | _density = recyclerView.Resources.DisplayMetrics.Density; 34 | } 35 | 36 | 37 | public static int GetLastVisibleItem(int[] lastVisibleItemPositions) 38 | { 39 | var maxSize = 0; 40 | for (var i = 0; i < lastVisibleItemPositions.Length; i++) 41 | if (i == 0 || lastVisibleItemPositions[i] > maxSize) 42 | maxSize = lastVisibleItemPositions[i]; 43 | return maxSize; 44 | } 45 | 46 | public override void OnScrollStateChanged(RecyclerView recyclerView, int newState) 47 | { 48 | base.OnScrollStateChanged(recyclerView, newState); 49 | var state = (ScrollState) newState; 50 | var x = _recyclerView.GetHorizontalScrollOffset() / _density; 51 | var y = _startScrollPosition / _density; 52 | 53 | if (_lastState == ScrollState.Idle && (state == ScrollState.TouchScroll || state == ScrollState.Fling)) 54 | _startScrollPosition = _recyclerView.GetVerticalScrollOffset(); 55 | 56 | if (state == ScrollState.TouchScroll || state == ScrollState.Fling) 57 | _fastGridView.RaiseOnStartScroll(x, y, 58 | state == ScrollState.TouchScroll ? ScrollActionType.Finger : ScrollActionType.Fling); 59 | 60 | if (_lastState == ScrollState.TouchScroll && (state == ScrollState.Fling || state == ScrollState.Idle)) 61 | _fastGridView.RaiseOnStopScroll(x, y, ScrollActionType.Finger, state == ScrollState.Idle); 62 | 63 | if (_lastState == ScrollState.Fling && state == ScrollState.Idle) 64 | _fastGridView.RaiseOnStopScroll(x, y, ScrollActionType.Fling, true); 65 | 66 | _lastState = state; 67 | } 68 | 69 | public override void OnScrolled(RecyclerView view, int dx, int dy) 70 | { 71 | if (dy == 0) return; 72 | _startScrollPosition += dy; 73 | _fastGridView.RaiseOnScroll(dy / _density, _recyclerView.GetHorizontalScrollOffset() / _density, 74 | _startScrollPosition / _density, ScrollActionType.Finger); 75 | 76 | 77 | if (!EnableLoadMore) return; 78 | var lastVisibleItemPosition = 0; 79 | var totalItemCount = _layoutManager.ItemCount; 80 | 81 | switch (_layoutManager) 82 | { 83 | case StaggeredGridLayoutManager manager: 84 | var lastVisibleItemPositions = manager.FindLastVisibleItemPositions(null); 85 | lastVisibleItemPosition = GetLastVisibleItem(lastVisibleItemPositions); 86 | break; 87 | case LinearLayoutManager _: 88 | lastVisibleItemPosition = ((LinearLayoutManager) _layoutManager).FindLastVisibleItemPosition(); 89 | break; 90 | } 91 | 92 | if (totalItemCount < _previousTotalItemCount) 93 | { 94 | _previousTotalItemCount = totalItemCount; 95 | if (totalItemCount == 0) 96 | _loading = true; 97 | } 98 | 99 | if (_loading && (totalItemCount > _previousTotalItemCount)) 100 | { 101 | _loading = false; 102 | _previousTotalItemCount = totalItemCount; 103 | } 104 | 105 | if (_loading || (lastVisibleItemPosition + _visibleThreshold) <= totalItemCount) return; 106 | 107 | LoadMore?.Invoke(); 108 | _loading = true; 109 | } 110 | 111 | public void ResetState() 112 | { 113 | _loading = true; 114 | _previousTotalItemCount = 0; 115 | } 116 | } 117 | } -------------------------------------------------------------------------------- /Binwell.Controls/FastGrid.Android/FastGrid/FastGridAdapter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Specialized; 4 | using System.Linq; 5 | using System.Reflection; 6 | using Android.Content; 7 | using Android.Support.V4.Widget; 8 | using Android.Support.V7.Widget; 9 | using Android.Util; 10 | using Android.Views; 11 | using Binwell.Controls.FastGrid.FastGrid; 12 | using Xamarin.Forms; 13 | using Xamarin.Forms.Platform.Android; 14 | using Size = Xamarin.Forms.Size; 15 | using View = Android.Views.View; 16 | 17 | namespace Binwell.Controls.FastGrid.Android.FastGrid { 18 | public class FastGridAdapter : RecyclerView.Adapter { 19 | readonly RecyclerView _recyclerView; 20 | IEnumerable _items; 21 | 22 | readonly DisplayMetrics _displayMetrics; 23 | readonly FastGridViewRenderer _fastGridViewRenderer; 24 | 25 | readonly FastGridView _fastGridView; 26 | readonly PropertyInfo _realParentProperty; 27 | 28 | FastGridView Element { get; } 29 | 30 | public IEnumerable Items { 31 | get => _items; 32 | set { 33 | if (_items is INotifyCollectionChanged oldCollection) { 34 | oldCollection.CollectionChanged -= NewCollection_CollectionChanged; 35 | } 36 | 37 | _items = value; 38 | if (_items is INotifyCollectionChanged newCollection) { 39 | newCollection.CollectionChanged += NewCollection_CollectionChanged; 40 | } 41 | 42 | NotifyDataSetChanged(); 43 | } 44 | } 45 | 46 | void NewCollection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { 47 | _fastGridViewRenderer.CalculateLayoutRects(); 48 | try { 49 | ((DefaultItemAnimator) _recyclerView.GetItemAnimator()).SupportsChangeAnimations = !Element.CollectionChangedWithoutAnimation; 50 | } 51 | catch { 52 | //ignored 53 | } 54 | 55 | RecyclerView.ItemAnimator animator = null; 56 | if (Element.CollectionChangedWithoutAnimation) { 57 | animator = _recyclerView.GetItemAnimator(); 58 | _recyclerView.SetItemAnimator(null); 59 | } 60 | 61 | switch (e.Action) { 62 | case NotifyCollectionChangedAction.Add: 63 | if (e.NewItems == null) return; 64 | var oneAdd = e.NewItems.Count == 1; 65 | if (oneAdd) NotifyItemInserted(e.NewStartingIndex); 66 | else NotifyItemRangeInserted(e.NewStartingIndex, e.NewItems.Count); 67 | break; 68 | case NotifyCollectionChangedAction.Remove: 69 | if (e.OldItems == null) return; 70 | var oneRemove = e.OldItems.Count == 1; 71 | if (oneRemove) NotifyItemRemoved(e.OldStartingIndex); 72 | else NotifyItemRangeRemoved(e.OldStartingIndex, e.OldItems.Count); 73 | break; 74 | case NotifyCollectionChangedAction.Replace: 75 | NotifyItemChanged(e.NewStartingIndex); 76 | break; 77 | case NotifyCollectionChangedAction.Move: 78 | NotifyItemMoved(e.OldStartingIndex, e.NewStartingIndex); 79 | break; 80 | case NotifyCollectionChangedAction.Reset: 81 | NotifyDataSetChanged(); 82 | break; 83 | } 84 | 85 | if (Element.CollectionChangedWithoutAnimation) 86 | _recyclerView.SetItemAnimator(animator); 87 | } 88 | 89 | public FastGridAdapter(IEnumerable items, RecyclerView recyclerView, FastGridView fastGridView, DisplayMetrics displayMetrics, FastGridViewRenderer fastGridViewRenderer) { 90 | Items = items; 91 | _recyclerView = recyclerView; 92 | Element = fastGridView; 93 | _displayMetrics = displayMetrics; 94 | _fastGridViewRenderer = fastGridViewRenderer; 95 | _fastGridView = fastGridView; 96 | _realParentProperty = typeof(Element).GetProperty("RealParent"); 97 | } 98 | 99 | public override int GetItemViewType(int position) { 100 | var item = Items.Cast().ElementAt(position); 101 | if (Element.ItemTemplateSelector is FastGridTemplateSelector selector) 102 | return selector.GetViewType(item, _fastGridView); 103 | return -1; 104 | } 105 | 106 | public override RecyclerView.ViewHolder OnCreateViewHolder(ViewGroup parent, int viewType) { 107 | var templateSelector = Element.ItemTemplateSelector; 108 | 109 | if (templateSelector == null) return new GridViewHolder(GetViewCell(parent.Context, null, parent, new Size(100, 100), null, viewType, Element), null); 110 | 111 | FastGridCell gridViewCell = null; 112 | var cellSize = Size.Zero; 113 | 114 | if (templateSelector.OnSelectTemplateByViewType(viewType, null) is FastGridDataTemplate template) { 115 | gridViewCell = template.CreateContent() as FastGridCell; 116 | cellSize = template.CellSize; 117 | } 118 | 119 | if (gridViewCell == null) return new GridViewHolder(GetViewCell(parent.Context, null, parent, new Size(100, 100), null, viewType, Element), null); 120 | 121 | 122 | cellSize.Width = cellSize.Width > 0 ? cellSize.Width : -1; 123 | cellSize.Height = cellSize.Height > 0 ? cellSize.Height : -1; 124 | 125 | //Without this line crashed in GetRenderer method for ListView 126 | _realParentProperty?.SetValue(gridViewCell, _fastGridView); 127 | var view = GetViewCell(parent.Context, gridViewCell, parent, cellSize, mMainView_Click, viewType, Element); 128 | 129 | _realParentProperty?.SetValue(gridViewCell, null); 130 | gridViewCell.Parent = _fastGridView; 131 | 132 | switch (viewType) { 133 | case 0: return new GridViewHolder0(view, gridViewCell.GetType()); 134 | case 1: return new GridViewHolder1(view, gridViewCell.GetType()); 135 | case 2: return new GridViewHolder2(view, gridViewCell.GetType()); 136 | case 3: return new GridViewHolder3(view, gridViewCell.GetType()); 137 | case 4: return new GridViewHolder4(view, gridViewCell.GetType()); 138 | case 5: return new GridViewHolder5(view, gridViewCell.GetType()); 139 | case 6: return new GridViewHolder6(view, gridViewCell.GetType()); 140 | case 7: return new GridViewHolder7(view, gridViewCell.GetType()); 141 | case 8: return new GridViewHolder8(view, gridViewCell.GetType()); 142 | case 9: return new GridViewHolder9(view, gridViewCell.GetType()); 143 | case 10: return new GridViewHolder10(view, gridViewCell.GetType()); 144 | case 11: return new GridViewHolder11(view, gridViewCell.GetType()); 145 | case 12: return new GridViewHolder12(view, gridViewCell.GetType()); 146 | case 13: return new GridViewHolder13(view, gridViewCell.GetType()); 147 | case 14: return new GridViewHolder14(view, gridViewCell.GetType()); 148 | case 15: return new GridViewHolder15(view, gridViewCell.GetType()); 149 | } 150 | 151 | return new GridViewHolder(view, gridViewCell.GetType()); 152 | } 153 | 154 | public static View GetViewCell(Context context, FastGridCell fastGridCell, ViewGroup parent, Size initialCellSize, EventHandler click, int viewType, FastGridView element) { 155 | if (fastGridCell == null) return new Space(context); 156 | fastGridCell.PrepareCell(initialCellSize); 157 | var view = FormsToNativeDroid.ConvertFormsToNative(context, fastGridCell.View, initialCellSize, context.Resources.DisplayMetrics.Density); 158 | if (view == null) return null; 159 | 160 | view.Tag = new ViewCellProperties { 161 | Cell = fastGridCell, 162 | ViewType = viewType, 163 | Size = new Rectangle(0, 0, initialCellSize.Width, initialCellSize.Height) 164 | }; 165 | 166 | fastGridCell.View.GestureRecognizers.Clear(); 167 | 168 | var tap = new TapGestureRecognizer { 169 | Command = new Command(() => { 170 | fastGridCell.ItemTapped(); 171 | click?.Invoke(view, EventArgs.Empty); 172 | }) 173 | }; 174 | fastGridCell.View.GestureRecognizers.Add(tap); 175 | 176 | return view; 177 | } 178 | 179 | 180 | public override void OnBindViewHolder(RecyclerView.ViewHolder holder, int position) { 181 | if (!(holder is GridViewHolder myHolder)) return; 182 | var item = Items.Cast().ElementAt(position); 183 | var properties = myHolder.View.Tag as ViewCellProperties; 184 | var cell = properties?.Cell; 185 | 186 | if (cell == null) return; 187 | try { 188 | cell.BindingContext = item; 189 | var pSize = properties.Size; 190 | 191 | if (pSize.Width > 0 && pSize.Height > 0) { 192 | cell.Layout(pSize); 193 | } 194 | else { 195 | var size = cell.View.Measure(properties.Size.Width, properties.Size.Height, MeasureFlags.IncludeMargins).Request; 196 | pSize.Width = pSize.Width > 0 ? pSize.Width : size.Width; 197 | pSize.Height = pSize.Height > 0 ? pSize.Height : size.Height; 198 | var d = _displayMetrics.Density; 199 | cell.Layout(pSize); 200 | myHolder.View.LayoutParameters = new ViewGroup.LayoutParams(ConvertDpToPixels(pSize.Width, d), ConvertDpToPixels(pSize.Height, d)); 201 | (myHolder.View as IVisualElementRenderer)?.Tracker?.UpdateLayout(); 202 | } 203 | 204 | } 205 | catch { 206 | //ignored 207 | } 208 | } 209 | 210 | static int ConvertDpToPixels(double dpValue, float density) { 211 | var pixels = (int) (dpValue * density); 212 | return pixels; 213 | } 214 | 215 | void mMainView_Click(object sender, EventArgs e) { 216 | if (sender == null) return; 217 | try { 218 | var position = _recyclerView.GetChildAdapterPosition((View) sender); 219 | var item = Items.Cast().ElementAt(position); 220 | Element.InvokeItemSelectedEvent(this, item); 221 | } 222 | catch { 223 | // ignored 224 | } 225 | } 226 | 227 | public override int ItemCount { 228 | get { 229 | var count = (Items as ICollection)?.Count ?? 0; 230 | return count; 231 | } 232 | } 233 | } 234 | 235 | internal sealed class ViewCellProperties : Java.Lang.Object { 236 | public FastGridCell Cell { get; set; } 237 | public int ViewType { get; set; } 238 | public EventHandler Click { get; set; } 239 | public Rectangle Size { get; set; } 240 | } 241 | } -------------------------------------------------------------------------------- /Binwell.Controls/FastGrid.Android/FastGrid/FastGridViewRenderer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.ComponentModel; 5 | using System.Linq; 6 | using Android.Content; 7 | using Android.Content.Res; 8 | using Android.Graphics; 9 | using Android.Runtime; 10 | using Android.Support.V4.Widget; 11 | using Android.Support.V7.Widget; 12 | using Android.Util; 13 | using Binwell.Controls.FastGrid.Android.FastGrid; 14 | using Binwell.Controls.FastGrid.FastGrid; 15 | using Xamarin.Forms; 16 | using Xamarin.Forms.Platform.Android; 17 | using Application = Android.App.Application; 18 | using Point = Xamarin.Forms.Point; 19 | using Size = Xamarin.Forms.Size; 20 | using View = Android.Views.View; 21 | 22 | [assembly: ExportRenderer(typeof(FastGridView), typeof(FastGridViewRenderer))] 23 | 24 | namespace Binwell.Controls.FastGrid.Android.FastGrid 25 | { 26 | public class FastGridViewRenderer : 27 | ViewRenderer, 28 | SwipeRefreshLayout.IOnRefreshListener, IGridViewProvider 29 | { 30 | readonly Orientation _orientation = Orientation.Undefined; 31 | 32 | ScrollRecyclerView _recyclerView; 33 | 34 | FastGridAdapter _adapter; 35 | int _columns = 1; 36 | 37 | GridLayoutManager _gridLayoutManager; 38 | float _density; 39 | SwipeRefreshLayoutWithDisabling _refresh; 40 | RecyclerView.ItemDecoration _paddingDecoration; 41 | 42 | GridSpanSizeLookup _sizeLookup; 43 | int _originalRefreshOffset; 44 | EndlessRecyclerViewScrollListener _scrollListener; 45 | 46 | public FastGridViewRenderer(Context context) : base(context) 47 | { 48 | } 49 | 50 | protected override void OnConfigurationChanged(Configuration newConfig) 51 | { 52 | base.OnConfigurationChanged(newConfig); 53 | if (newConfig.Orientation != _orientation) 54 | OnElementChanged( 55 | new ElementChangedEventArgs(Element, 56 | Element)); 57 | } 58 | 59 | protected override void OnElementChanged( 60 | ElementChangedEventArgs e) 61 | { 62 | base.OnElementChanged(e); 63 | if (e.NewElement == null) return; 64 | 65 | _density = Context.Resources.DisplayMetrics.Density; 66 | CreateRecyclerView(); 67 | 68 | e.NewElement.GridViewProvider = this; 69 | 70 | var refresh = new SwipeRefreshLayoutWithDisabling(Context); 71 | refresh.SetOnRefreshListener(this); 72 | refresh.Refreshing = e.NewElement.IsRefreshing; 73 | refresh.IsPullToRefreshEnabled = e.NewElement.IsPullToRefreshEnabled; 74 | _originalRefreshOffset = refresh.ProgressViewStartOffset; 75 | 76 | if (Element.RefreshTopOffset != -1) 77 | { 78 | refresh.SetProgressViewOffset(true, _originalRefreshOffset, 79 | (int) ((e.NewElement.RefreshTopOffset) * _density)); 80 | } 81 | 82 | refresh.AddView(_recyclerView, LayoutParams.MatchParent); 83 | _refresh = refresh; 84 | SetNativeControl(_refresh); 85 | 86 | _recyclerView.Enabled = e.NewElement.IsPullToRefreshEnabled; 87 | e.NewElement.GetScrollPositionCommand = new Command(GetScrollPosition); 88 | _scrollListener.EnableLoadMore = Element?.LoadMoreCommand != null; 89 | } 90 | 91 | void GetScrollPosition(object obj) 92 | { 93 | var func = obj as Func; 94 | if (func == null) return; 95 | var point = new Point(_recyclerView.GetHorizontalScrollOffset() / _density, 96 | _recyclerView.GetVerticalScrollOffset() / _density); 97 | func.Invoke(point); 98 | } 99 | 100 | protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e) 101 | { 102 | base.OnElementPropertyChanged(sender, e); 103 | if (e.PropertyName == FastGridView.ItemsSourceProperty.PropertyName) 104 | { 105 | CalculateLayoutRects(); 106 | if (_adapter != null && Element != null) _adapter.Items = Element.ItemsSource; 107 | _recyclerView?.GetLayoutManager()?.ScrollToPosition(0); 108 | } 109 | else if (e.PropertyName == VisualElement.WidthProperty.PropertyName || e.PropertyName == 110 | FastGridView.ItemTemplateSelectorProperty.PropertyName) 111 | { 112 | _recyclerView?.GetRecycledViewPool()?.Clear(); 113 | _recyclerView?.SetAdapter(null); 114 | _recyclerView?.SetAdapter(_adapter); 115 | CalculateLayoutRects(); 116 | _adapter?.NotifyDataSetChanged(); 117 | } 118 | else if (e.PropertyName == FastGridView.IsScrollEnabledProperty 119 | .PropertyName) 120 | { 121 | Device.BeginInvokeOnMainThread(() => 122 | { 123 | if (_recyclerView != null && Element != null) _recyclerView.Enabled = Element.IsScrollEnabled; 124 | }); 125 | } 126 | else if (e.PropertyName == 127 | FastGridView.IsRefreshingProperty.PropertyName) 128 | { 129 | if (_refresh != null && Element != null) 130 | _refresh.Refreshing = Element.IsRefreshing; 131 | } 132 | else if (e.PropertyName == FastGridView 133 | .IsPullToRefreshEnabledProperty.PropertyName) 134 | { 135 | if (_refresh != null && Element != null) 136 | { 137 | _refresh.IsPullToRefreshEnabled = Element.IsPullToRefreshEnabled; 138 | } 139 | } 140 | else if (e.PropertyName == FastGridView.RefreshTopOffsetProperty 141 | .PropertyName) 142 | { 143 | if (_refresh != null && Element != null) 144 | { 145 | if (Element.RefreshTopOffset != -1) 146 | { 147 | _refresh.Refreshing = false; 148 | var size = (int) (Element.RefreshTopOffset * _density); 149 | _refresh.SetProgressViewOffset(true, 0, size); 150 | _refresh.Refreshing = Element.IsRefreshing; 151 | } 152 | } 153 | } 154 | else if (e.PropertyName == FastGridView.LoadMoreCommandProperty 155 | .PropertyName) 156 | { 157 | Device.BeginInvokeOnMainThread(() => 158 | { 159 | if (_scrollListener != null) _scrollListener.EnableLoadMore = Element?.LoadMoreCommand != null; 160 | }); 161 | } 162 | } 163 | 164 | 165 | LinearLayoutManager _layoutManager; 166 | void CreateRecyclerView() 167 | { 168 | _recyclerView = new ScrollRecyclerView(Application.Context); 169 | _recyclerView.SetClipToPadding(false); 170 | _adapter = new FastGridAdapter(Element.ItemsSource, _recyclerView, Element, Resources.DisplayMetrics, 171 | this); 172 | if (Element.IsHorizontal) 173 | { 174 | _layoutManager = 175 | new LinearLayoutManager(Context, OrientationHelper.Horizontal, 176 | false); /*{AutoMeasureEnabled = true}*/ 177 | _recyclerView.HasFixedSize = true; 178 | CalculateLayoutRects(); 179 | } 180 | else 181 | { 182 | _gridLayoutManager = new SmoothGridLayoutManager(Context, _columns > 0 ? _columns : 1, 183 | OrientationHelper.Vertical, false) 184 | { 185 | RecyclerView = _recyclerView 186 | }; 187 | _recyclerView.HasFixedSize = true; 188 | _layoutManager = _gridLayoutManager; 189 | CalculateLayoutRects(); 190 | } 191 | 192 | _recyclerView.SetLayoutManager(_layoutManager); 193 | 194 | var scrollListener = new EndlessRecyclerViewScrollListener(_layoutManager, Element, _recyclerView) 195 | { 196 | EnableLoadMore = Element.LoadMoreCommand != null 197 | }; 198 | scrollListener.LoadMore += LoadMore; 199 | _recyclerView.AddOnScrollListener(scrollListener); 200 | _scrollListener = scrollListener; 201 | 202 | _recyclerView.HorizontalScrollBarEnabled = Element.IsHorizontal; 203 | _recyclerView.VerticalScrollBarEnabled = !Element.IsHorizontal; 204 | 205 | _recyclerView.SetAdapter(_adapter); 206 | } 207 | 208 | void LoadMore() 209 | { 210 | Element?.RaiseLoadMoreEvent(); 211 | } 212 | 213 | protected internal void CalculateLayoutRects() 214 | { 215 | if (Element == null || Element.Width < 10 || _layoutManager == null) return; 216 | 217 | var itemTemplate = Element.ItemTemplateSelector; 218 | if (!(itemTemplate is FastGridTemplateSelector templateSelector)) return; 219 | templateSelector.Prepare(); 220 | 221 | if (!Element.IsHorizontal) 222 | { 223 | var width = Element.Width; 224 | var widths = templateSelector.DataTemplates.Select(t => t.CellSize.Width); 225 | _columns = Math.Max(1, widths.Max(w => (int)(width / w))); 226 | 227 | _gridLayoutManager.SpanCount = _columns; 228 | if (_sizeLookup == null) 229 | { 230 | _sizeLookup = new GridSpanSizeLookup(); 231 | _gridLayoutManager.SetSpanSizeLookup(_sizeLookup); 232 | } 233 | 234 | _sizeLookup.MaxColumns = _columns; 235 | _sizeLookup.Width = width; 236 | } 237 | 238 | 239 | var source = Element.ItemsSource as ICollection; 240 | var numberOfItems = source?.Count ?? 0; 241 | 242 | var layoutInfo = new Size[numberOfItems]; 243 | if (numberOfItems == 0 || source == null) return; 244 | var density = _density; 245 | 246 | var items = source.Cast().ToArray(); 247 | 248 | for (var i = 0; i < numberOfItems; i++) 249 | { 250 | var item = items[i]; 251 | var size = GetSizeByItem(templateSelector, item); 252 | size.Height *= density; 253 | size.Width *= density; 254 | layoutInfo[i] = size; 255 | } 256 | 257 | var widthByPos = layoutInfo.Select(t => t.Width / density).ToList(); 258 | 259 | if (!Element.IsHorizontal) 260 | { 261 | _sizeLookup.WidthByPos = widthByPos; 262 | } 263 | } 264 | 265 | public class GridSpanSizeLookup : GridLayoutManager.SpanSizeLookup 266 | { 267 | public int MaxColumns { get; set; } 268 | public double Width { get; set; } 269 | public List WidthByPos { get; set; } 270 | 271 | public override int GetSpanSize(int position) 272 | { 273 | if (MaxColumns < 2 || Width < 1 || WidthByPos == null || WidthByPos.Count == 0 || 274 | WidthByPos.Count <= position) 275 | return 1; 276 | var span = MaxColumns + 1 - (int) (Width / WidthByPos[position]); 277 | span = span < 1 ? 1 : span; 278 | return Math.Min(span, MaxColumns); 279 | } 280 | } 281 | 282 | Size GetSizeByItem(FastGridTemplateSelector templateSelector, object item) 283 | { 284 | var cellSize = Size.Zero; 285 | if (templateSelector?.OnSelectTemplate(item) is FastGridDataTemplate template) 286 | cellSize = template.CellSize; 287 | 288 | return cellSize; 289 | } 290 | 291 | protected override void OnSizeChanged(int w, int h, int oldw, int oldh) 292 | { 293 | base.OnSizeChanged(w, h, oldw, oldh); 294 | UpdatePadding(); 295 | } 296 | 297 | void UpdatePadding() 298 | { 299 | _recyclerView.SetPadding((int) (Element.ContentPaddingLeft * _density), 300 | (int) (Element.ContentPaddingTop * _density), 301 | (int) (Element.ContentPaddingRight * _density), 302 | (int) (Element.ContentPaddingBottom * _density)); 303 | if (Element.IsHorizontal) 304 | { 305 | if (_paddingDecoration != null) 306 | { 307 | _recyclerView.RemoveItemDecoration(_paddingDecoration); 308 | } 309 | 310 | var cs = Element.ColumnSpacing; 311 | var rs = Element.RowSpacing; 312 | if (cs > 0 || rs > 0) 313 | { 314 | _paddingDecoration = 315 | new HorizontalSpacesItemDecoration(ConvertDpToPixels((float) cs), ConvertDpToPixels((int) rs)); 316 | _recyclerView.AddItemDecoration(_paddingDecoration); 317 | } 318 | } 319 | else 320 | UpdateGridLayout(); 321 | } 322 | 323 | void UpdateGridLayout() 324 | { 325 | if (_paddingDecoration != null) 326 | _recyclerView.RemoveItemDecoration(_paddingDecoration); 327 | 328 | _recyclerView.InvalidateItemDecorations(); 329 | var cs = Element.ColumnSpacing; 330 | var rs = Element.RowSpacing; 331 | if (cs > 0 || rs > 0) 332 | { 333 | _paddingDecoration = new HorizontalSpacesItemDecoration(ConvertDpToPixels((int) Element.ColumnSpacing), 334 | ConvertDpToPixels((int) Element.RowSpacing)); 335 | _recyclerView.AddItemDecoration(_paddingDecoration); 336 | } 337 | } 338 | 339 | int ConvertDpToPixels(float dpValue) 340 | { 341 | var pixels = (int) ((dpValue) * Resources.DisplayMetrics.Density); 342 | return pixels; 343 | } 344 | 345 | public void OnRefresh() 346 | { 347 | Element?.Refresh(); 348 | } 349 | 350 | public void ReloadData() 351 | { 352 | CalculateLayoutRects(); 353 | _adapter.Items = Element.ItemsSource; 354 | } 355 | 356 | public void ScrollToItem(int index, bool animated) 357 | { 358 | if (!animated) 359 | _recyclerView.ScrollToPosition(index); 360 | else 361 | _recyclerView.SmoothScrollToPosition(index); 362 | } 363 | 364 | public void ScrollTo(float x, float y) 365 | { 366 | _recyclerView?.GetLayoutManager()?.ScrollToPosition((int) (x)); 367 | } 368 | 369 | public int GetVisibleItemsCount() 370 | { 371 | var layoutManager = (LinearLayoutManager) _recyclerView.GetLayoutManager(); 372 | var firstVisiblePosition = layoutManager.FindFirstCompletelyVisibleItemPosition(); 373 | var lastVisiblePosition = layoutManager.FindLastCompletelyVisibleItemPosition(); 374 | return lastVisiblePosition - firstVisiblePosition; 375 | } 376 | } 377 | 378 | 379 | public class HorizontalSpacesItemDecoration : RecyclerView.ItemDecoration 380 | { 381 | readonly int _columnSpacing; 382 | readonly int _rowSpacing; 383 | 384 | public HorizontalSpacesItemDecoration(int columnSpacing, int rowSpacing) 385 | { 386 | _rowSpacing = rowSpacing; 387 | _columnSpacing = columnSpacing; 388 | } 389 | 390 | public override void GetItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) 391 | { 392 | outRect.Left = _columnSpacing / 2; 393 | outRect.Right = _columnSpacing / 2; 394 | outRect.Bottom = _rowSpacing / 2; 395 | outRect.Top = _rowSpacing / 2; 396 | } 397 | } 398 | 399 | public class ScrollRecyclerView : RecyclerView 400 | { 401 | public ScrollRecyclerView(IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference, transfer) 402 | { 403 | } 404 | 405 | 406 | public ScrollRecyclerView(Context context, IAttributeSet attrs, int defStyle) : base(context, attrs, defStyle) 407 | { 408 | } 409 | 410 | 411 | public ScrollRecyclerView(Context context, IAttributeSet attrs) : base(context, attrs) 412 | { 413 | } 414 | 415 | 416 | public ScrollRecyclerView(Context context) : base(context) 417 | { 418 | } 419 | 420 | public int GetVerticalScrollOffset() 421 | { 422 | return ComputeVerticalScrollOffset(); 423 | } 424 | 425 | public int GetHorizontalScrollOffset() 426 | { 427 | return ComputeHorizontalScrollOffset(); 428 | } 429 | } 430 | } -------------------------------------------------------------------------------- /Binwell.Controls/FastGrid.Android/FastGrid/FormsToNativeDroid.cs: -------------------------------------------------------------------------------- 1 | using Android.Content; 2 | using Android.Views; 3 | using Xamarin.Forms; 4 | using Xamarin.Forms.Platform.Android; 5 | using View = Android.Views.View; 6 | 7 | namespace Binwell.Controls.FastGrid.Android.FastGrid 8 | { 9 | public static class FormsToNativeDroid 10 | { 11 | public static View ConvertFormsToNative(Context context, Xamarin.Forms.View view, Size size, float density) 12 | { 13 | if (view == null) return null; 14 | if (Platform.GetRenderer(view) == null) 15 | Platform.SetRenderer(view, Platform.CreateRendererWithContext(view, context)); 16 | 17 | var vRenderer = Platform.GetRenderer(view); 18 | var nativeView = vRenderer.View; 19 | var dpW = size.Width > 0 ? ConvertDpToPixels(size.Width, density) : ViewGroup.LayoutParams.WrapContent; 20 | var dpH = size.Height > 0 ? ConvertDpToPixels(size.Height, density) : ViewGroup.LayoutParams.WrapContent; 21 | 22 | nativeView.LayoutParameters = new ViewGroup.LayoutParams(dpW, dpH); 23 | 24 | return nativeView; 25 | } 26 | 27 | 28 | static int ConvertDpToPixels(double dpValue, float density) { 29 | var pixels = (int)(dpValue * density); 30 | return pixels; 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /Binwell.Controls/FastGrid.Android/FastGrid/SmoothGridLayoutManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Android.Content; 3 | using Android.Graphics; 4 | using Android.Runtime; 5 | using Android.Support.V7.Widget; 6 | using Android.Util; 7 | 8 | namespace Binwell.Controls.FastGrid.Android.FastGrid 9 | { 10 | public class SmoothGridLayoutManager : GridLayoutManager 11 | { 12 | readonly Context _context; 13 | public RecyclerView RecyclerView { get; set; } 14 | 15 | public SmoothGridLayoutManager(Context context, int spanCount, int orientation, bool reverseLayout) : base(context, spanCount, orientation, reverseLayout) 16 | { 17 | _context = context; 18 | } 19 | 20 | public SmoothGridLayoutManager(Context context, int spanCount) : base(context, spanCount) 21 | { 22 | _context = context; 23 | } 24 | 25 | public SmoothGridLayoutManager(Context context, IAttributeSet attrs, int defStyleAttr, int defStyleRes) : base(context, attrs, defStyleAttr, defStyleRes) 26 | { 27 | _context = context; 28 | } 29 | 30 | public override void SmoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) 31 | { 32 | LinearSmoothScroller smoothScroller = new CustomLinearSmoothScroller(_context, this); 33 | 34 | smoothScroller.TargetPosition = (position); 35 | StartSmoothScroll(smoothScroller); 36 | } 37 | 38 | public override int ComputeVerticalScrollOffset(RecyclerView.State state) { 39 | 40 | var firstItemView = RecyclerView.GetChildAt(0); 41 | var lastItemView = RecyclerView.GetChildAt(RecyclerView.ChildCount - 1); 42 | var firstItem = RecyclerView.GetChildLayoutPosition(firstItemView); 43 | var lastItem = RecyclerView.GetChildLayoutPosition(lastItemView); 44 | var itemsBefore = firstItem; 45 | if (firstItemView == null) return 0; 46 | var laidOutArea = GetDecoratedBottom(lastItemView) - GetDecoratedTop(firstItemView); 47 | var itemRange = lastItem - firstItem + 1; 48 | var avgSizePerRow = (float)laidOutArea / itemRange; 49 | 50 | var offset = (int)(itemsBefore * avgSizePerRow + PaddingTop - GetDecoratedTop(firstItemView)); 51 | return offset; 52 | } 53 | 54 | 55 | 56 | internal class CustomLinearSmoothScroller : LinearSmoothScroller 57 | { 58 | static float MILLISECONDS_PER_INCH = 75f; 59 | readonly SmoothGridLayoutManager _layoutManager; 60 | 61 | public CustomLinearSmoothScroller(IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference, transfer) 62 | { 63 | } 64 | 65 | public CustomLinearSmoothScroller(Context context, SmoothGridLayoutManager layoutManager) : base(context) 66 | { 67 | _layoutManager = layoutManager; 68 | } 69 | 70 | public override PointF ComputeScrollVectorForPosition(int targetPosition) 71 | { 72 | return _layoutManager?.ComputeScrollVectorForPosition(targetPosition); 73 | } 74 | 75 | protected override float CalculateSpeedPerPixel(DisplayMetrics displayMetrics) 76 | { 77 | return MILLISECONDS_PER_INCH / (float) displayMetrics.DensityDpi; 78 | } 79 | } 80 | } 81 | } -------------------------------------------------------------------------------- /Binwell.Controls/FastGrid.Android/FastGrid/SwipeRefreshLayoutWithDisabling.cs: -------------------------------------------------------------------------------- 1 | using Android.Content; 2 | using Android.Support.V4.Widget; 3 | 4 | namespace Binwell.Controls.FastGrid.Android.FastGrid 5 | { 6 | public class SwipeRefreshLayoutWithDisabling : SwipeRefreshLayout 7 | { 8 | 9 | public SwipeRefreshLayoutWithDisabling(Context context) : base(context) 10 | { 11 | } 12 | 13 | public bool IsPullToRefreshEnabled { get; set; } 14 | 15 | public override bool CanChildScrollUp() 16 | { 17 | if (!IsPullToRefreshEnabled) return true; 18 | return base.CanChildScrollUp(); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Binwell.Controls/FastGrid.Android/FastGrid/ViewHolders.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Android.Support.V7.Widget; 3 | using Android.Views; 4 | 5 | namespace Binwell.Controls.FastGrid.Android.FastGrid 6 | { 7 | public class GridViewHolder : RecyclerView.ViewHolder 8 | { 9 | public Type ContentType { get; set; } 10 | public View View { get; set; } 11 | 12 | public GridViewHolder(View view, Type templateType) : base(view) 13 | { 14 | ContentType = templateType; 15 | View = view; 16 | } 17 | } 18 | 19 | public class GridViewHolder0 : GridViewHolder 20 | { 21 | public GridViewHolder0(View view, Type templateType) : base(view, templateType) 22 | { 23 | } 24 | } 25 | 26 | public class GridViewHolder1 : GridViewHolder 27 | { 28 | public GridViewHolder1(View view, Type templateType) : base(view, templateType) 29 | { 30 | } 31 | } 32 | 33 | public class GridViewHolder2 : GridViewHolder 34 | { 35 | public GridViewHolder2(View view, Type templateType) : base(view, templateType) 36 | { 37 | } 38 | } 39 | 40 | public class GridViewHolder3 : GridViewHolder 41 | { 42 | public GridViewHolder3(View view, Type templateType) : base(view, templateType) 43 | { 44 | } 45 | } 46 | 47 | public class GridViewHolder4 : GridViewHolder 48 | { 49 | public GridViewHolder4(View view, Type templateType) : base(view, templateType) 50 | { 51 | } 52 | } 53 | 54 | public class GridViewHolder5 : GridViewHolder 55 | { 56 | public GridViewHolder5(View view, Type templateType) : base(view, templateType) 57 | { 58 | } 59 | } 60 | 61 | public class GridViewHolder6 : GridViewHolder 62 | { 63 | public GridViewHolder6(View view, Type templateType) : base(view, templateType) 64 | { 65 | } 66 | } 67 | 68 | public class GridViewHolder7 : GridViewHolder 69 | { 70 | public GridViewHolder7(View view, Type templateType) : base(view, templateType) 71 | { 72 | } 73 | } 74 | 75 | public class GridViewHolder8 : GridViewHolder 76 | { 77 | public GridViewHolder8(View view, Type templateType) : base(view, templateType) 78 | { 79 | } 80 | } 81 | 82 | public class GridViewHolder9 : GridViewHolder 83 | { 84 | public GridViewHolder9(View view, Type templateType) : base(view, templateType) 85 | { 86 | } 87 | } 88 | 89 | public class GridViewHolder10 : GridViewHolder 90 | { 91 | public GridViewHolder10(View view, Type templateType) : base(view, templateType) 92 | { 93 | } 94 | } 95 | 96 | public class GridViewHolder11 : GridViewHolder 97 | { 98 | public GridViewHolder11(View view, Type templateType) : base(view, templateType) 99 | { 100 | } 101 | 102 | } 103 | 104 | public class GridViewHolder12 : GridViewHolder 105 | { 106 | public GridViewHolder12(View view, Type templateType) : base(view, templateType) 107 | { 108 | } 109 | } 110 | 111 | public class GridViewHolder13 : GridViewHolder 112 | { 113 | public GridViewHolder13(View view, Type templateType) : base(view, templateType) 114 | { 115 | } 116 | } 117 | 118 | public class GridViewHolder14 : GridViewHolder 119 | { 120 | public GridViewHolder14(View view, Type templateType) : base(view, templateType) 121 | { 122 | } 123 | } 124 | 125 | public class GridViewHolder15 : GridViewHolder 126 | { 127 | public GridViewHolder15(View view, Type templateType) : base(view, templateType) 128 | { 129 | } 130 | } 131 | } -------------------------------------------------------------------------------- /Binwell.Controls/FastGrid.iOS/FastGrid.iOS.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | iPhoneSimulator 6 | 8.0.30703 7 | 2.0 8 | {EBC99883-B9E5-45F7-826B-81ADFF29E100} 9 | {FEACFBD2-3405-455C-9665-78FE426C6842};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 10 | {6143fdea-f3c2-4a09-aafa-6e230626515e} 11 | Library 12 | Binwell.Controls.FastGrid.iOS 13 | Binwell.Controls.FastGrid.iOS 14 | NSUrlSessionHandler 15 | 16 | 17 | 18 | 19 | true 20 | full 21 | false 22 | bin\iPhoneSimulator\Debug 23 | DEBUG 24 | prompt 25 | 4 26 | false 27 | x86_64 28 | None 29 | true 30 | 31 | 32 | none 33 | true 34 | bin\iPhoneSimulator\Release 35 | prompt 36 | 4 37 | None 38 | x86_64 39 | false 40 | 41 | 42 | true 43 | full 44 | false 45 | bin\iPhone\Debug 46 | DEBUG 47 | prompt 48 | 4 49 | false 50 | ARM64 51 | iPhone Developer 52 | true 53 | 54 | 55 | none 56 | true 57 | bin\iPhone\Release 58 | prompt 59 | 4 60 | ARM64 61 | false 62 | iPhone Developer 63 | 64 | 65 | none 66 | True 67 | bin\iPhone\Ad-Hoc 68 | prompt 69 | 4 70 | False 71 | ARM64 72 | True 73 | Automatic:AdHoc 74 | iPhone Distribution 75 | 76 | 77 | none 78 | True 79 | bin\iPhone\AppStore 80 | prompt 81 | 4 82 | False 83 | ARM64 84 | Automatic:AppStore 85 | iPhone Distribution 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | {15EDF08A-A5D1-4D9A-ABB3-EC8F0ED87395} 105 | FastGrid 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 3.4.0.1008975 114 | 115 | 116 | -------------------------------------------------------------------------------- /Binwell.Controls/FastGrid.iOS/FastGrid/FastCollectionView.cs: -------------------------------------------------------------------------------- 1 | // Based on https://github.com/twintechs/TwinTechsFormsLib 2 | // Special thanks to Twin Technologies from Binwell Ltd. 3 | 4 | // Distributed under Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0 5 | 6 | using CoreGraphics; 7 | using UIKit; 8 | 9 | namespace Binwell.Controls.FastGrid.iOS.FastGrid 10 | { 11 | public sealed class FastCollectionView : UICollectionView 12 | { 13 | public bool SelectionEnable { get; set; } 14 | 15 | public FastCollectionView() : this(default(CGRect)) 16 | { 17 | } 18 | 19 | public FastCollectionView(CGRect rect) : base(rect, new LeftFlowCollectionViewLayout()) 20 | { 21 | AutoresizingMask = UIViewAutoresizing.None; 22 | ContentMode = UIViewContentMode.ScaleAspectFill; 23 | } 24 | 25 | public double RowSpacing 26 | { 27 | get => (CollectionViewLayout as UICollectionViewFlowLayout)?.MinimumLineSpacing ?? 0; 28 | set 29 | { 30 | if (CollectionViewLayout is UICollectionViewFlowLayout layout) layout.MinimumLineSpacing = (float) value; 31 | } 32 | } 33 | 34 | public double ColumnSpacing 35 | { 36 | get => (CollectionViewLayout as UICollectionViewFlowLayout)?.MinimumInteritemSpacing ?? 0; 37 | set 38 | { 39 | if (CollectionViewLayout is UICollectionViewFlowLayout layout) layout.MinimumInteritemSpacing = (float) value; 40 | } 41 | } 42 | 43 | } 44 | } -------------------------------------------------------------------------------- /Binwell.Controls/FastGrid.iOS/FastGrid/FastCollectionViewCell.cs: -------------------------------------------------------------------------------- 1 | // Based on https://github.com/twintechs/TwinTechsFormsLib 2 | // Special thanks to Twin Technologies from Binwell Ltd. 3 | 4 | // Distributed under Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0 5 | 6 | using System; 7 | using Binwell.Controls.FastGrid.FastGrid; 8 | using CoreGraphics; 9 | using Foundation; 10 | using UIKit; 11 | using Xamarin.Forms; 12 | using Xamarin.Forms.Internals; 13 | using Xamarin.Forms.Platform.iOS; 14 | 15 | namespace Binwell.Controls.FastGrid.iOS.FastGrid { 16 | public sealed class FastCollectionViewCell : UICollectionViewCell { 17 | UIView _view; 18 | object _originalBindingContext; 19 | UIGestureRecognizer _tapGestureRecognizer; 20 | CGSize _lastSize; 21 | internal Action CellTapped; 22 | 23 | FastGridCell ViewCell { get; set; } 24 | 25 | public static UIView ConvertFormsToNative(View view, Rectangle size) { 26 | if (view == null) return null; 27 | try { 28 | if (Platform.GetRenderer(view) == null) 29 | Platform.SetRenderer(view, Platform.CreateRenderer(view)); 30 | var vRenderer = Platform.GetRenderer(view); 31 | var viewGroup = vRenderer.NativeView; 32 | view.Layout(size); 33 | return viewGroup; 34 | } 35 | catch { 36 | return new UIView(new CGRect(0, 0, 1, 1)); 37 | } 38 | } 39 | 40 | public void RecycleCell(object data, FastGridTemplateSelector dataTemplate, VisualElement parent) { 41 | if (ViewCell == null) { 42 | var cellSize = new Size(Bounds.Width, Bounds.Height); 43 | 44 | if (!(dataTemplate is FastGridTemplateSelector templateSelector)) throw new NotSupportedException(@"DataTemplate should be FastGridTemplateSelector"); 45 | 46 | var template = templateSelector.SelectTemplate(data) as FastGridDataTemplate; 47 | ViewCell = template?.CreateContent() as FastGridCell; 48 | cellSize = template?.CellSize ?? cellSize; 49 | 50 | if (ViewCell != null) { 51 | ViewCell.BindingContext = data; 52 | ViewCell.PrepareCell(cellSize); 53 | ViewCell.Parent = parent; 54 | 55 | _originalBindingContext = data; 56 | _view = ConvertFormsToNative(ViewCell.View, new Rectangle(new Point(0, 0), cellSize)); 57 | } 58 | 59 | if (_view == null) { 60 | return; 61 | } 62 | 63 | _view.AutoresizingMask = UIViewAutoresizing.All; 64 | _view.ContentMode = UIViewContentMode.ScaleAspectFit; 65 | _view.ClipsToBounds = true; 66 | 67 | ContentView.AddSubview(_view); 68 | } 69 | else if (data == _originalBindingContext) { 70 | ViewCell.BindingContext = _originalBindingContext; 71 | } 72 | else { 73 | ViewCell.BindingContext = data; 74 | } 75 | 76 | var gr = GestureRecognizers; 77 | if (gr != null && gr.Length > 0) { 78 | gr.ForEach(RemoveGestureRecognizer); 79 | } 80 | 81 | _tapGestureRecognizer = new UITapGestureRecognizer(Tapped); 82 | AddGestureRecognizer(_tapGestureRecognizer); 83 | } 84 | 85 | void Tapped() { 86 | ViewCell.ItemTapped(); 87 | if (ViewCell?.BindingContext != null) 88 | CellTapped?.Invoke(ViewCell.BindingContext); 89 | } 90 | 91 | [Export("initWithFrame:")] 92 | public FastCollectionViewCell(CGRect frame) : base(frame) { 93 | } 94 | 95 | protected override void Dispose(bool disposing) { 96 | if (_tapGestureRecognizer != null) 97 | Device.BeginInvokeOnMainThread(() => { 98 | RemoveGestureRecognizer(_tapGestureRecognizer); 99 | _tapGestureRecognizer = null; 100 | }); 101 | ; 102 | _view = null; 103 | _originalBindingContext = null; 104 | if (ViewCell != null) { 105 | ViewCell.BindingContext = null; 106 | ViewCell.Parent = null; 107 | ViewCell = null; 108 | } 109 | 110 | base.Dispose(disposing); 111 | } 112 | 113 | public override void LayoutSubviews() { 114 | base.LayoutSubviews(); 115 | 116 | if (_lastSize.Equals(CGSize.Empty) || !_lastSize.Equals(Frame.Size) && ViewCell != null) { 117 | ViewCell?.View?.Layout(Frame.ToRectangle()); 118 | _lastSize = Frame.Size; 119 | } 120 | 121 | if (_view != null && !_view.Frame.Equals(Bounds)) 122 | _view.Frame = Bounds; 123 | } 124 | } 125 | } -------------------------------------------------------------------------------- /Binwell.Controls/FastGrid.iOS/FastGrid/FastCollectionViewDataSource.cs: -------------------------------------------------------------------------------- 1 | // Based on https://github.com/twintechs/TwinTechsFormsLib 2 | // Special thanks to Twin Technologies from Binwell Ltd. 3 | 4 | // Distributed under Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0 5 | 6 | using System; 7 | using Foundation; 8 | using UIKit; 9 | 10 | namespace Binwell.Controls.FastGrid.iOS.FastGrid 11 | { 12 | public class FastCollectionViewDataSource : UICollectionViewSource 13 | { 14 | public delegate UICollectionViewCell OnGetCell(UICollectionView collectionView, NSIndexPath indexPath); 15 | 16 | public delegate int OnRowsInSection(UICollectionView collectionView, nint section); 17 | 18 | public delegate int OnNumberOfSections(UICollectionView collectionView); 19 | 20 | OnGetCell _onGetCell; 21 | OnRowsInSection _onRowsInSection; 22 | OnNumberOfSections _onNumberOfSections; 23 | 24 | public FastCollectionViewDataSource(OnGetCell onGetCell, OnRowsInSection onRowsInSection, OnNumberOfSections onNumberOfSections) 25 | { 26 | _onGetCell = onGetCell; 27 | _onRowsInSection = onRowsInSection; 28 | _onNumberOfSections = onNumberOfSections; 29 | } 30 | 31 | protected override void Dispose(bool disposing) 32 | { 33 | _onGetCell = null; 34 | _onRowsInSection = null; 35 | _onNumberOfSections = null; 36 | 37 | base.Dispose(disposing); 38 | } 39 | 40 | public override nint NumberOfSections(UICollectionView collectionView) 41 | { 42 | return _onNumberOfSections(collectionView); 43 | } 44 | 45 | public override nint GetItemsCount(UICollectionView collectionView, nint section) 46 | { 47 | return _onRowsInSection(collectionView, section); 48 | } 49 | 50 | public override UICollectionViewCell GetCell(UICollectionView collectionView, NSIndexPath indexPath) 51 | { 52 | return _onGetCell(collectionView, indexPath); 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /Binwell.Controls/FastGrid.iOS/FastGrid/FastCollectionViewDelegate.cs: -------------------------------------------------------------------------------- 1 | // Based on https://github.com/twintechs/TwinTechsFormsLib 2 | // Special thanks to Twin Technologies from Binwell Ltd. 3 | 4 | // Distributed under Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0 5 | 6 | using System; 7 | using Binwell.Controls.FastGrid.FastGrid; 8 | using CoreGraphics; 9 | using Foundation; 10 | using UIKit; 11 | 12 | namespace Binwell.Controls.FastGrid.iOS.FastGrid 13 | { 14 | public class FastCollectionViewDelegate : UICollectionViewDelegateFlowLayout 15 | { 16 | public delegate void OnScrolled(CGPoint contentOffset, ScrollActionType type); 17 | 18 | public delegate CGSize OnGetSizeForItem(UICollectionView collectionView, UICollectionViewLayout layout, NSIndexPath indexPath); 19 | 20 | public delegate nfloat OnMinimumInterItemSpacing(UICollectionView collectionView, int section); 21 | 22 | public delegate UIEdgeInsets OnSectionInsetForItem(UICollectionView collectionView, int index); 23 | 24 | OnScrolled _onScrolled; 25 | OnGetSizeForItem _onGetSizeForItem; 26 | readonly Action _onScrollEnded; 27 | readonly Action _onScrollStarted; 28 | 29 | public FastCollectionViewDelegate(OnScrolled onScrolled, OnGetSizeForItem onGetSizeForItem, Action onScrollStarted, Action onScrollEnded) { 30 | 31 | _onScrolled = onScrolled; 32 | _onGetSizeForItem = onGetSizeForItem; 33 | _onScrollStarted = onScrollStarted; 34 | _onScrollEnded = onScrollEnded; 35 | } 36 | 37 | protected override void Dispose(bool disposing) 38 | { 39 | _onScrolled = null; 40 | _onGetSizeForItem = null; 41 | base.Dispose(disposing); 42 | } 43 | 44 | public override void Scrolled(UIScrollView scrollView) 45 | { 46 | _onScrolled(scrollView.ContentOffset, ScrollActionType.Finger); 47 | } 48 | 49 | public override void DecelerationEnded(UIScrollView scrollView) { 50 | _onScrollEnded?.Invoke(scrollView.ContentOffset, ScrollActionType.Fling, true); 51 | } 52 | 53 | 54 | public override void DraggingStarted(UIScrollView scrollView) { 55 | _onScrollStarted?.Invoke(scrollView.ContentOffset, ScrollActionType.Finger); 56 | } 57 | 58 | public override void DraggingEnded(UIScrollView scrollView, bool willDecelerate) { 59 | _onScrollEnded?.Invoke(scrollView.ContentOffset, ScrollActionType.Finger, !willDecelerate); 60 | } 61 | 62 | public override CGSize GetSizeForItem(UICollectionView collectionView, UICollectionViewLayout layout, 63 | NSIndexPath indexPath) 64 | { 65 | return _onGetSizeForItem(collectionView, layout, indexPath); 66 | } 67 | 68 | public override CGSize GetReferenceSizeForHeader(UICollectionView collectionView, UICollectionViewLayout layout, nint section) 69 | { 70 | return new CGSize(0, 0); 71 | } 72 | 73 | public override CGSize GetReferenceSizeForFooter(UICollectionView collectionView, UICollectionViewLayout layout, nint section) 74 | { 75 | return new CGSize(0, 0); 76 | } 77 | } 78 | } -------------------------------------------------------------------------------- /Binwell.Controls/FastGrid.iOS/FastGrid/FastGridViewRenderer.cs: -------------------------------------------------------------------------------- 1 | // Based on https://github.com/twintechs/TwinTechsFormsLib 2 | // Special thanks to Twin Technologies from Binwell Ltd. 3 | 4 | // Distributed under Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0 5 | 6 | using System; 7 | using System.Collections; 8 | using System.Collections.Generic; 9 | using System.Collections.Specialized; 10 | using System.Linq; 11 | using Binwell.Controls.FastGrid.FastGrid; 12 | using Binwell.Controls.FastGrid.iOS.FastGrid; 13 | using CoreGraphics; 14 | using Foundation; 15 | using ObjCRuntime; 16 | using UIKit; 17 | using Xamarin.Forms; 18 | using Xamarin.Forms.Platform.iOS; 19 | 20 | [assembly: ExportRenderer(typeof(FastGridView), typeof(FastGridViewRenderer))] 21 | namespace Binwell.Controls.FastGrid.iOS.FastGrid 22 | { 23 | public class FastGridViewRenderer : ViewRenderer, IGridViewProvider 24 | { 25 | readonly List _cellTypes = new List(); 26 | FastCollectionView _fastCollectionView; 27 | NSIndexPath _initialIndex; 28 | UIRefreshControl _refreshControl; 29 | readonly object _lock = new object(); 30 | 31 | public static void Init() 32 | { 33 | var temp = DateTime.Now; 34 | } 35 | 36 | public IList Source 37 | { 38 | get => _source; 39 | private set { 40 | if (_source is INotifyCollectionChanged oldCollection) 41 | { 42 | oldCollection.CollectionChanged -= DataCollectionChanged; 43 | } 44 | _source = value; 45 | if (_source is INotifyCollectionChanged newCollection) 46 | { 47 | newCollection.CollectionChanged += DataCollectionChanged; 48 | } 49 | } 50 | } 51 | 52 | bool _isAllowLoadMore = true; 53 | bool _loadMoreEnabled; 54 | IList _source; 55 | 56 | protected override void OnElementChanged(ElementChangedEventArgs e) 57 | { 58 | base.OnElementChanged(e); 59 | 60 | if (e.NewElement == null) return; 61 | 62 | e.NewElement.GridViewProvider = this; 63 | _loadMoreEnabled = e.NewElement.LoadMoreCommand != null; 64 | 65 | _fastCollectionView = new FastCollectionView 66 | { 67 | AllowsMultipleSelection = false, 68 | SelectionEnable = e.NewElement.SelectionEnabled, 69 | BackgroundColor = Element.BackgroundColor.ToUIColor(), 70 | RowSpacing = Element.RowSpacing, 71 | ColumnSpacing = Element.ColumnSpacing, 72 | ContentInset = new UIEdgeInsets((nfloat) Element.ContentPaddingTop, (nfloat) Element.ContentPaddingLeft, 73 | (nfloat) Element.ContentPaddingBottom, (nfloat) Element.ContentPaddingRight), 74 | ShowsHorizontalScrollIndicator = false, 75 | AlwaysBounceVertical = !e.NewElement.IsHorizontal, 76 | CanCancelContentTouches = false, 77 | Frame = Bounds, 78 | Bounds = Bounds 79 | }; 80 | 81 | 82 | var flowLayout = (UICollectionViewFlowLayout) _fastCollectionView.CollectionViewLayout; 83 | 84 | if (flowLayout != null) 85 | { 86 | if (e.NewElement.IsHorizontal) 87 | flowLayout.ScrollDirection = UICollectionViewScrollDirection.Horizontal; 88 | 89 | flowLayout.SectionInset = new UIEdgeInsets((nfloat) Element.SectionPaddingTop, (nfloat)Element.SectionPaddingLeft, 90 | (nfloat) Element.SectionPaddingBottom, 0); 91 | } 92 | 93 | if (e.NewElement.IsPullToRefreshEnabled) 94 | { 95 | _refreshControl = new UIRefreshControl(); 96 | _refreshControl.AddTarget(this, new Selector(@"pullToRefresh"), UIControlEvent.ValueChanged); 97 | _fastCollectionView.AddSubview(_refreshControl); 98 | } 99 | 100 | Unbind(e.OldElement); 101 | 102 | Source = e.NewElement.ItemsSource as IList; 103 | RegisterCellTypes(e.NewElement.ItemTemplateSelector); 104 | SetDataSource(_fastCollectionView); 105 | _fastCollectionView.WeakDelegate = new FastCollectionViewDelegate(HandleOnScrolled, GetSizeForItem, HandleScrollStarted, HandleScrollEnded); 106 | _fastCollectionView.ReloadData(); 107 | _fastCollectionView.ScrollEnabled = Element.IsScrollEnabled; 108 | ScrollToInitialIndex(); 109 | 110 | SetNativeControl(_fastCollectionView); 111 | e.NewElement.GetScrollPositionCommand = new Command(GetScrollPosition); 112 | } 113 | 114 | void GetScrollPosition(object obj) { 115 | var func = obj as Func; 116 | if (func == null) return; 117 | var offset = _fastCollectionView.ContentOffset; 118 | var point = new Point(offset.X, offset.Y); 119 | func.Invoke(point); 120 | } 121 | 122 | void SetDataSource(UICollectionView collectionView) 123 | { 124 | if (collectionView == null) return; 125 | 126 | if (UIDevice.CurrentDevice.CheckSystemVersion(10, 0)) 127 | collectionView.PrefetchingEnabled = false; 128 | 129 | collectionView.WeakDataSource = new FastCollectionViewDataSource(GetCell, RowsInSection, NumberOfSections); 130 | } 131 | 132 | [Export("pullToRefresh")] 133 | // ReSharper disable once UnusedMember.Local 134 | void Refresh() 135 | { 136 | if (Element != null && Element.IsPullToRefreshEnabled) 137 | Element?.Refresh(); 138 | } 139 | 140 | CGSize GetSizeForItem(UICollectionView collectionView, UICollectionViewLayout layout, NSIndexPath indexPath) { 141 | var templateSelector = Element.ItemTemplateSelector; 142 | if (Source == null || Source.Count < indexPath.Row || templateSelector==null) return CGSize.Empty; 143 | var item = Source[indexPath.Row]; 144 | if (item == null) return CGSize.Empty; 145 | var key = templateSelector.GetKey(item) ?? item.GetType().Name; 146 | var size = templateSelector.GetSizesByKey(key); 147 | 148 | return new CGSize(size.Width, size.Height); 149 | } 150 | 151 | void RegisterCellTypes(FastGridTemplateSelector templateSelector) { 152 | if (templateSelector!=null) 153 | foreach (var template in templateSelector.DataTemplates) { 154 | var contain = _cellTypes.Contains(template.Key); 155 | if (contain) continue; 156 | _fastCollectionView.RegisterClassForCell(typeof(FastCollectionViewCell), new NSString(template.Key)); 157 | _cellTypes.Add(template.Key); 158 | } 159 | } 160 | 161 | protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) 162 | { 163 | base.OnElementPropertyChanged(sender, e); 164 | 165 | var gridView = sender as FastGridView; 166 | if (!(_fastCollectionView?.CollectionViewLayout is UICollectionViewFlowLayout flowLayout)) return; 167 | switch (e.PropertyName) 168 | { 169 | case "ItemTemplate": 170 | case "ItemsSource": 171 | if (gridView?.ItemTemplateSelector == null) break; 172 | Source = gridView.ItemsSource as IList; 173 | RegisterCellTypes(gridView.ItemTemplateSelector); 174 | _fastCollectionView.ReloadData(); 175 | ScrollToInitialIndex(); 176 | break; 177 | case "LoadMoreCommand": 178 | _loadMoreEnabled = Element?.LoadMoreCommand != null; 179 | break; 180 | case "IsScrollEnabled": 181 | Device.BeginInvokeOnMainThread(() => _fastCollectionView.ScrollEnabled = Element.IsScrollEnabled); 182 | break; 183 | case "IsHorizontal": 184 | flowLayout.ScrollDirection = Element.IsHorizontal 185 | ? UICollectionViewScrollDirection.Horizontal 186 | : UICollectionViewScrollDirection.Vertical; 187 | break; 188 | case "IsPullToRefreshEnabled": 189 | if (_refreshControl != null) break; 190 | _refreshControl = new UIRefreshControl(); 191 | _refreshControl.AddTarget(this, new Selector(@"pullToRefresh"), UIControlEvent.ValueChanged); 192 | _fastCollectionView.AddSubview(_refreshControl); 193 | break; 194 | case "ContentPaddingLeft": 195 | case "ContentPaddingTop": 196 | case "ContentPaddingBottom": 197 | case "ContentPaddingRight": 198 | _fastCollectionView.ContentInset = new UIEdgeInsets((float) Element.ContentPaddingTop, 199 | (float) Element.ContentPaddingLeft, (float) Element.ContentPaddingBottom, 200 | (float) Element.ContentPaddingRight); 201 | break; 202 | case "SectionPaddingLeft": 203 | case "SectionPaddingBottom": 204 | case "SectionPaddingTop": 205 | flowLayout.SectionInset = new UIEdgeInsets((float)Element.SectionPaddingTop, (float)Element.SectionPaddingLeft, 206 | (float)Element.SectionPaddingBottom, 0); 207 | break; 208 | case "IsRefreshing": 209 | if (_refreshControl != null && Element != null) 210 | { 211 | if (Element.IsRefreshing && ! _refreshControl.Refreshing) 212 | _refreshControl.BeginRefreshing(); 213 | else if (!Element.IsRefreshing && _refreshControl.Refreshing) 214 | _refreshControl.EndRefreshing(); 215 | } 216 | break; 217 | case "Width": 218 | case "Height": 219 | _fastCollectionView.Frame = new CGRect(Element.X, Element.Y, Math.Abs(Element.Width) < 0.1 ? Bounds.Width : Element.Width, Math.Abs(Element.Height) < 0.1 ? Bounds.Height : Element.Height); 220 | break; 221 | } 222 | } 223 | 224 | void Unbind(FastGridView oldElement) 225 | { 226 | if (oldElement == null) return; 227 | 228 | if (oldElement.ItemsSource is INotifyCollectionChanged itemsSource) 229 | itemsSource.CollectionChanged -= DataCollectionChanged; 230 | } 231 | 232 | 233 | 234 | void DataCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { 235 | if (_fastCollectionView == null) return; 236 | lock (_lock) { 237 | void Change() { 238 | int count; 239 | var paths = new List(); 240 | switch (e.Action) { 241 | case NotifyCollectionChangedAction.Add: 242 | if (e.NewItems == null) return; 243 | count = e.NewItems.Count; 244 | 245 | if (count == 0) return; 246 | for (var i = 0; i < count; i++) { 247 | paths.Add(NSIndexPath.FromRowSection(e.NewStartingIndex + i, 0)); 248 | } 249 | 250 | _fastCollectionView.InsertItems(paths.ToArray()); 251 | break; 252 | case NotifyCollectionChangedAction.Remove: 253 | if (e.OldItems == null) return; 254 | count = e.OldItems.Count; 255 | if (count == 0) return; 256 | 257 | for (var i = 0; i < count; i++) { 258 | paths.Add(NSIndexPath.FromRowSection(e.OldStartingIndex + i, 0)); 259 | } 260 | 261 | _fastCollectionView.DeleteItems(paths.ToArray()); 262 | break; 263 | case NotifyCollectionChangedAction.Replace: 264 | count = e.NewItems.Count; 265 | if (count == 0) return; 266 | for (var i = 0; i < count; i++) { 267 | paths.Add(NSIndexPath.FromRowSection(e.OldStartingIndex + i, 0)); 268 | } 269 | 270 | _fastCollectionView.ReloadItems(paths.ToArray()); 271 | break; 272 | case NotifyCollectionChangedAction.Move: 273 | _fastCollectionView.MoveItem(NSIndexPath.Create(e.OldStartingIndex), NSIndexPath.Create(e.NewStartingIndex)); 274 | break; 275 | case NotifyCollectionChangedAction.Reset: 276 | _fastCollectionView.ReloadData(); 277 | break; 278 | } 279 | } 280 | if (Element.CollectionChangedWithoutAnimation) PerformWithoutAnimation(Change); 281 | else Change(); 282 | } 283 | } 284 | 285 | void HandleScrollStarted(CGPoint contentOffset, ScrollActionType scrollActionType) { 286 | Element.RaiseOnStartScroll(contentOffset.X, contentOffset.Y, scrollActionType); 287 | } 288 | 289 | void HandleScrollEnded(CGPoint contentOffset, ScrollActionType scrollActionType, bool fullStop) { 290 | Element.RaiseOnStopScroll(contentOffset.X,contentOffset.Y, scrollActionType, fullStop); 291 | } 292 | 293 | void HandleOnScrolled(CGPoint contentOffset, ScrollActionType type) 294 | { 295 | if (Element == null) return; 296 | Element.RaiseOnScroll(contentOffset.X, contentOffset.X, contentOffset.Y, type); 297 | 298 | if (Control != null && !Control.IsFirstResponder) 299 | { 300 | Control.BecomeFirstResponder(); 301 | UIApplication.SharedApplication.SendAction(new Selector(@"resignFirstResponder"), null, null, null); 302 | } 303 | 304 | if (!_loadMoreEnabled || _fastCollectionView == null) return; 305 | 306 | 307 | if (Element.IsHorizontal || _fastCollectionView.ContentSize.Height < _fastCollectionView.Bounds.Height || contentOffset.Y==-1) return; 308 | 309 | var dy = _fastCollectionView.ContentSize.Height - _fastCollectionView.Bounds.Height - contentOffset.Y; 310 | 311 | if (dy > 40 && !_isAllowLoadMore) 312 | _isAllowLoadMore = true; 313 | 314 | if (dy >= 30 || !_isAllowLoadMore) return; 315 | 316 | _isAllowLoadMore = false; 317 | Element.RaiseLoadMoreEvent(); 318 | } 319 | 320 | void ScrollToInitialIndex() 321 | { 322 | if (_fastCollectionView?.DataSource == null) return; 323 | 324 | ScrollToItem(0, false); 325 | _initialIndex = null; 326 | } 327 | 328 | public int RowsInSection(UICollectionView collectionView, nint sectionNumber) 329 | { 330 | if (Element?.ItemTemplateSelector == null) return 0; 331 | var list = Source as ICollection; 332 | var count = list?.Count; 333 | return count ?? 0; 334 | } 335 | 336 | int NumberOfSections(UICollectionView collectionView) 337 | { 338 | if (Element?.ItemTemplateSelector == null) return 0; 339 | return Source != null ? 1 : 0; 340 | } 341 | 342 | public UICollectionViewCell GetCell(UICollectionView collectionView, NSIndexPath indexPath) { 343 | 344 | var item = Source?[indexPath.Row]; 345 | if (item == null) return null; 346 | 347 | var templateSelector = Element.ItemTemplateSelector; 348 | if (templateSelector == null) return null; 349 | var key = templateSelector.GetKey(item); 350 | try { 351 | if (!(collectionView.DequeueReusableCell(new NSString(key), indexPath) is FastCollectionViewCell collectionCell)) { 352 | return null; 353 | } 354 | 355 | collectionCell.RecycleCell(item, Element?.ItemTemplateSelector, Element); 356 | 357 | if (collectionView is FastCollectionView gridCollectionView && gridCollectionView.SelectionEnable && collectionCell.CellTapped == null) 358 | collectionCell.CellTapped = CellTapped; 359 | 360 | return collectionCell; 361 | } 362 | catch (Exception e) { 363 | throw new NotSupportedException($@"Check the key ""{key}"" is declared in FastGridTemplateSelector", e); 364 | } 365 | } 366 | 367 | void CellTapped(object obj) 368 | { 369 | Element?.InvokeItemSelectedEvent(this, obj); 370 | } 371 | 372 | public void ReloadData() 373 | { 374 | if (_fastCollectionView != null) 375 | InvokeOnMainThread(_fastCollectionView.ReloadData); 376 | } 377 | 378 | public void ScrollToItem(int row, bool animated) 379 | { 380 | ScrollToItem(row, animated, UICollectionViewScrollPosition.None); 381 | } 382 | 383 | public void ScrollTo(float x, float y) { 384 | _fastCollectionView?.SetContentOffset(new CGPoint(x,y), true); 385 | } 386 | 387 | public void ScrollToItem(int row, bool animated, UICollectionViewScrollPosition position) 388 | { 389 | var indexPath = NSIndexPath.FromRowSection(row, 0); 390 | if (_fastCollectionView != null && _fastCollectionView.NumberOfSections() > 0 && 391 | _fastCollectionView.NumberOfItemsInSection(0) > 0) 392 | InvokeOnMainThread( 393 | () => _fastCollectionView?.ScrollToItem(indexPath, position, animated)); 394 | else 395 | _initialIndex = indexPath; 396 | } 397 | 398 | protected override void Dispose(bool disposing) 399 | { 400 | _refreshControl?.RemoveTarget(this, new Selector(@"pullToRefresh"), UIControlEvent.ValueChanged); 401 | _refreshControl?.Dispose(); 402 | _refreshControl = null; 403 | 404 | _initialIndex?.Dispose(); 405 | 406 | _fastCollectionView = null; 407 | Source = null; 408 | 409 | base.Dispose(disposing); 410 | } 411 | 412 | public int GetVisibleItemsCount() 413 | { 414 | var firstCell = _fastCollectionView.VisibleCells.FirstOrDefault(); 415 | if (firstCell == null) return 0; 416 | var first = _fastCollectionView.IndexPathForCell(firstCell); 417 | 418 | // TODO Improve for support multiple cell types 419 | var size = GetSizeForItem(_fastCollectionView, null, first); 420 | var cnt = (int) (_fastCollectionView.Bounds.Height / size.Height)-1; 421 | return cnt; 422 | } 423 | } 424 | } -------------------------------------------------------------------------------- /Binwell.Controls/FastGrid.iOS/FastGrid/LeftFlowCollectionViewLayout.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using CoreGraphics; 3 | using UIKit; 4 | 5 | namespace Binwell.Controls.FastGrid.iOS.FastGrid 6 | { 7 | public class LeftFlowCollectionViewLayout : UICollectionViewFlowLayout 8 | { 9 | public override UICollectionViewLayoutAttributes[] LayoutAttributesForElementsInRect(CGRect rect) 10 | { 11 | var attributes = base.LayoutAttributesForElementsInRect(rect); 12 | 13 | if (ScrollDirection == UICollectionViewScrollDirection.Horizontal) 14 | return attributes; 15 | 16 | var maxY = -1.0; 17 | var leftMargin = SectionInset.Left; 18 | var w = CollectionView.Frame.Width; 19 | 20 | foreach (var layoutAttribute in attributes) 21 | { 22 | var frame = layoutAttribute.Frame; 23 | var x = leftMargin; 24 | 25 | if (x > SectionInset.Left && leftMargin + frame.Width > w) 26 | { 27 | x = SectionInset.Left; 28 | leftMargin = SectionInset.Left + frame.Width; 29 | } 30 | else 31 | { 32 | x = leftMargin; 33 | leftMargin += frame.Width; 34 | } 35 | 36 | frame.X = x; 37 | layoutAttribute.Frame = frame; 38 | maxY = Math.Max(layoutAttribute.Frame.GetMaxY(), maxY); 39 | } 40 | 41 | return attributes; 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /Binwell.Controls/FastGrid/FastGrid.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | netstandard2.0 4 | Binwell.Controls.FastGrid 5 | Binwell.Controls.FastGrid 6 | 7 | 8 | 9 | pdbonly 10 | true 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Binwell.Controls/FastGrid/FastGrid/FastGridCell.cs: -------------------------------------------------------------------------------- 1 | // Based on https://github.com/twintechs/TwinTechsFormsLib 2 | // Special thanks to Twin Technologies from Binwell Ltd. 3 | 4 | // Distributed under Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0 5 | 6 | using System; 7 | using System.ComponentModel; 8 | using Xamarin.Forms; 9 | 10 | namespace Binwell.Controls.FastGrid.FastGrid { 11 | [ContentProperty("View")] 12 | public abstract class FastGridCell : ContentView, IDisposable { 13 | public View View { 14 | get => Content; 15 | set => Content = value; 16 | } 17 | 18 | public bool IsInitialized { get; private set; } 19 | public Size CellSize { get; set; } 20 | 21 | object _currentBindingContext; 22 | 23 | public void PrepareCell(Size cellSize) { 24 | CellSize = cellSize; 25 | InitializeCell(); 26 | if (BindingContext != null) 27 | SetupCell(false); 28 | IsInitialized = true; 29 | } 30 | 31 | protected override void OnBindingContextChanged() { 32 | base.OnBindingContextChanged(); 33 | 34 | if (_currentBindingContext is INotifyPropertyChanged notifyPropertyChanged) 35 | notifyPropertyChanged.PropertyChanged -= OnBindingContextPropertyChanged; 36 | 37 | _currentBindingContext = BindingContext; 38 | 39 | if (_currentBindingContext is INotifyPropertyChanged newNotifyPropertyChanged) 40 | newNotifyPropertyChanged.PropertyChanged += OnBindingContextPropertyChanged; 41 | 42 | if (IsInitialized) 43 | SetupCell(true); 44 | } 45 | 46 | protected abstract void InitializeCell(); 47 | 48 | public virtual void ItemTapped() {} 49 | 50 | protected abstract void SetupCell(bool isRecycled); 51 | 52 | public virtual void Dispose() { 53 | if (_currentBindingContext is INotifyPropertyChanged notifyPropertyChanged) 54 | notifyPropertyChanged.PropertyChanged -= OnBindingContextPropertyChanged; 55 | 56 | _currentBindingContext = null; 57 | } 58 | 59 | protected virtual void OnBindingContextPropertyChanged(object sender, PropertyChangedEventArgs e) { 60 | } 61 | } 62 | } -------------------------------------------------------------------------------- /Binwell.Controls/FastGrid/FastGrid/FastGridDataTemplate.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Xamarin.Forms; 3 | 4 | namespace Binwell.Controls.FastGrid.FastGrid 5 | { 6 | public class FastGridDataTemplate : DataTemplate 7 | { 8 | public string Key { get; } 9 | public Size CellSize { get; } 10 | 11 | public FastGridDataTemplate(string key, Type cellType, Size cellSize): base(cellType) 12 | { 13 | Key = key; 14 | CellSize = cellSize; 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Binwell.Controls/FastGrid/FastGrid/FastGridEventArgs.cs: -------------------------------------------------------------------------------- 1 | // Based on https://github.com/twintechs/TwinTechsFormsLib 2 | // Special thanks to Twin Technologies from Binwell Ltd. 3 | 4 | // Distributed under Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0 5 | 6 | using System; 7 | 8 | namespace Binwell.Controls.FastGrid.FastGrid 9 | { 10 | public class FastGridEventArgs : EventArgs 11 | { 12 | public FastGridEventArgs(T value) 13 | { 14 | Value = value; 15 | } 16 | 17 | public T Value { get; } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Binwell.Controls/FastGrid/FastGrid/FastGridTemplateSelector.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using Xamarin.Forms; 4 | 5 | namespace Binwell.Controls.FastGrid.FastGrid 6 | { 7 | public class FastGridTemplateSelector 8 | { 9 | public readonly List DataTemplates = new List(); 10 | protected readonly Dictionary DataTemplateViewTypes = new Dictionary(); 11 | 12 | public FastGridTemplateSelector(params FastGridDataTemplate[] dataTemplates) 13 | { 14 | foreach (var dataTemplate in dataTemplates) 15 | DataTemplates.Add(dataTemplate); 16 | } 17 | 18 | public DataTemplate SelectTemplate(object item) 19 | { 20 | return OnSelectTemplate(item); 21 | } 22 | 23 | public string GetKey(object item) 24 | { 25 | var template = OnSelectTemplate(item) as FastGridDataTemplate; 26 | return template?.Key; 27 | } 28 | 29 | public virtual DataTemplate OnSelectTemplate(object item) { 30 | if (item == null) return null; 31 | var key = item.GetType().Name; 32 | return DataTemplates.FirstOrDefault(dt => dt.Key == key); 33 | } 34 | 35 | public FastGridTemplateSelector Prepare() 36 | { 37 | DataTemplateViewTypes.Clear(); 38 | var id = 0; 39 | foreach (var dataTemplate in DataTemplates) 40 | { 41 | var key = dataTemplate.Key; 42 | DataTemplateViewTypes.Add(id++,key); 43 | } 44 | return this; 45 | } 46 | 47 | public virtual int GetViewType(object item, BindableObject container) 48 | { 49 | var key = item.GetType().Name; 50 | return DataTemplateViewTypes.FirstOrDefault(dt => dt.Value == key).Key; 51 | } 52 | 53 | public DataTemplate OnSelectTemplateByViewType(int type, BindableObject container) 54 | { 55 | if (!DataTemplateViewTypes.ContainsKey(type)) return null; 56 | var key = DataTemplateViewTypes[type]; 57 | return DataTemplates.FirstOrDefault(dt => dt.Key == key); 58 | } 59 | 60 | public Dictionary GetSizesByViewType() 61 | { 62 | return DataTemplateViewTypes.ToDictionary(t => t.Key, t=> DataTemplates.First(dt => dt.Key == t.Value).CellSize); 63 | } 64 | 65 | public Size GetSizesByKey(string key) 66 | { 67 | var size = DataTemplates?.FirstOrDefault(dt => dt.Key == key)?.CellSize ?? Size.Zero; 68 | if (double.IsNaN(size.Width)) size.Width = 0; 69 | if (double.IsNaN(size.Height)) size.Height = 0; 70 | return size; 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Binwell.Controls/FastGrid/FastGrid/FastGridView.cs: -------------------------------------------------------------------------------- 1 | // Based on https://github.com/twintechs/TwinTechsFormsLib 2 | // Special thanks to Twin Technologies from Binwell Ltd. 3 | 4 | // Distributed under Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0 5 | 6 | using System; 7 | using System.Collections; 8 | using System.Windows.Input; 9 | using Xamarin.Forms; 10 | 11 | namespace Binwell.Controls.FastGrid.FastGrid 12 | { 13 | 14 | public interface IGridViewProvider 15 | { 16 | void ReloadData(); 17 | void ScrollToItem(int index, bool animated); 18 | void ScrollTo(float x, float y); 19 | int GetVisibleItemsCount(); 20 | } 21 | 22 | public class FastGridView : ContentView, IScrollAwareElement, IDisposable 23 | { 24 | int _initialIndex; 25 | IGridViewProvider _gridViewProvider; 26 | 27 | public bool SelectionEnabled { get; set; } = true; 28 | public bool CollectionChangedWithoutAnimation { get; set; } 29 | 30 | public event EventHandler> ItemSelected; 31 | 32 | public static readonly BindableProperty ItemsSourceProperty = BindableProperty.Create(nameof(ItemsSource), 33 | typeof(IEnumerable), typeof(FastGridView)); 34 | 35 | public static readonly BindableProperty ItemTemplateSelectorProperty = BindableProperty.Create( 36 | nameof(ItemTemplateSelector), 37 | typeof(FastGridTemplateSelector), typeof(FastGridView)); 38 | 39 | public static readonly BindableProperty RefreshCommandProperty = BindableProperty.Create( 40 | nameof(RefreshCommand), typeof(ICommand), typeof(FastGridView)); 41 | 42 | public static readonly BindableProperty LoadMoreCommandProperty = 43 | BindableProperty.Create(nameof(LoadMoreCommand), typeof(ICommand), typeof(FastGridView)); 44 | 45 | public static readonly BindableProperty ItemSelectedCommandProperty = 46 | BindableProperty.Create(nameof(ItemSelectedCommand), typeof(ICommand), typeof(FastGridView)); 47 | 48 | public static readonly BindableProperty RowSpacingProperty = BindableProperty.Create(nameof(RowSpacing), 49 | typeof(double), typeof(FastGridView), 0.0); 50 | 51 | public static readonly BindableProperty IsScrollEnabledProperty = 52 | BindableProperty.Create(nameof(IsScrollEnabled), typeof(bool), typeof(FastGridView), true); 53 | 54 | public static readonly BindableProperty ColumnSpacingProperty = BindableProperty.Create(nameof(ColumnSpacing), 55 | typeof(double), typeof(FastGridView), 0.0); 56 | 57 | public static readonly BindableProperty ContentPaddingLeftProperty = 58 | BindableProperty.Create(nameof(ContentPaddingLeft), typeof(double), typeof(FastGridView), 0.0); 59 | 60 | public static readonly BindableProperty ContentPaddingRightProperty = 61 | BindableProperty.Create(nameof(ContentPaddingRight), typeof(double), typeof(FastGridView), 0.0); 62 | 63 | public static readonly BindableProperty ContentPaddingBottomProperty = 64 | BindableProperty.Create(nameof(ContentPaddingBottom), typeof(double), typeof(FastGridView), 0.0); 65 | 66 | public static readonly BindableProperty SectionPaddingLeftProperty = 67 | BindableProperty.Create(nameof(SectionPaddingLeft), typeof(double), typeof(FastGridView), default(double), 68 | BindingMode.Default); 69 | 70 | public static readonly BindableProperty SectionPaddingBottomProperty = 71 | BindableProperty.Create(nameof(SectionPaddingBottom), typeof(double), typeof(FastGridView), 0.0); 72 | 73 | public static readonly BindableProperty SectionPaddingTopProperty = 74 | BindableProperty.Create(nameof(SectionPaddingTop), typeof(double), typeof(FastGridView), 0.0); 75 | 76 | public static readonly BindableProperty ContentPaddingTopProperty = 77 | BindableProperty.Create(nameof(ContentPaddingTop), typeof(double), typeof(FastGridView), 0.0); 78 | 79 | public static readonly BindableProperty IsHorizontalProperty = BindableProperty.Create(nameof(IsHorizontal), 80 | typeof(bool), typeof(FastGridView), false); 81 | 82 | public static readonly BindableProperty IsPullToRefreshEnabledProperty = 83 | BindableProperty.Create(nameof(IsPullToRefreshEnabled), typeof(bool), typeof(FastGridView), false); 84 | 85 | public static readonly BindableProperty IsRefreshingProperty = BindableProperty.Create(nameof(IsRefreshing), 86 | typeof(bool), typeof(FastGridView), false); 87 | 88 | public static readonly BindableProperty RefreshTopOffsetProperty = 89 | BindableProperty.Create(nameof(RefreshTopOffset), typeof(double), typeof(FastGridView), -1d, 90 | BindingMode.Default); 91 | 92 | public static readonly BindableProperty GetScrollPositionCommandProperty = 93 | BindableProperty.Create(nameof(GetScrollPositionCommand), typeof(ICommand), typeof(FastGridView), 94 | default(ICommand), BindingMode.Default); 95 | 96 | public ICommand GetScrollPositionCommand 97 | { 98 | get => (ICommand) GetValue(GetScrollPositionCommandProperty); 99 | set => SetValue(GetScrollPositionCommandProperty, value); 100 | } 101 | 102 | public double RefreshTopOffset 103 | { 104 | get => (double) GetValue(RefreshTopOffsetProperty); 105 | set => SetValue(RefreshTopOffsetProperty, value); 106 | } 107 | 108 | public IGridViewProvider GridViewProvider 109 | { 110 | get => _gridViewProvider; 111 | set 112 | { 113 | _gridViewProvider = value; 114 | _initialIndex = 0; 115 | _gridViewProvider?.ScrollToItem(_initialIndex, false); 116 | } 117 | } 118 | 119 | public IEnumerable ItemsSource 120 | { 121 | get => (IEnumerable) GetValue(ItemsSourceProperty); 122 | set => SetValue(ItemsSourceProperty, value); 123 | } 124 | 125 | public ICommand RefreshCommand 126 | { 127 | get => (ICommand) GetValue(RefreshCommandProperty); 128 | set => SetValue(RefreshCommandProperty, value); 129 | } 130 | 131 | public ICommand ItemSelectedCommand 132 | { 133 | get => (ICommand) GetValue(ItemSelectedCommandProperty); 134 | set => SetValue(ItemSelectedCommandProperty, value); 135 | } 136 | 137 | public ICommand LoadMoreCommand 138 | { 139 | get => (ICommand) GetValue(LoadMoreCommandProperty); 140 | set => SetValue(LoadMoreCommandProperty, value); 141 | } 142 | 143 | public FastGridTemplateSelector ItemTemplateSelector 144 | { 145 | get => (FastGridTemplateSelector) GetValue(ItemTemplateSelectorProperty); 146 | set => SetValue(ItemTemplateSelectorProperty, value); 147 | } 148 | 149 | public double RowSpacing 150 | { 151 | get => (double) GetValue(RowSpacingProperty); 152 | set => SetValue(RowSpacingProperty, value); 153 | } 154 | 155 | public double ColumnSpacing 156 | { 157 | get => (double) GetValue(ColumnSpacingProperty); 158 | set => SetValue(ColumnSpacingProperty, value); 159 | } 160 | 161 | public bool IsPullToRefreshEnabled 162 | { 163 | get => (bool) GetValue(IsPullToRefreshEnabledProperty); 164 | set => SetValue(IsPullToRefreshEnabledProperty, value); 165 | } 166 | 167 | public bool IsScrollEnabled 168 | { 169 | get => (bool) GetValue(IsScrollEnabledProperty); 170 | set => SetValue(IsScrollEnabledProperty, value); 171 | } 172 | 173 | public double ContentPaddingLeft 174 | { 175 | get => (double) GetValue(ContentPaddingLeftProperty); 176 | set => SetValue(ContentPaddingLeftProperty, value); 177 | } 178 | 179 | public double ContentPaddingRight 180 | { 181 | get => (double) GetValue(ContentPaddingRightProperty); 182 | set => SetValue(ContentPaddingRightProperty, value); 183 | } 184 | public double ContentPaddingTop 185 | { 186 | get => (double) GetValue(ContentPaddingTopProperty); 187 | set => SetValue(ContentPaddingTopProperty, value); 188 | } 189 | public double ContentPaddingBottom 190 | { 191 | get => (double) GetValue(ContentPaddingBottomProperty); 192 | set => SetValue(ContentPaddingBottomProperty, value); 193 | } 194 | 195 | public double SectionPaddingTop 196 | { 197 | get => (double) GetValue(SectionPaddingTopProperty); 198 | set => SetValue(SectionPaddingTopProperty, value); 199 | } 200 | 201 | public double SectionPaddingLeft 202 | { 203 | get => (double) GetValue(SectionPaddingLeftProperty); 204 | set => SetValue(SectionPaddingLeftProperty, value); 205 | } 206 | 207 | public double SectionPaddingBottom 208 | { 209 | get => (double) GetValue(SectionPaddingBottomProperty); 210 | set => SetValue(SectionPaddingBottomProperty, value); 211 | } 212 | 213 | public bool IsHorizontal 214 | { 215 | get => (bool) GetValue(IsHorizontalProperty); 216 | set => SetValue(IsHorizontalProperty, value); 217 | } 218 | 219 | public bool IsRefreshing 220 | { 221 | get => (bool) GetValue(IsRefreshingProperty); 222 | set => SetValue(IsRefreshingProperty, value); 223 | } 224 | 225 | public void RaiseLoadMoreEvent() 226 | { 227 | LoadMoreCommand?.Execute(null); 228 | } 229 | 230 | public void InvokeItemSelectedEvent(object sender, object item) 231 | { 232 | ItemSelectedCommand?.Execute(item); 233 | ItemSelected?.Invoke(sender, new FastGridEventArgs(item)); 234 | } 235 | 236 | public void Dispose() 237 | { 238 | _gridViewProvider = null; 239 | } 240 | 241 | public void ReloadData() 242 | { 243 | GridViewProvider?.ReloadData(); 244 | } 245 | 246 | public void ScrollToItem(int section, int index, bool animated) 247 | { 248 | if (GridViewProvider != null) 249 | GridViewProvider.ScrollToItem(index, animated); 250 | else 251 | _initialIndex = index; 252 | } 253 | 254 | public void ScrollTo(float x, float y) 255 | { 256 | GridViewProvider?.ScrollTo(x, y); 257 | } 258 | 259 | public void Refresh() 260 | { 261 | if (IsPullToRefreshEnabled == false) return; 262 | RefreshCommand?.Execute(null); 263 | } 264 | 265 | #region IScrollAwareElement 266 | 267 | public event EventHandler OnScrollEvent; 268 | public event EventHandler OnStartScrollEvent; 269 | public event EventHandler OnStopScrollEvent; 270 | 271 | public void RaiseOnScroll(double delta, double currentX, double currentY, ScrollActionType type) 272 | { 273 | var args = new ControlScrollEventArgs(delta, currentX, currentY, type); 274 | OnScrollEvent?.Invoke(this, args); 275 | } 276 | 277 | public void RaiseOnStartScroll(double currentX, double currentY, ScrollActionType type) 278 | { 279 | var args = new ControlScrollEventArgs(0, currentX, currentY, type); 280 | OnStartScrollEvent?.Invoke(this, args); 281 | } 282 | 283 | public void RaiseOnStopScroll(double currentX, double currentY, ScrollActionType type, bool fullStop) 284 | { 285 | var args = new ControlScrollEventArgs(0, currentX, currentY, type, fullStop); 286 | OnStopScrollEvent?.Invoke(this, args); 287 | } 288 | 289 | #endregion 290 | } 291 | } -------------------------------------------------------------------------------- /Binwell.Controls/FastGrid/FastGrid/IScrollAwareElement.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Binwell.Controls.FastGrid.FastGrid 4 | { 5 | public interface IScrollAwareElement 6 | { 7 | event EventHandler OnStartScrollEvent; 8 | event EventHandler OnStopScrollEvent; 9 | event EventHandler OnScrollEvent; 10 | 11 | void RaiseOnScroll(double delta, double currentX, double currentY, ScrollActionType type); 12 | void RaiseOnStartScroll(double currentX, double currentY, ScrollActionType type); 13 | void RaiseOnStopScroll(double currentX, double currentY, ScrollActionType type, bool fullStop); 14 | } 15 | 16 | public enum ScrollActionType { 17 | Finger, 18 | Fling, 19 | Auto, 20 | None 21 | } 22 | 23 | public class ControlScrollEventArgs : EventArgs 24 | { 25 | public double Delta { get; set; } 26 | public double CurrentY { get; set; } 27 | public double CurrentX { get; set; } 28 | public ScrollActionType Type { get; set; } 29 | public bool FullStop { get; } 30 | 31 | public ControlScrollEventArgs(double delta, double currentX, double currentY, ScrollActionType type, bool fullStop=false) 32 | { 33 | Delta = delta; 34 | CurrentY = currentY; 35 | Type = type; 36 | FullStop = fullStop; 37 | CurrentX = currentX; 38 | } 39 | 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /FastGrid.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.28010.2046 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FastGridSample.Android", "Sample\FastGridSample.Android\FastGridSample.Android.csproj", "{7C6D1654-3E04-4F34-B3DD-72FFA9693499}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FastGridSample.iOS", "Sample\FastGridSample.iOS\FastGridSample.iOS.csproj", "{02BCE766-BA94-4714-84B0-26C303A3E110}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FastGridSample", "Sample\FastGridSample\FastGridSample.csproj", "{FC5821C5-BCDD-4905-A242-F318B2677C5D}" 11 | ProjectSection(ProjectDependencies) = postProject 12 | {7B749930-552F-429F-B303-18878FDB264C} = {7B749930-552F-429F-B303-18878FDB264C} 13 | EndProjectSection 14 | EndProject 15 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Library", "Library", "{A4371132-88AC-4C2C-BF08-AE0F0D1E6F2D}" 16 | EndProject 17 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FastGrid", "Binwell.Controls\FastGrid\FastGrid.csproj", "{7B749930-552F-429F-B303-18878FDB264C}" 18 | EndProject 19 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FastGrid.Android", "Binwell.Controls\FastGrid.Android\FastGrid.Android.csproj", "{A25A0002-A767-4554-A6D5-31D91E7F3545}" 20 | EndProject 21 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FastGrid.iOS", "Binwell.Controls\FastGrid.iOS\FastGrid.iOS.csproj", "{EBC99883-B9E5-45F7-826B-81ADFF29E100}" 22 | EndProject 23 | Global 24 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 25 | Ad-Hoc|Any CPU = Ad-Hoc|Any CPU 26 | Ad-Hoc|iPhone = Ad-Hoc|iPhone 27 | Ad-Hoc|iPhoneSimulator = Ad-Hoc|iPhoneSimulator 28 | AppStore|Any CPU = AppStore|Any CPU 29 | AppStore|iPhone = AppStore|iPhone 30 | AppStore|iPhoneSimulator = AppStore|iPhoneSimulator 31 | Debug|Any CPU = Debug|Any CPU 32 | Debug|iPhone = Debug|iPhone 33 | Debug|iPhoneSimulator = Debug|iPhoneSimulator 34 | Release|Any CPU = Release|Any CPU 35 | Release|iPhone = Release|iPhone 36 | Release|iPhoneSimulator = Release|iPhoneSimulator 37 | EndGlobalSection 38 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 39 | {7C6D1654-3E04-4F34-B3DD-72FFA9693499}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU 40 | {7C6D1654-3E04-4F34-B3DD-72FFA9693499}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU 41 | {7C6D1654-3E04-4F34-B3DD-72FFA9693499}.Ad-Hoc|Any CPU.Deploy.0 = Release|Any CPU 42 | {7C6D1654-3E04-4F34-B3DD-72FFA9693499}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU 43 | {7C6D1654-3E04-4F34-B3DD-72FFA9693499}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU 44 | {7C6D1654-3E04-4F34-B3DD-72FFA9693499}.Ad-Hoc|iPhone.Deploy.0 = Release|Any CPU 45 | {7C6D1654-3E04-4F34-B3DD-72FFA9693499}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU 46 | {7C6D1654-3E04-4F34-B3DD-72FFA9693499}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU 47 | {7C6D1654-3E04-4F34-B3DD-72FFA9693499}.Ad-Hoc|iPhoneSimulator.Deploy.0 = Release|Any CPU 48 | {7C6D1654-3E04-4F34-B3DD-72FFA9693499}.AppStore|Any CPU.ActiveCfg = Release|Any CPU 49 | {7C6D1654-3E04-4F34-B3DD-72FFA9693499}.AppStore|Any CPU.Build.0 = Release|Any CPU 50 | {7C6D1654-3E04-4F34-B3DD-72FFA9693499}.AppStore|Any CPU.Deploy.0 = Release|Any CPU 51 | {7C6D1654-3E04-4F34-B3DD-72FFA9693499}.AppStore|iPhone.ActiveCfg = Release|Any CPU 52 | {7C6D1654-3E04-4F34-B3DD-72FFA9693499}.AppStore|iPhone.Build.0 = Release|Any CPU 53 | {7C6D1654-3E04-4F34-B3DD-72FFA9693499}.AppStore|iPhone.Deploy.0 = Release|Any CPU 54 | {7C6D1654-3E04-4F34-B3DD-72FFA9693499}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU 55 | {7C6D1654-3E04-4F34-B3DD-72FFA9693499}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU 56 | {7C6D1654-3E04-4F34-B3DD-72FFA9693499}.AppStore|iPhoneSimulator.Deploy.0 = Release|Any CPU 57 | {7C6D1654-3E04-4F34-B3DD-72FFA9693499}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 58 | {7C6D1654-3E04-4F34-B3DD-72FFA9693499}.Debug|Any CPU.Build.0 = Debug|Any CPU 59 | {7C6D1654-3E04-4F34-B3DD-72FFA9693499}.Debug|Any CPU.Deploy.0 = Debug|Any CPU 60 | {7C6D1654-3E04-4F34-B3DD-72FFA9693499}.Debug|iPhone.ActiveCfg = Debug|Any CPU 61 | {7C6D1654-3E04-4F34-B3DD-72FFA9693499}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU 62 | {7C6D1654-3E04-4F34-B3DD-72FFA9693499}.Release|Any CPU.ActiveCfg = Release|Any CPU 63 | {7C6D1654-3E04-4F34-B3DD-72FFA9693499}.Release|Any CPU.Build.0 = Release|Any CPU 64 | {7C6D1654-3E04-4F34-B3DD-72FFA9693499}.Release|Any CPU.Deploy.0 = Release|Any CPU 65 | {7C6D1654-3E04-4F34-B3DD-72FFA9693499}.Release|iPhone.ActiveCfg = Release|Any CPU 66 | {7C6D1654-3E04-4F34-B3DD-72FFA9693499}.Release|iPhone.Build.0 = Release|Any CPU 67 | {7C6D1654-3E04-4F34-B3DD-72FFA9693499}.Release|iPhone.Deploy.0 = Release|Any CPU 68 | {7C6D1654-3E04-4F34-B3DD-72FFA9693499}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU 69 | {7C6D1654-3E04-4F34-B3DD-72FFA9693499}.Release|iPhoneSimulator.Build.0 = Release|Any CPU 70 | {7C6D1654-3E04-4F34-B3DD-72FFA9693499}.Release|iPhoneSimulator.Deploy.0 = Release|Any CPU 71 | {02BCE766-BA94-4714-84B0-26C303A3E110}.Ad-Hoc|Any CPU.ActiveCfg = Ad-Hoc|iPhone 72 | {02BCE766-BA94-4714-84B0-26C303A3E110}.Ad-Hoc|Any CPU.Build.0 = Ad-Hoc|iPhone 73 | {02BCE766-BA94-4714-84B0-26C303A3E110}.Ad-Hoc|Any CPU.Deploy.0 = Ad-Hoc|iPhone 74 | {02BCE766-BA94-4714-84B0-26C303A3E110}.Ad-Hoc|iPhone.ActiveCfg = Ad-Hoc|iPhone 75 | {02BCE766-BA94-4714-84B0-26C303A3E110}.Ad-Hoc|iPhone.Build.0 = Ad-Hoc|iPhone 76 | {02BCE766-BA94-4714-84B0-26C303A3E110}.Ad-Hoc|iPhone.Deploy.0 = Ad-Hoc|iPhone 77 | {02BCE766-BA94-4714-84B0-26C303A3E110}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Ad-Hoc|iPhoneSimulator 78 | {02BCE766-BA94-4714-84B0-26C303A3E110}.Ad-Hoc|iPhoneSimulator.Build.0 = Ad-Hoc|iPhoneSimulator 79 | {02BCE766-BA94-4714-84B0-26C303A3E110}.Ad-Hoc|iPhoneSimulator.Deploy.0 = Ad-Hoc|iPhoneSimulator 80 | {02BCE766-BA94-4714-84B0-26C303A3E110}.AppStore|Any CPU.ActiveCfg = AppStore|iPhone 81 | {02BCE766-BA94-4714-84B0-26C303A3E110}.AppStore|Any CPU.Build.0 = AppStore|iPhone 82 | {02BCE766-BA94-4714-84B0-26C303A3E110}.AppStore|Any CPU.Deploy.0 = AppStore|iPhone 83 | {02BCE766-BA94-4714-84B0-26C303A3E110}.AppStore|iPhone.ActiveCfg = AppStore|iPhone 84 | {02BCE766-BA94-4714-84B0-26C303A3E110}.AppStore|iPhone.Build.0 = AppStore|iPhone 85 | {02BCE766-BA94-4714-84B0-26C303A3E110}.AppStore|iPhone.Deploy.0 = AppStore|iPhone 86 | {02BCE766-BA94-4714-84B0-26C303A3E110}.AppStore|iPhoneSimulator.ActiveCfg = AppStore|iPhoneSimulator 87 | {02BCE766-BA94-4714-84B0-26C303A3E110}.AppStore|iPhoneSimulator.Build.0 = AppStore|iPhoneSimulator 88 | {02BCE766-BA94-4714-84B0-26C303A3E110}.AppStore|iPhoneSimulator.Deploy.0 = AppStore|iPhoneSimulator 89 | {02BCE766-BA94-4714-84B0-26C303A3E110}.Debug|Any CPU.ActiveCfg = Debug|iPhoneSimulator 90 | {02BCE766-BA94-4714-84B0-26C303A3E110}.Debug|Any CPU.Deploy.0 = Debug|iPhoneSimulator 91 | {02BCE766-BA94-4714-84B0-26C303A3E110}.Debug|iPhone.ActiveCfg = Debug|iPhone 92 | {02BCE766-BA94-4714-84B0-26C303A3E110}.Debug|iPhone.Build.0 = Debug|iPhone 93 | {02BCE766-BA94-4714-84B0-26C303A3E110}.Debug|iPhone.Deploy.0 = Debug|iPhone 94 | {02BCE766-BA94-4714-84B0-26C303A3E110}.Debug|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator 95 | {02BCE766-BA94-4714-84B0-26C303A3E110}.Debug|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator 96 | {02BCE766-BA94-4714-84B0-26C303A3E110}.Debug|iPhoneSimulator.Deploy.0 = Debug|iPhoneSimulator 97 | {02BCE766-BA94-4714-84B0-26C303A3E110}.Release|Any CPU.ActiveCfg = Release|iPhoneSimulator 98 | {02BCE766-BA94-4714-84B0-26C303A3E110}.Release|Any CPU.Deploy.0 = Release|iPhoneSimulator 99 | {02BCE766-BA94-4714-84B0-26C303A3E110}.Release|iPhone.ActiveCfg = Release|iPhone 100 | {02BCE766-BA94-4714-84B0-26C303A3E110}.Release|iPhone.Build.0 = Release|iPhone 101 | {02BCE766-BA94-4714-84B0-26C303A3E110}.Release|iPhone.Deploy.0 = Release|iPhone 102 | {02BCE766-BA94-4714-84B0-26C303A3E110}.Release|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator 103 | {02BCE766-BA94-4714-84B0-26C303A3E110}.Release|iPhoneSimulator.Build.0 = Release|iPhoneSimulator 104 | {02BCE766-BA94-4714-84B0-26C303A3E110}.Release|iPhoneSimulator.Deploy.0 = Release|iPhoneSimulator 105 | {FC5821C5-BCDD-4905-A242-F318B2677C5D}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU 106 | {FC5821C5-BCDD-4905-A242-F318B2677C5D}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU 107 | {FC5821C5-BCDD-4905-A242-F318B2677C5D}.Ad-Hoc|Any CPU.Deploy.0 = Debug|Any CPU 108 | {FC5821C5-BCDD-4905-A242-F318B2677C5D}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU 109 | {FC5821C5-BCDD-4905-A242-F318B2677C5D}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU 110 | {FC5821C5-BCDD-4905-A242-F318B2677C5D}.Ad-Hoc|iPhone.Deploy.0 = Debug|Any CPU 111 | {FC5821C5-BCDD-4905-A242-F318B2677C5D}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU 112 | {FC5821C5-BCDD-4905-A242-F318B2677C5D}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU 113 | {FC5821C5-BCDD-4905-A242-F318B2677C5D}.Ad-Hoc|iPhoneSimulator.Deploy.0 = Debug|Any CPU 114 | {FC5821C5-BCDD-4905-A242-F318B2677C5D}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU 115 | {FC5821C5-BCDD-4905-A242-F318B2677C5D}.AppStore|Any CPU.Build.0 = Debug|Any CPU 116 | {FC5821C5-BCDD-4905-A242-F318B2677C5D}.AppStore|Any CPU.Deploy.0 = Debug|Any CPU 117 | {FC5821C5-BCDD-4905-A242-F318B2677C5D}.AppStore|iPhone.ActiveCfg = Debug|Any CPU 118 | {FC5821C5-BCDD-4905-A242-F318B2677C5D}.AppStore|iPhone.Build.0 = Debug|Any CPU 119 | {FC5821C5-BCDD-4905-A242-F318B2677C5D}.AppStore|iPhone.Deploy.0 = Debug|Any CPU 120 | {FC5821C5-BCDD-4905-A242-F318B2677C5D}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU 121 | {FC5821C5-BCDD-4905-A242-F318B2677C5D}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU 122 | {FC5821C5-BCDD-4905-A242-F318B2677C5D}.AppStore|iPhoneSimulator.Deploy.0 = Debug|Any CPU 123 | {FC5821C5-BCDD-4905-A242-F318B2677C5D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 124 | {FC5821C5-BCDD-4905-A242-F318B2677C5D}.Debug|Any CPU.Build.0 = Debug|Any CPU 125 | {FC5821C5-BCDD-4905-A242-F318B2677C5D}.Debug|Any CPU.Deploy.0 = Debug|Any CPU 126 | {FC5821C5-BCDD-4905-A242-F318B2677C5D}.Debug|iPhone.ActiveCfg = Debug|Any CPU 127 | {FC5821C5-BCDD-4905-A242-F318B2677C5D}.Debug|iPhone.Build.0 = Debug|Any CPU 128 | {FC5821C5-BCDD-4905-A242-F318B2677C5D}.Debug|iPhone.Deploy.0 = Debug|Any CPU 129 | {FC5821C5-BCDD-4905-A242-F318B2677C5D}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU 130 | {FC5821C5-BCDD-4905-A242-F318B2677C5D}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU 131 | {FC5821C5-BCDD-4905-A242-F318B2677C5D}.Debug|iPhoneSimulator.Deploy.0 = Debug|Any CPU 132 | {FC5821C5-BCDD-4905-A242-F318B2677C5D}.Release|Any CPU.ActiveCfg = Release|Any CPU 133 | {FC5821C5-BCDD-4905-A242-F318B2677C5D}.Release|Any CPU.Build.0 = Release|Any CPU 134 | {FC5821C5-BCDD-4905-A242-F318B2677C5D}.Release|Any CPU.Deploy.0 = Release|Any CPU 135 | {FC5821C5-BCDD-4905-A242-F318B2677C5D}.Release|iPhone.ActiveCfg = Release|Any CPU 136 | {FC5821C5-BCDD-4905-A242-F318B2677C5D}.Release|iPhone.Build.0 = Release|Any CPU 137 | {FC5821C5-BCDD-4905-A242-F318B2677C5D}.Release|iPhone.Deploy.0 = Release|Any CPU 138 | {FC5821C5-BCDD-4905-A242-F318B2677C5D}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU 139 | {FC5821C5-BCDD-4905-A242-F318B2677C5D}.Release|iPhoneSimulator.Build.0 = Release|Any CPU 140 | {FC5821C5-BCDD-4905-A242-F318B2677C5D}.Release|iPhoneSimulator.Deploy.0 = Release|Any CPU 141 | {7B749930-552F-429F-B303-18878FDB264C}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU 142 | {7B749930-552F-429F-B303-18878FDB264C}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU 143 | {7B749930-552F-429F-B303-18878FDB264C}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU 144 | {7B749930-552F-429F-B303-18878FDB264C}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU 145 | {7B749930-552F-429F-B303-18878FDB264C}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU 146 | {7B749930-552F-429F-B303-18878FDB264C}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU 147 | {7B749930-552F-429F-B303-18878FDB264C}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU 148 | {7B749930-552F-429F-B303-18878FDB264C}.AppStore|Any CPU.Build.0 = Debug|Any CPU 149 | {7B749930-552F-429F-B303-18878FDB264C}.AppStore|iPhone.ActiveCfg = Debug|Any CPU 150 | {7B749930-552F-429F-B303-18878FDB264C}.AppStore|iPhone.Build.0 = Debug|Any CPU 151 | {7B749930-552F-429F-B303-18878FDB264C}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU 152 | {7B749930-552F-429F-B303-18878FDB264C}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU 153 | {7B749930-552F-429F-B303-18878FDB264C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 154 | {7B749930-552F-429F-B303-18878FDB264C}.Debug|Any CPU.Build.0 = Debug|Any CPU 155 | {7B749930-552F-429F-B303-18878FDB264C}.Debug|iPhone.ActiveCfg = Debug|Any CPU 156 | {7B749930-552F-429F-B303-18878FDB264C}.Debug|iPhone.Build.0 = Debug|Any CPU 157 | {7B749930-552F-429F-B303-18878FDB264C}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU 158 | {7B749930-552F-429F-B303-18878FDB264C}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU 159 | {7B749930-552F-429F-B303-18878FDB264C}.Release|Any CPU.ActiveCfg = Release|Any CPU 160 | {7B749930-552F-429F-B303-18878FDB264C}.Release|Any CPU.Build.0 = Release|Any CPU 161 | {7B749930-552F-429F-B303-18878FDB264C}.Release|iPhone.ActiveCfg = Release|Any CPU 162 | {7B749930-552F-429F-B303-18878FDB264C}.Release|iPhone.Build.0 = Release|Any CPU 163 | {7B749930-552F-429F-B303-18878FDB264C}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU 164 | {7B749930-552F-429F-B303-18878FDB264C}.Release|iPhoneSimulator.Build.0 = Release|Any CPU 165 | {A25A0002-A767-4554-A6D5-31D91E7F3545}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU 166 | {A25A0002-A767-4554-A6D5-31D91E7F3545}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU 167 | {A25A0002-A767-4554-A6D5-31D91E7F3545}.Ad-Hoc|Any CPU.Deploy.0 = Release|Any CPU 168 | {A25A0002-A767-4554-A6D5-31D91E7F3545}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU 169 | {A25A0002-A767-4554-A6D5-31D91E7F3545}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU 170 | {A25A0002-A767-4554-A6D5-31D91E7F3545}.Ad-Hoc|iPhone.Deploy.0 = Release|Any CPU 171 | {A25A0002-A767-4554-A6D5-31D91E7F3545}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU 172 | {A25A0002-A767-4554-A6D5-31D91E7F3545}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU 173 | {A25A0002-A767-4554-A6D5-31D91E7F3545}.Ad-Hoc|iPhoneSimulator.Deploy.0 = Release|Any CPU 174 | {A25A0002-A767-4554-A6D5-31D91E7F3545}.AppStore|Any CPU.ActiveCfg = Release|Any CPU 175 | {A25A0002-A767-4554-A6D5-31D91E7F3545}.AppStore|Any CPU.Build.0 = Release|Any CPU 176 | {A25A0002-A767-4554-A6D5-31D91E7F3545}.AppStore|Any CPU.Deploy.0 = Release|Any CPU 177 | {A25A0002-A767-4554-A6D5-31D91E7F3545}.AppStore|iPhone.ActiveCfg = Release|Any CPU 178 | {A25A0002-A767-4554-A6D5-31D91E7F3545}.AppStore|iPhone.Build.0 = Release|Any CPU 179 | {A25A0002-A767-4554-A6D5-31D91E7F3545}.AppStore|iPhone.Deploy.0 = Release|Any CPU 180 | {A25A0002-A767-4554-A6D5-31D91E7F3545}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU 181 | {A25A0002-A767-4554-A6D5-31D91E7F3545}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU 182 | {A25A0002-A767-4554-A6D5-31D91E7F3545}.AppStore|iPhoneSimulator.Deploy.0 = Release|Any CPU 183 | {A25A0002-A767-4554-A6D5-31D91E7F3545}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 184 | {A25A0002-A767-4554-A6D5-31D91E7F3545}.Debug|Any CPU.Build.0 = Debug|Any CPU 185 | {A25A0002-A767-4554-A6D5-31D91E7F3545}.Debug|Any CPU.Deploy.0 = Debug|Any CPU 186 | {A25A0002-A767-4554-A6D5-31D91E7F3545}.Debug|iPhone.ActiveCfg = Debug|Any CPU 187 | {A25A0002-A767-4554-A6D5-31D91E7F3545}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU 188 | {A25A0002-A767-4554-A6D5-31D91E7F3545}.Release|Any CPU.ActiveCfg = Release|Any CPU 189 | {A25A0002-A767-4554-A6D5-31D91E7F3545}.Release|Any CPU.Build.0 = Release|Any CPU 190 | {A25A0002-A767-4554-A6D5-31D91E7F3545}.Release|Any CPU.Deploy.0 = Release|Any CPU 191 | {A25A0002-A767-4554-A6D5-31D91E7F3545}.Release|iPhone.ActiveCfg = Release|Any CPU 192 | {A25A0002-A767-4554-A6D5-31D91E7F3545}.Release|iPhone.Build.0 = Release|Any CPU 193 | {A25A0002-A767-4554-A6D5-31D91E7F3545}.Release|iPhone.Deploy.0 = Release|Any CPU 194 | {A25A0002-A767-4554-A6D5-31D91E7F3545}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU 195 | {A25A0002-A767-4554-A6D5-31D91E7F3545}.Release|iPhoneSimulator.Build.0 = Release|Any CPU 196 | {A25A0002-A767-4554-A6D5-31D91E7F3545}.Release|iPhoneSimulator.Deploy.0 = Release|Any CPU 197 | {EBC99883-B9E5-45F7-826B-81ADFF29E100}.Ad-Hoc|Any CPU.ActiveCfg = Ad-Hoc|iPhone 198 | {EBC99883-B9E5-45F7-826B-81ADFF29E100}.Ad-Hoc|iPhone.ActiveCfg = Ad-Hoc|iPhone 199 | {EBC99883-B9E5-45F7-826B-81ADFF29E100}.Ad-Hoc|iPhone.Build.0 = Ad-Hoc|iPhone 200 | {EBC99883-B9E5-45F7-826B-81ADFF29E100}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Ad-Hoc|iPhoneSimulator 201 | {EBC99883-B9E5-45F7-826B-81ADFF29E100}.Ad-Hoc|iPhoneSimulator.Build.0 = Ad-Hoc|iPhoneSimulator 202 | {EBC99883-B9E5-45F7-826B-81ADFF29E100}.AppStore|Any CPU.ActiveCfg = AppStore|iPhone 203 | {EBC99883-B9E5-45F7-826B-81ADFF29E100}.AppStore|iPhone.ActiveCfg = AppStore|iPhone 204 | {EBC99883-B9E5-45F7-826B-81ADFF29E100}.AppStore|iPhone.Build.0 = AppStore|iPhone 205 | {EBC99883-B9E5-45F7-826B-81ADFF29E100}.AppStore|iPhoneSimulator.ActiveCfg = AppStore|iPhoneSimulator 206 | {EBC99883-B9E5-45F7-826B-81ADFF29E100}.AppStore|iPhoneSimulator.Build.0 = AppStore|iPhoneSimulator 207 | {EBC99883-B9E5-45F7-826B-81ADFF29E100}.Debug|Any CPU.ActiveCfg = Debug|iPhoneSimulator 208 | {EBC99883-B9E5-45F7-826B-81ADFF29E100}.Debug|iPhone.ActiveCfg = Debug|iPhone 209 | {EBC99883-B9E5-45F7-826B-81ADFF29E100}.Debug|iPhone.Build.0 = Debug|iPhone 210 | {EBC99883-B9E5-45F7-826B-81ADFF29E100}.Debug|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator 211 | {EBC99883-B9E5-45F7-826B-81ADFF29E100}.Debug|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator 212 | {EBC99883-B9E5-45F7-826B-81ADFF29E100}.Release|Any CPU.ActiveCfg = Release|iPhoneSimulator 213 | {EBC99883-B9E5-45F7-826B-81ADFF29E100}.Release|iPhone.ActiveCfg = Release|iPhone 214 | {EBC99883-B9E5-45F7-826B-81ADFF29E100}.Release|iPhone.Build.0 = Release|iPhone 215 | {EBC99883-B9E5-45F7-826B-81ADFF29E100}.Release|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator 216 | {EBC99883-B9E5-45F7-826B-81ADFF29E100}.Release|iPhoneSimulator.Build.0 = Release|iPhoneSimulator 217 | EndGlobalSection 218 | GlobalSection(SolutionProperties) = preSolution 219 | HideSolutionNode = FALSE 220 | EndGlobalSection 221 | GlobalSection(NestedProjects) = preSolution 222 | {7B749930-552F-429F-B303-18878FDB264C} = {A4371132-88AC-4C2C-BF08-AE0F0D1E6F2D} 223 | {A25A0002-A767-4554-A6D5-31D91E7F3545} = {A4371132-88AC-4C2C-BF08-AE0F0D1E6F2D} 224 | {EBC99883-B9E5-45F7-826B-81ADFF29E100} = {A4371132-88AC-4C2C-BF08-AE0F0D1E6F2D} 225 | EndGlobalSection 226 | GlobalSection(ExtensibilityGlobals) = postSolution 227 | SolutionGuid = {0B6FE0CF-2C0B-4C3B-8DC2-C486B3A42760} 228 | EndGlobalSection 229 | EndGlobal 230 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Binwell 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FastGrid 2 | FastGrid component for Xamarin.Forms based on Android RecyclerView and iOS UICollectionView 3 | 4 | More details can be found in Xamarin Blog: https://blog.xamarin.com/complex-ui-with-fastgrid-for-xamarin-forms/ 5 | 6 | Current Binwell FastGrid Features 7 | * Use a large number of cell types 8 | * Every cell type is related to the data model (including sections) 9 | * Using of Flow Layouts to deal with layout inside RecyclerView and UICollectionView 10 | * Support for dynamic data adding/updating/removing, including LoadMore feature 11 | * Pull-to-refresh features 12 | 13 | Special thanks to: 14 | - [Subhan Ali](https://github.com/SubhanAli94 "Subhan Ali") for fixing horizontal orientation 15 | -------------------------------------------------------------------------------- /Sample/FastGridSample.Android/Assets/AboutAssets.txt: -------------------------------------------------------------------------------- 1 | Any raw assets you want to be deployed with your application can be placed in 2 | this directory (and child directories) and given a Build Action of "AndroidAsset". 3 | 4 | These files will be deployed with you package and will be accessible using Android's 5 | AssetManager, like this: 6 | 7 | public class ReadAsset : Activity 8 | { 9 | protected override void OnCreate (Bundle bundle) 10 | { 11 | base.OnCreate (bundle); 12 | 13 | InputStream input = Assets.Open ("my_asset.txt"); 14 | } 15 | } 16 | 17 | Additionally, some Android functions will automatically load asset files: 18 | 19 | Typeface tf = Typeface.CreateFromAsset (Context.Assets, "fonts/samplefont.ttf"); 20 | -------------------------------------------------------------------------------- /Sample/FastGridSample.Android/FastGridSample.Android.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | AnyCPU 6 | {7C6D1654-3E04-4F34-B3DD-72FFA9693499} 7 | {EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 8 | {c9e5eea5-ca05-42a1-839b-61506e0a37df} 9 | Library 10 | FastGridSample.Android 11 | FastGridSample.Android 12 | True 13 | Resources\Resource.designer.cs 14 | Resource 15 | Properties\AndroidManifest.xml 16 | Resources 17 | Assets 18 | false 19 | v8.1 20 | Xamarin.Android.Net.AndroidClientHandler 21 | 22 | 23 | 24 | 25 | true 26 | portable 27 | false 28 | bin\Debug 29 | DEBUG; 30 | prompt 31 | 4 32 | None 33 | 34 | 35 | true 36 | pdbonly 37 | true 38 | bin\Release 39 | prompt 40 | 4 41 | true 42 | false 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 2.4.4.859 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | {A25A0002-A767-4554-A6D5-31D91E7F3545} 94 | FastGrid.Android 95 | 96 | 97 | {7b749930-552f-429f-b303-18878fdb264c} 98 | FastGrid 99 | 100 | 101 | {FC5821C5-BCDD-4905-A242-F318B2677C5D} 102 | FastGridSample 103 | 104 | 105 | 106 | -------------------------------------------------------------------------------- /Sample/FastGridSample.Android/MainActivity.cs: -------------------------------------------------------------------------------- 1 | using Android; 2 | using Android.App; 3 | using Android.Content.PM; 4 | using Android.OS; 5 | 6 | namespace FastGridSample.Android 7 | { 8 | [Activity(Label = "FastGridSample", Icon = "@mipmap/icon", Theme = "@style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)] 9 | public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity 10 | { 11 | protected override void OnCreate(Bundle savedInstanceState) 12 | { 13 | TabLayoutResource = Resource.Layout.Tabbar; 14 | ToolbarResource = Resource.Layout.Toolbar; 15 | 16 | base.OnCreate(savedInstanceState); 17 | Xamarin.Forms.Forms.Init(this, savedInstanceState); 18 | FFImageLoading.Forms.Platform.CachedImageRenderer.Init(true); 19 | LoadApplication(new App()); 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /Sample/FastGridSample.Android/Properties/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Sample/FastGridSample.Android/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | using Android.App; 5 | 6 | // General Information about an assembly is controlled through the following 7 | // set of attributes. Change these attribute values to modify the information 8 | // associated with an assembly. 9 | [assembly: AssemblyTitle("FastGridSample.Android")] 10 | [assembly: AssemblyDescription("")] 11 | [assembly: AssemblyConfiguration("")] 12 | [assembly: AssemblyCompany("")] 13 | [assembly: AssemblyProduct("FastGridSample.Android")] 14 | [assembly: AssemblyCopyright("Copyright © 2014")] 15 | [assembly: AssemblyTrademark("")] 16 | [assembly: AssemblyCulture("")] 17 | [assembly: ComVisible(false)] 18 | 19 | // Version information for an assembly consists of the following four values: 20 | // 21 | // Major Version 22 | // Minor Version 23 | // Build Number 24 | // Revision 25 | // 26 | // You can specify all the values or you can default the Build and Revision Numbers 27 | // by using the '*' as shown below: 28 | // [assembly: AssemblyVersion("1.0.*")] 29 | [assembly: AssemblyVersion("1.0.0.0")] 30 | [assembly: AssemblyFileVersion("1.0.0.0")] 31 | 32 | // Add some common permissions, these can be removed if not needed 33 | [assembly: UsesPermission(Android.Manifest.Permission.Internet)] 34 | [assembly: UsesPermission(Android.Manifest.Permission.WriteExternalStorage)] 35 | -------------------------------------------------------------------------------- /Sample/FastGridSample.Android/Resources/AboutResources.txt: -------------------------------------------------------------------------------- 1 | Images, layout descriptions, binary blobs and string dictionaries can be included 2 | in your application as resource files. Various Android APIs are designed to 3 | operate on the resource IDs instead of dealing with images, strings or binary blobs 4 | directly. 5 | 6 | For example, a sample Android app that contains a user interface layout (main.xml), 7 | an internationalization string table (strings.xml) and some icons (drawable-XXX/icon.png) 8 | would keep its resources in the "Resources" directory of the application: 9 | 10 | Resources/ 11 | drawable-hdpi/ 12 | icon.png 13 | 14 | drawable-ldpi/ 15 | icon.png 16 | 17 | drawable-mdpi/ 18 | icon.png 19 | 20 | layout/ 21 | main.xml 22 | 23 | values/ 24 | strings.xml 25 | 26 | In order to get the build system to recognize Android resources, set the build action to 27 | "AndroidResource". The native Android APIs do not operate directly with filenames, but 28 | instead operate on resource IDs. When you compile an Android application that uses resources, 29 | the build system will package the resources for distribution and generate a class called 30 | "Resource" that contains the tokens for each one of the resources included. For example, 31 | for the above Resources layout, this is what the Resource class would expose: 32 | 33 | public class Resource { 34 | public class drawable { 35 | public const int icon = 0x123; 36 | } 37 | 38 | public class layout { 39 | public const int main = 0x456; 40 | } 41 | 42 | public class strings { 43 | public const int first_string = 0xabc; 44 | public const int second_string = 0xbcd; 45 | } 46 | } 47 | 48 | You would then use R.drawable.icon to reference the drawable/icon.png file, or Resource.layout.main 49 | to reference the layout/main.xml file, or Resource.strings.first_string to reference the first 50 | string in the dictionary file values/strings.xml. 51 | -------------------------------------------------------------------------------- /Sample/FastGridSample.Android/Resources/layout/Tabbar.axml: -------------------------------------------------------------------------------- 1 | 2 | 12 | -------------------------------------------------------------------------------- /Sample/FastGridSample.Android/Resources/layout/Toolbar.axml: -------------------------------------------------------------------------------- 1 | 9 | 10 | -------------------------------------------------------------------------------- /Sample/FastGridSample.Android/Resources/mipmap-anydpi-v26/icon.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /Sample/FastGridSample.Android/Resources/mipmap-anydpi-v26/icon_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /Sample/FastGridSample.Android/Resources/mipmap-hdpi/Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Binwell/FastGrid/fa52ef303f7689ec0bf32739d5af69ca4ad2583a/Sample/FastGridSample.Android/Resources/mipmap-hdpi/Icon.png -------------------------------------------------------------------------------- /Sample/FastGridSample.Android/Resources/mipmap-hdpi/launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Binwell/FastGrid/fa52ef303f7689ec0bf32739d5af69ca4ad2583a/Sample/FastGridSample.Android/Resources/mipmap-hdpi/launcher_foreground.png -------------------------------------------------------------------------------- /Sample/FastGridSample.Android/Resources/mipmap-mdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Binwell/FastGrid/fa52ef303f7689ec0bf32739d5af69ca4ad2583a/Sample/FastGridSample.Android/Resources/mipmap-mdpi/icon.png -------------------------------------------------------------------------------- /Sample/FastGridSample.Android/Resources/mipmap-mdpi/launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Binwell/FastGrid/fa52ef303f7689ec0bf32739d5af69ca4ad2583a/Sample/FastGridSample.Android/Resources/mipmap-mdpi/launcher_foreground.png -------------------------------------------------------------------------------- /Sample/FastGridSample.Android/Resources/mipmap-xhdpi/Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Binwell/FastGrid/fa52ef303f7689ec0bf32739d5af69ca4ad2583a/Sample/FastGridSample.Android/Resources/mipmap-xhdpi/Icon.png -------------------------------------------------------------------------------- /Sample/FastGridSample.Android/Resources/mipmap-xhdpi/launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Binwell/FastGrid/fa52ef303f7689ec0bf32739d5af69ca4ad2583a/Sample/FastGridSample.Android/Resources/mipmap-xhdpi/launcher_foreground.png -------------------------------------------------------------------------------- /Sample/FastGridSample.Android/Resources/mipmap-xxhdpi/Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Binwell/FastGrid/fa52ef303f7689ec0bf32739d5af69ca4ad2583a/Sample/FastGridSample.Android/Resources/mipmap-xxhdpi/Icon.png -------------------------------------------------------------------------------- /Sample/FastGridSample.Android/Resources/mipmap-xxhdpi/launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Binwell/FastGrid/fa52ef303f7689ec0bf32739d5af69ca4ad2583a/Sample/FastGridSample.Android/Resources/mipmap-xxhdpi/launcher_foreground.png -------------------------------------------------------------------------------- /Sample/FastGridSample.Android/Resources/mipmap-xxxhdpi/Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Binwell/FastGrid/fa52ef303f7689ec0bf32739d5af69ca4ad2583a/Sample/FastGridSample.Android/Resources/mipmap-xxxhdpi/Icon.png -------------------------------------------------------------------------------- /Sample/FastGridSample.Android/Resources/mipmap-xxxhdpi/launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Binwell/FastGrid/fa52ef303f7689ec0bf32739d5af69ca4ad2583a/Sample/FastGridSample.Android/Resources/mipmap-xxxhdpi/launcher_foreground.png -------------------------------------------------------------------------------- /Sample/FastGridSample.Android/Resources/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFFFFF 4 | #3F51B5 5 | #303F9F 6 | #FF4081 7 | 8 | -------------------------------------------------------------------------------- /Sample/FastGridSample.Android/Resources/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 26 | 27 | 30 | 31 | -------------------------------------------------------------------------------- /Sample/FastGridSample.iOS/AppDelegate.cs: -------------------------------------------------------------------------------- 1 | using Binwell.Controls.FastGrid.iOS.FastGrid; 2 | using Foundation; 3 | using UIKit; 4 | 5 | namespace FastGridSample.iOS 6 | { 7 | [Register("AppDelegate")] 8 | public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate 9 | { 10 | public override bool FinishedLaunching(UIApplication app, NSDictionary options) 11 | { 12 | global::Xamarin.Forms.Forms.Init(); 13 | FFImageLoading.Forms.Platform.CachedImageRenderer.Init(); 14 | FastGridViewRenderer.Init(); 15 | LoadApplication(new App()); 16 | 17 | return base.FinishedLaunching(app, options); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Sample/FastGridSample.iOS/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images": [ 3 | { 4 | "scale": "2x", 5 | "size": "20x20", 6 | "idiom": "iphone", 7 | "filename": "Icon40.png" 8 | }, 9 | { 10 | "scale": "3x", 11 | "size": "20x20", 12 | "idiom": "iphone", 13 | "filename": "Icon60.png" 14 | }, 15 | { 16 | "scale": "2x", 17 | "size": "29x29", 18 | "idiom": "iphone", 19 | "filename": "Icon58.png" 20 | }, 21 | { 22 | "scale": "3x", 23 | "size": "29x29", 24 | "idiom": "iphone", 25 | "filename": "Icon87.png" 26 | }, 27 | { 28 | "scale": "2x", 29 | "size": "40x40", 30 | "idiom": "iphone", 31 | "filename": "Icon80.png" 32 | }, 33 | { 34 | "scale": "3x", 35 | "size": "40x40", 36 | "idiom": "iphone", 37 | "filename": "Icon120.png" 38 | }, 39 | { 40 | "scale": "2x", 41 | "size": "60x60", 42 | "idiom": "iphone", 43 | "filename": "Icon120.png" 44 | }, 45 | { 46 | "scale": "3x", 47 | "size": "60x60", 48 | "idiom": "iphone", 49 | "filename": "Icon180.png" 50 | }, 51 | { 52 | "scale": "1x", 53 | "size": "20x20", 54 | "idiom": "ipad", 55 | "filename": "Icon20.png" 56 | }, 57 | { 58 | "scale": "2x", 59 | "size": "20x20", 60 | "idiom": "ipad", 61 | "filename": "Icon40.png" 62 | }, 63 | { 64 | "scale": "1x", 65 | "size": "29x29", 66 | "idiom": "ipad", 67 | "filename": "Icon29.png" 68 | }, 69 | { 70 | "scale": "2x", 71 | "size": "29x29", 72 | "idiom": "ipad", 73 | "filename": "Icon58.png" 74 | }, 75 | { 76 | "scale": "1x", 77 | "size": "40x40", 78 | "idiom": "ipad", 79 | "filename": "Icon40.png" 80 | }, 81 | { 82 | "scale": "2x", 83 | "size": "40x40", 84 | "idiom": "ipad", 85 | "filename": "Icon80.png" 86 | }, 87 | { 88 | "scale": "1x", 89 | "size": "76x76", 90 | "idiom": "ipad", 91 | "filename": "Icon76.png" 92 | }, 93 | { 94 | "scale": "2x", 95 | "size": "76x76", 96 | "idiom": "ipad", 97 | "filename": "Icon152.png" 98 | }, 99 | { 100 | "scale": "2x", 101 | "size": "83.5x83.5", 102 | "idiom": "ipad", 103 | "filename": "Icon167.png" 104 | }, 105 | { 106 | "scale": "1x", 107 | "size": "1024x1024", 108 | "idiom": "ios-marketing", 109 | "filename": "Icon1024.png" 110 | } 111 | ], 112 | "properties": {}, 113 | "info": { 114 | "version": 1, 115 | "author": "xcode" 116 | } 117 | } -------------------------------------------------------------------------------- /Sample/FastGridSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Binwell/FastGrid/fa52ef303f7689ec0bf32739d5af69ca4ad2583a/Sample/FastGridSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon1024.png -------------------------------------------------------------------------------- /Sample/FastGridSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Binwell/FastGrid/fa52ef303f7689ec0bf32739d5af69ca4ad2583a/Sample/FastGridSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon120.png -------------------------------------------------------------------------------- /Sample/FastGridSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Binwell/FastGrid/fa52ef303f7689ec0bf32739d5af69ca4ad2583a/Sample/FastGridSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon152.png -------------------------------------------------------------------------------- /Sample/FastGridSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon167.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Binwell/FastGrid/fa52ef303f7689ec0bf32739d5af69ca4ad2583a/Sample/FastGridSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon167.png -------------------------------------------------------------------------------- /Sample/FastGridSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Binwell/FastGrid/fa52ef303f7689ec0bf32739d5af69ca4ad2583a/Sample/FastGridSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon180.png -------------------------------------------------------------------------------- /Sample/FastGridSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Binwell/FastGrid/fa52ef303f7689ec0bf32739d5af69ca4ad2583a/Sample/FastGridSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon20.png -------------------------------------------------------------------------------- /Sample/FastGridSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Binwell/FastGrid/fa52ef303f7689ec0bf32739d5af69ca4ad2583a/Sample/FastGridSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon29.png -------------------------------------------------------------------------------- /Sample/FastGridSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Binwell/FastGrid/fa52ef303f7689ec0bf32739d5af69ca4ad2583a/Sample/FastGridSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon40.png -------------------------------------------------------------------------------- /Sample/FastGridSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon58.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Binwell/FastGrid/fa52ef303f7689ec0bf32739d5af69ca4ad2583a/Sample/FastGridSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon58.png -------------------------------------------------------------------------------- /Sample/FastGridSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Binwell/FastGrid/fa52ef303f7689ec0bf32739d5af69ca4ad2583a/Sample/FastGridSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon60.png -------------------------------------------------------------------------------- /Sample/FastGridSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Binwell/FastGrid/fa52ef303f7689ec0bf32739d5af69ca4ad2583a/Sample/FastGridSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon76.png -------------------------------------------------------------------------------- /Sample/FastGridSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon80.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Binwell/FastGrid/fa52ef303f7689ec0bf32739d5af69ca4ad2583a/Sample/FastGridSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon80.png -------------------------------------------------------------------------------- /Sample/FastGridSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon87.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Binwell/FastGrid/fa52ef303f7689ec0bf32739d5af69ca4ad2583a/Sample/FastGridSample.iOS/Assets.xcassets/AppIcon.appiconset/Icon87.png -------------------------------------------------------------------------------- /Sample/FastGridSample.iOS/Entitlements.plist: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Sample/FastGridSample.iOS/FastGridSample.iOS.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | iPhoneSimulator 6 | 8.0.30703 7 | 2.0 8 | {02BCE766-BA94-4714-84B0-26C303A3E110} 9 | {FEACFBD2-3405-455C-9665-78FE426C6842};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 10 | {6143fdea-f3c2-4a09-aafa-6e230626515e} 11 | Exe 12 | FastGridSample.iOS 13 | Resources 14 | FastGridSample.iOS 15 | NSUrlSessionHandler 16 | 17 | 18 | 19 | 20 | true 21 | full 22 | false 23 | bin\iPhoneSimulator\Debug 24 | DEBUG 25 | prompt 26 | 4 27 | false 28 | x86_64 29 | None 30 | true 31 | 32 | 33 | none 34 | true 35 | bin\iPhoneSimulator\Release 36 | prompt 37 | 4 38 | None 39 | x86_64 40 | false 41 | 42 | 43 | true 44 | full 45 | false 46 | bin\iPhone\Debug 47 | DEBUG 48 | prompt 49 | 4 50 | false 51 | ARM64 52 | iPhone Developer 53 | true 54 | Entitlements.plist 55 | 56 | 57 | none 58 | true 59 | bin\iPhone\Release 60 | prompt 61 | 4 62 | ARM64 63 | false 64 | iPhone Developer 65 | Entitlements.plist 66 | 67 | 68 | none 69 | True 70 | bin\iPhone\Ad-Hoc 71 | prompt 72 | 4 73 | False 74 | ARM64 75 | True 76 | Automatic:AdHoc 77 | iPhone Distribution 78 | Entitlements.plist 79 | 80 | 81 | none 82 | True 83 | bin\iPhone\AppStore 84 | prompt 85 | 4 86 | False 87 | ARM64 88 | Automatic:AppStore 89 | iPhone Distribution 90 | Entitlements.plist 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | false 103 | 104 | 105 | false 106 | 107 | 108 | false 109 | 110 | 111 | false 112 | 113 | 114 | false 115 | 116 | 117 | false 118 | 119 | 120 | false 121 | 122 | 123 | false 124 | 125 | 126 | false 127 | 128 | 129 | false 130 | 131 | 132 | false 133 | 134 | 135 | false 136 | 137 | 138 | false 139 | 140 | 141 | false 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 2.4.4.859 153 | 154 | 155 | 156 | 157 | 158 | 159 | {EBC99883-B9E5-45F7-826B-81ADFF29E100} 160 | FastGrid.iOS 161 | false 162 | false 163 | 164 | 165 | {7b749930-552f-429f-b303-18878fdb264c} 166 | FastGrid 167 | 168 | 169 | {fc5821c5-bcdd-4905-a242-f318b2677c5d} 170 | FastGridSample 171 | 172 | 173 | -------------------------------------------------------------------------------- /Sample/FastGridSample.iOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | UIDeviceFamily 6 | 7 | 1 8 | 2 9 | 10 | UISupportedInterfaceOrientations 11 | 12 | UIInterfaceOrientationPortrait 13 | UIInterfaceOrientationLandscapeLeft 14 | UIInterfaceOrientationLandscapeRight 15 | 16 | UISupportedInterfaceOrientations~ipad 17 | 18 | UIInterfaceOrientationPortrait 19 | UIInterfaceOrientationPortraitUpsideDown 20 | UIInterfaceOrientationLandscapeLeft 21 | UIInterfaceOrientationLandscapeRight 22 | 23 | MinimumOSVersion 24 | 8.0 25 | CFBundleDisplayName 26 | FastGridSample 27 | CFBundleIdentifier 28 | com.binwell.FastGridSample 29 | CFBundleVersion 30 | 1.0 31 | UILaunchStoryboardName 32 | LaunchScreen 33 | CFBundleName 34 | FCViewSample 35 | XSAppIconAssets 36 | Assets.xcassets/AppIcon.appiconset 37 | 38 | 39 | -------------------------------------------------------------------------------- /Sample/FastGridSample.iOS/Main.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | using Foundation; 6 | using UIKit; 7 | 8 | namespace FCViewSample.iOS 9 | { 10 | public class Application 11 | { 12 | // This is the main entry point of the application. 13 | static void Main(string[] args) 14 | { 15 | // if you want to use a different Application Delegate class from "AppDelegate" 16 | // you can specify it here. 17 | UIApplication.Main(args, null, "AppDelegate"); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Sample/FastGridSample.iOS/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("FastGridSample.iOS")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("FastGridSample.iOS")] 13 | [assembly: AssemblyCopyright("Copyright © 2014")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("72bdc44f-c588-44f3-b6df-9aace7daafdd")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /Sample/FastGridSample.iOS/Resources/Default-568h@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Binwell/FastGrid/fa52ef303f7689ec0bf32739d5af69ca4ad2583a/Sample/FastGridSample.iOS/Resources/Default-568h@2x.png -------------------------------------------------------------------------------- /Sample/FastGridSample.iOS/Resources/Default-Portrait.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Binwell/FastGrid/fa52ef303f7689ec0bf32739d5af69ca4ad2583a/Sample/FastGridSample.iOS/Resources/Default-Portrait.png -------------------------------------------------------------------------------- /Sample/FastGridSample.iOS/Resources/Default-Portrait@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Binwell/FastGrid/fa52ef303f7689ec0bf32739d5af69ca4ad2583a/Sample/FastGridSample.iOS/Resources/Default-Portrait@2x.png -------------------------------------------------------------------------------- /Sample/FastGridSample.iOS/Resources/Default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Binwell/FastGrid/fa52ef303f7689ec0bf32739d5af69ca4ad2583a/Sample/FastGridSample.iOS/Resources/Default.png -------------------------------------------------------------------------------- /Sample/FastGridSample.iOS/Resources/Default@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Binwell/FastGrid/fa52ef303f7689ec0bf32739d5af69ca4ad2583a/Sample/FastGridSample.iOS/Resources/Default@2x.png -------------------------------------------------------------------------------- /Sample/FastGridSample.iOS/Resources/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /Sample/FastGridSample/App.cs: -------------------------------------------------------------------------------- 1 | using Xamarin.Forms; 2 | 3 | namespace FastGridSample 4 | { 5 | public class App : Application 6 | { 7 | public App() 8 | { 9 | MainPage = new NavigationPage(new MainPage()); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Sample/FastGridSample/Cells/CategoryCell.xaml: -------------------------------------------------------------------------------- 1 |  2 | 7 | 8 | 10 | -------------------------------------------------------------------------------- /Sample/FastGridSample/Cells/CategoryCell.xaml.cs: -------------------------------------------------------------------------------- 1 | using Binwell.Controls.FastGrid.FastGrid; 2 | using Xamarin.Forms.Xaml; 3 | 4 | namespace FastGridSample.Cells 5 | { 6 | [XamlCompilation(XamlCompilationOptions.Compile)] 7 | public partial class CategoryCell : FastGridCell 8 | { 9 | protected override void InitializeCell() 10 | { 11 | InitializeComponent(); 12 | } 13 | 14 | protected override void SetupCell(bool isRecycled) 15 | { 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /Sample/FastGridSample/Cells/ProductCell.cs: -------------------------------------------------------------------------------- 1 | using Binwell.Controls.FastGrid.FastGrid; 2 | using FastGridSample.DataObjects; 3 | using FFImageLoading.Forms; 4 | using Xamarin.Forms; 5 | 6 | namespace FastGridSample.Cells 7 | { 8 | public class ProductCell : FastGridCell 9 | { 10 | CachedImage _image; 11 | Label _name; 12 | Label _price; 13 | 14 | protected override void InitializeCell() 15 | { 16 | var screenWidth = Device.Info.ScaledScreenSize.Width; 17 | 18 | _image = new CachedImage 19 | { 20 | HorizontalOptions = LayoutOptions.Center, 21 | Aspect = Aspect.AspectFill, 22 | WidthRequest = screenWidth / 2 - 40, 23 | HeightRequest = screenWidth / 2 - 40 24 | }; 25 | 26 | _name = new Label 27 | { 28 | HorizontalOptions = LayoutOptions.Center, 29 | FontSize = 20, 30 | TextColor = Color.Black 31 | }; 32 | 33 | _price = new Label { HorizontalOptions = LayoutOptions.Center, 34 | FontSize = 14, 35 | TextColor = Color.Black 36 | }; 37 | 38 | View = new StackLayout 39 | { 40 | BackgroundColor = Color.White, 41 | Padding = 20, 42 | VerticalOptions = LayoutOptions.FillAndExpand, 43 | HorizontalOptions = LayoutOptions.FillAndExpand, 44 | Children = 45 | { 46 | _image, 47 | _name, 48 | _price 49 | } 50 | }; 51 | } 52 | 53 | protected override void SetupCell(bool isRecycled) 54 | { 55 | if (!(BindingContext is ProductObject bindingContext)) return; 56 | 57 | _image.Source = null; 58 | _image.Source = bindingContext.ImageUrl; 59 | _name.Text = bindingContext.Name; 60 | _price.Text = bindingContext.Price; 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Sample/FastGridSample/DataObjects/CategoryObject.cs: -------------------------------------------------------------------------------- 1 | namespace FastGridSample.DataObjects 2 | { 3 | public class CategoryObject 4 | { 5 | public string Name { get; set; } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Sample/FastGridSample/DataObjects/ProductObject.cs: -------------------------------------------------------------------------------- 1 | namespace FastGridSample.DataObjects 2 | { 3 | public class ProductObject 4 | { 5 | public string ImageUrl { get; set; } 6 | public string Name { get; set; } 7 | public string Price { get; set; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Sample/FastGridSample/FastGridSample.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | FastGridSample 6 | FastGridSample 7 | 8 | 9 | 10 | pdbonly 11 | true 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | CategoryCell.xaml 27 | 28 | 29 | 30 | 31 | 32 | MSBuild:UpdateDesignTimeXaml 33 | 34 | 35 | 36 | 37 | 38 | MSBuild:Compile 39 | 40 | 41 | MSBuild:Compile 42 | 43 | 44 | MSBuild:Compile 45 | 46 | 47 | -------------------------------------------------------------------------------- /Sample/FastGridSample/MainPage.xaml: -------------------------------------------------------------------------------- 1 |  2 | 6 | 7 | 18 | 22 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Sample/FastGridSample/MainPage.xaml.cs: -------------------------------------------------------------------------------- 1 | using Binwell.Controls.FastGrid.FastGrid; 2 | using FastGridSample.Cells; 3 | using FastGridSample.DataObjects; 4 | using Xamarin.Forms; 5 | 6 | namespace FastGridSample 7 | { 8 | public partial class MainPage : ContentPage 9 | { 10 | public MainPage() 11 | { 12 | InitializeComponent(); 13 | 14 | var size = Device.Info.ScaledScreenSize; 15 | fastGridView.ItemTemplateSelector = new FastGridTemplateSelector( 16 | new FastGridDataTemplate(typeof(CategoryObject).Name, typeof(CategoryCell),new Size(size.Width, 70)), 17 | new FastGridDataTemplate(typeof(ProductObject).Name, typeof(ProductCell),new Size(size.Width / 2, 260)) 18 | ); 19 | 20 | BindingContext = new MainViewModel(); 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /Sample/FastGridSample/MainViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | using System.Windows.Input; 5 | using FastGridSample.DataObjects; 6 | using MvvmHelpers; 7 | using Xamarin.Forms; 8 | 9 | namespace FastGridSample 10 | { 11 | public class MainViewModel : BaseViewModel 12 | { 13 | ObservableRangeCollection _itemsSource = new ObservableRangeCollection(); 14 | ICommand _loadMoreCommand; 15 | ICommand _refreshCommand; 16 | ICommand _itemSelectedCommand; 17 | 18 | public ICommand LoadMoreCommand => _loadMoreCommand ?? 19 | (_loadMoreCommand = new Command(async () => await LoadMoreCommandAsync())); 20 | public ICommand RefreshCommand => 21 | _refreshCommand ?? (_refreshCommand = new Command(async () => await RefreshCommandAsync())); 22 | public ICommand ItemSelectedCommand => _itemSelectedCommand ?? 23 | (_itemSelectedCommand = new Command(async (o) => 24 | await ItemSelectedCommandAsync(o))); 25 | 26 | public ObservableRangeCollection ItemsSource 27 | { 28 | get => _itemsSource; 29 | set 30 | { 31 | _itemsSource = value; 32 | OnPropertyChanged(); 33 | } 34 | } 35 | 36 | public MainViewModel() 37 | { 38 | GenerateSource(); 39 | } 40 | 41 | async Task LoadMoreCommandAsync() 42 | { 43 | IsBusy = true; 44 | await Task.Delay(3000); 45 | GenerateSource(); 46 | IsBusy = false; 47 | } 48 | 49 | async Task RefreshCommandAsync() 50 | { 51 | IsBusy = true; 52 | await Task.Delay(3000); 53 | ItemsSource.Clear(); 54 | await Task.Delay(150); 55 | GenerateSource(); 56 | IsBusy = false; 57 | } 58 | 59 | async Task ItemSelectedCommandAsync(object obj) 60 | { 61 | if (obj is ProductObject product) 62 | await Application.Current.MainPage.DisplayAlert("Selected item", product.Name, "Ok"); 63 | } 64 | 65 | void GenerateSource() 66 | { 67 | var size = Device.Info.ScaledScreenSize; 68 | var imageSize = (int) ((size.Width / 2 - 40) * 2); 69 | var imageUrl = $"https://loremflickr.com/{imageSize}/{imageSize}/"; 70 | var r = new Random(DateTime.Now.Millisecond); 71 | 72 | string GetImage(string name) 73 | { 74 | return $"{imageUrl}{name}?random={r.Next()}"; 75 | } 76 | 77 | var items = new List 78 | { 79 | new CategoryObject {Name = "Fruits"}, 80 | new ProductObject() 81 | { 82 | ImageUrl = GetImage("Pears"), 83 | Name = "Pears", 84 | Price = "120 rub" 85 | }, 86 | new ProductObject() 87 | { 88 | ImageUrl = GetImage("Apples"), 89 | Name = "Apples", 90 | Price = "50 rub" 91 | }, 92 | new ProductObject() 93 | { 94 | ImageUrl = GetImage("Bananas"), 95 | Name = "Bananas", 96 | Price = "55 rub" 97 | }, 98 | new ProductObject() 99 | { 100 | ImageUrl = GetImage("Oranges"), 101 | Name = "Oranges", 102 | Price = "89 rub" 103 | }, 104 | new CategoryObject {Name = "Vegetables"}, 105 | new ProductObject() 106 | { 107 | ImageUrl = GetImage("Tomatos"), 108 | Name = "Tomatos", 109 | Price = "110 rub." 110 | }, 111 | new ProductObject() 112 | { 113 | ImageUrl = GetImage("Cucumbers"), 114 | Name = "Cucumbers", 115 | Price = "100 rub." 116 | }, 117 | new ProductObject() 118 | { 119 | ImageUrl = GetImage("Eggplants"), 120 | Name = "Eggplants", 121 | Price = "280 rub." 122 | }, 123 | new ProductObject() 124 | { 125 | ImageUrl = GetImage("Pumpkins"), 126 | Name = "Pumpkins", 127 | Price = "40 rub." 128 | }, 129 | }; 130 | ItemsSource.AddRange(items); 131 | } 132 | } 133 | } 134 | --------------------------------------------------------------------------------