Created
February 23, 2018 08:14
-
-
Save mmierzwa/0d19d3e0b5e5c966b734448f7beb061e to your computer and use it in GitHub Desktop.
PullToRefreshLayout on Android
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using Xamarin.Forms; | |
using Xamarin.Forms.Platform.Android; | |
using Android.Support.V4.Widget; | |
using Android.Views; | |
using System.Reflection; | |
using System.ComponentModel; | |
using Android.Gms.Maps; | |
using Android.OS; | |
using View = Android.Views.View; | |
namespace example.CustomRenderers | |
{ | |
public class PullToRefreshLayoutRenderer : SwipeRefreshLayout, IVisualElementRenderer, SwipeRefreshLayout.IOnRefreshListener | |
{ | |
private bool _init; | |
private IVisualElementRenderer _visualElementRenderer; | |
private BindableProperty _rendererProperty; | |
private bool _refreshing; | |
public ExtendedMapViewRenderer NestedMapView { get; set; } | |
public event EventHandler<VisualElementChangedEventArgs> ElementChanged; | |
private BindableProperty RendererProperty | |
{ | |
get | |
{ | |
if (_rendererProperty != null) | |
{ | |
return _rendererProperty; | |
} | |
var type = Type.GetType("Xamarin.Forms.Platform.Android.Platform, Xamarin.Forms.Platform.Android"); | |
var prop = type.GetField(nameof(RendererProperty), BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); | |
var val = prop.GetValue(null); | |
_rendererProperty = val as BindableProperty; | |
return _rendererProperty; | |
} | |
} | |
public override bool IsInLayout => this.SafeExecute(() => base.IsInLayout); | |
public override bool IsLayoutRequested => this.SafeExecute(() => base.IsLayoutRequested); | |
public override ViewStates Visibility | |
{ | |
get => this.SafeExecute(() => base.Visibility); | |
set => this.SafeExecute(() => base.Visibility = value); | |
} | |
public override bool Refreshing | |
{ | |
get => _refreshing; | |
set | |
{ | |
this.SafeExecute(() => | |
{ | |
_refreshing = value; | |
if (RefreshView != null && RefreshView.IsRefreshing != _refreshing) | |
{ | |
RefreshView.IsRefreshing = _refreshing; | |
} | |
if (base.Refreshing == _refreshing) | |
{ | |
return; | |
} | |
base.Refreshing = _refreshing; | |
}); | |
} | |
} | |
public PullToRefreshLayout RefreshView => (PullToRefreshLayout) Element; | |
public VisualElementTracker Tracker { get; private set; } | |
public VisualElement Element { get; private set; } | |
public ViewGroup ViewGroup => this; | |
public static async void Init() | |
{ | |
DateTime temp = DateTime.Now; | |
} | |
public PullToRefreshLayoutRenderer() | |
: base(Forms.Context) | |
{ | |
} | |
public PullToRefreshLayoutRenderer(IntPtr javaReference, Android.Runtime.JniHandleOwnership transfer) | |
: base(javaReference, transfer) | |
{ | |
} | |
public void SetElement(VisualElement element) | |
{ | |
this.SafeExecute(() => SetElementInternal(element)); | |
} | |
private void SetElementInternal(VisualElement element) | |
{ | |
var oldElement = Element; | |
if (oldElement != null) | |
{ | |
oldElement.PropertyChanged -= SafeHandlePropertyChanged; | |
} | |
Element = element; | |
if (Element != null) | |
{ | |
UpdateContent(); | |
Element.PropertyChanged += SafeHandlePropertyChanged; | |
} | |
if (!_init) | |
{ | |
_init = true; | |
Tracker = new VisualElementTracker(this); | |
SetOnRefreshListener(this); | |
} | |
UpdateColors(); | |
UpdateIsRefreshing(); | |
UpdateIsSwipeToRefreshEnabled(); | |
ElementChanged?.Invoke(this, new VisualElementChangedEventArgs(oldElement, this.Element)); | |
} | |
private void UpdateContent() | |
{ | |
if (RefreshView.Content == null) | |
{ | |
return; | |
} | |
if (_visualElementRenderer != null) | |
{ | |
RemoveView(_visualElementRenderer.ViewGroup); | |
} | |
_visualElementRenderer = Platform.CreateRenderer(RefreshView.Content); | |
try | |
{ | |
RefreshView.Content.SetValue(RendererProperty, _visualElementRenderer); | |
} | |
catch (Exception ex) | |
{ | |
System.Diagnostics.Debug.WriteLine("Unable to sent renderer property, maybe an issue: " + ex); | |
} | |
AddView(_visualElementRenderer.ViewGroup, LayoutParams.MatchParent); | |
NestedMapView = FindNestedMapView(_visualElementRenderer.ViewGroup); | |
if (NestedMapView != null) | |
{ | |
NestedMapView.UserSwipingOnMap += MapViewOnUserSwipingOnMap; | |
} | |
} | |
private void UpdateColors() | |
{ | |
if (RefreshView == null) | |
{ | |
return; | |
} | |
if (RefreshView.RefreshColor != Color.Default) | |
{ | |
SetColorSchemeColors(RefreshView.RefreshColor.ToAndroid()); | |
} | |
if (RefreshView.RefreshBackgroundColor != Color.Default) | |
{ | |
SetProgressBackgroundColorSchemeColor(RefreshView.RefreshBackgroundColor.ToAndroid()); | |
} | |
} | |
private void UpdateIsRefreshing() | |
{ | |
Refreshing = RefreshView.IsRefreshing; | |
} | |
private void UpdateIsSwipeToRefreshEnabled() | |
{ | |
Enabled = Element.IsEnabled = RefreshView.IsPullToRefreshEnabled; | |
} | |
public override bool CanChildScrollUp() | |
{ | |
return CanScrollUp(_visualElementRenderer.ViewGroup); | |
} | |
private static ExtendedMapViewRenderer FindNestedMapView(ViewGroup viewGroup) | |
{ | |
if (viewGroup == null) | |
{ | |
return null; | |
} | |
for (var i = 0; i < viewGroup.ChildCount; i++) | |
{ | |
var child = viewGroup.GetChildAt(i); | |
if (child is ExtendedMapViewRenderer mapView) | |
{ | |
return mapView; | |
} | |
if (child is ViewGroup childViewGroup) | |
{ | |
var view = FindNestedMapView(childViewGroup); | |
if (view != null) | |
{ | |
return view; | |
} | |
} | |
} | |
return null; | |
} | |
private bool CanScrollUp(ViewGroup viewGroup) | |
{ | |
if (viewGroup == null) | |
{ | |
return base.CanChildScrollUp(); | |
} | |
var sdk = (int)Build.VERSION.SdkInt; | |
if (sdk >= 16) | |
{ | |
if (viewGroup.IsScrollContainer) | |
{ | |
return base.CanChildScrollUp(); | |
} | |
} | |
for (var i = 0; i < viewGroup.ChildCount; i++) | |
{ | |
View child = viewGroup.GetChildAt(i); | |
if (child is Android.Widget.AbsListView) | |
{ | |
var list = child as Android.Widget.AbsListView; | |
if (list.FirstVisiblePosition == 0) | |
{ | |
View subChild = list.GetChildAt(0); | |
return subChild != null && subChild.Top != 0; | |
} | |
return true; | |
} | |
if (child is Android.Widget.ScrollView) | |
{ | |
var scrollview = child as Android.Widget.ScrollView; | |
return (scrollview.ScrollY <= 0.0); | |
} | |
if (child is Android.Webkit.WebView) | |
{ | |
var webView = child as Android.Webkit.WebView; | |
return (webView.ScrollY > 0.0); | |
} | |
if (child is SwipeRefreshLayout) | |
{ | |
return CanScrollUp(child as ViewGroup); | |
} | |
} | |
return false; | |
} | |
private void MapViewOnUserSwipingOnMap(bool swipingInProgress) | |
{ | |
this.SafeExecute(() => Enabled = Element.IsEnabled = RefreshView.IsPullToRefreshEnabled = !swipingInProgress); | |
} | |
public void OnRefresh() | |
{ | |
RefreshView?.RefreshCommand?.Execute(null); | |
} | |
private void SafeHandlePropertyChanged(object sender, PropertyChangedEventArgs e) | |
{ | |
this.SafeExecute(() => HandlePropertyChanged(e)); | |
} | |
private void HandlePropertyChanged(PropertyChangedEventArgs e) | |
{ | |
if (e.PropertyName == "Content") | |
{ | |
UpdateContent(); | |
} | |
else if (e.PropertyName == PullToRefreshLayout.IsPullToRefreshEnabledProperty.PropertyName) | |
{ | |
UpdateIsSwipeToRefreshEnabled(); | |
} | |
else if (e.PropertyName == PullToRefreshLayout.IsRefreshingProperty.PropertyName) | |
{ | |
UpdateIsRefreshing(); | |
} | |
else if (e.PropertyName == PullToRefreshLayout.RefreshColorProperty.PropertyName) | |
{ | |
UpdateColors(); | |
} | |
else if (e.PropertyName == PullToRefreshLayout.RefreshBackgroundColorProperty.PropertyName) | |
{ | |
UpdateColors(); | |
} | |
} | |
public SizeRequest GetDesiredSize(int widthConstraint, int heightConstraint) | |
{ | |
_visualElementRenderer.ViewGroup.Measure(widthConstraint, heightConstraint); | |
//Measure child here and determine size | |
return new SizeRequest(new Size(_visualElementRenderer.ViewGroup.MeasuredWidth, _visualElementRenderer.ViewGroup.MeasuredHeight)); | |
} | |
public void UpdateLayout() | |
{ | |
this.SafeExecute(() => Tracker?.UpdateLayout()); | |
} | |
protected override void Dispose(bool disposing) | |
{ | |
base.Dispose(disposing); | |
if (Element != null) | |
{ | |
Element.PropertyChanged -= SafeHandlePropertyChanged; | |
} | |
if (NestedMapView != null) | |
{ | |
NestedMapView.UserSwipingOnMap -= MapViewOnUserSwipingOnMap; | |
} | |
_init = false; | |
} | |
public override void RequestLayout() | |
{ | |
this.SafeExecute(() => base.RequestLayout()); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment