Created
June 29, 2016 17:23
-
-
Save jcmm33/9722d6ec1145437cd81fc7d66e6ea6e5 to your computer and use it in GitHub Desktop.
Appcompat Fragments
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 System.Collections.Generic; | |
using Android.Runtime; | |
using System.ComponentModel; | |
using System.Reactive.Subjects; | |
using System.Reactive; | |
using ReactiveUI; | |
namespace ReactiveUI.Framework.Android | |
{ | |
/// <summary> | |
/// This is a Fragment that is both an Activity and has ReactiveObject powers | |
/// (i.e. you can call RaiseAndSetIfChanged) | |
/// </summary> | |
public class ReactiveAppCompatFragment<TViewModel> : ReactiveAppCompatFragment, IViewFor<TViewModel>, ICanActivate | |
where TViewModel : class | |
{ | |
protected ReactiveAppCompatFragment() { } | |
protected ReactiveAppCompatFragment(IntPtr handle, JniHandleOwnership ownership) : base(handle, ownership) | |
{ | |
} | |
/// <summary> | |
/// The underlying view model | |
/// </summary> | |
//private TViewModel _viewModel; | |
private HiddenReference<TViewModel> _viewModel = new HiddenReference<TViewModel>(); | |
public TViewModel ViewModel | |
{ | |
get { return this._viewModel.Value; } | |
set | |
{ | |
this.RaiseAndSetIfChangedHiddenReference(ref this._viewModel, value); | |
} | |
} | |
object IViewFor.ViewModel { | |
get { return ViewModel; } | |
set { ViewModel = (TViewModel)value; } | |
} | |
public override void OnDestroyView() | |
{ | |
// belt and braces, clear down the view model. | |
this.ViewModel = null; | |
base.OnDestroyView(); | |
} | |
} | |
/// <summary> | |
/// This is a Fragment that is both an Activity and has ReactiveObject powers | |
/// (i.e. you can call RaiseAndSetIfChanged) | |
/// </summary> | |
public class ReactiveAppCompatFragment : global::Android.Support.V4.App.Fragment, IReactiveNotifyPropertyChanged<ReactiveAppCompatFragment>, IReactiveObject, IHandleObservableErrors | |
{ | |
protected ReactiveAppCompatFragment() { } | |
protected ReactiveAppCompatFragment(IntPtr handle, JniHandleOwnership ownership) : base(handle, ownership) | |
{ | |
} | |
public event PropertyChangingEventHandler PropertyChanging | |
{ | |
add { PropertyChangingEventManager.AddHandler(this, value); } | |
remove { PropertyChangingEventManager.RemoveHandler(this, value); } | |
} | |
void IReactiveObject.RaisePropertyChanging(PropertyChangingEventArgs args) | |
{ | |
PropertyChangingEventManager.DeliverEvent(this, args); | |
} | |
public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged | |
{ | |
add { PropertyChangedEventManager.AddHandler(this, value); } | |
remove { PropertyChangedEventManager.RemoveHandler(this, value); } | |
} | |
void IReactiveObject.RaisePropertyChanged(PropertyChangedEventArgs args) | |
{ | |
PropertyChangedEventManager.DeliverEvent(this, args); | |
} | |
/// <summary> | |
/// Represents an Observable that fires *before* a property is about to | |
/// be changed. | |
/// </summary> | |
public IObservable<IReactivePropertyChangedEventArgs<ReactiveAppCompatFragment>> Changing | |
{ | |
get { return this.getChangingObservable(); } | |
} | |
/// <summary> | |
/// Represents an Observable that fires *after* a property has changed. | |
/// </summary> | |
public IObservable<IReactivePropertyChangedEventArgs<ReactiveAppCompatFragment>> Changed | |
{ | |
get { return this.getChangedObservable(); } | |
} | |
/// <summary> | |
/// When this method is called, an object will not fire change | |
/// notifications (neither traditional nor Observable notifications) | |
/// until the return value is disposed. | |
/// </summary> | |
/// <returns>An object that, when disposed, reenables change | |
/// notifications.</returns> | |
public IDisposable SuppressChangeNotifications() | |
{ | |
return this.suppressChangeNotifications(); | |
} | |
public IObservable<Exception> ThrownExceptions { get { return this.getThrownExceptionsObservable(); } } | |
readonly Subject<Unit> activated = new Subject<Unit>(); | |
public IObservable<Unit> Activated { get { return activated; } } | |
readonly Subject<Unit> deactivated = new Subject<Unit>(); | |
public IObservable<Unit> Deactivated { get { return deactivated; } } | |
public override void OnPause() | |
{ | |
base.OnPause(); | |
deactivated.OnNext(Unit.Default); | |
} | |
public override void OnResume() | |
{ | |
base.OnResume(); | |
activated.OnNext(Unit.Default); | |
} | |
} | |
} |
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 System.Linq; | |
using System.Reactive.Disposables; | |
using System.Reflection; | |
using System.Runtime.CompilerServices; | |
using System.Threading.Tasks; | |
using Android.OS; | |
using Android.Runtime; | |
using Android.Views; | |
using Common; | |
using Common.Logging; | |
using Newtonsoft.Json.Serialization; | |
using ReactiveUI.Framework.Scope; | |
using ReactiveUI.Framework.ViewModel; | |
namespace ReactiveUI.Framework.Android.Views | |
{ | |
public abstract class RxAppCompatFragment<TViewModel> : ReactiveAppCompatFragment<TViewModel>,ILifetimeContainer,IRxLogPublisher where TViewModel:RxViewModel | |
{ | |
/// <summary> | |
/// The layout resource Id to be used. | |
/// </summary> | |
private int _layoutResourceId; | |
private static readonly MethodInfo GetControlView; | |
private readonly HiddenReference<ILifetime> _hiddenLifetime = new HiddenReference<ILifetime>(new Lifetime()); | |
public ILifetime Lifetime | |
{ | |
get { return _hiddenLifetime.Value; } | |
set { _hiddenLifetime.Value = value; } | |
} | |
protected RxAppCompatFragment(int layoutResourceId) | |
{ | |
_layoutResourceId = layoutResourceId; | |
Setup(); | |
} | |
protected RxAppCompatFragment(IntPtr handle, JniHandleOwnership ownership) : base(handle, ownership) | |
{ | |
Setup(); | |
} | |
protected RxAppCompatFragment() | |
{ | |
Setup(); | |
} | |
private void Setup() | |
{ | |
// indicate you want the fragment instance to be retained... | |
this.RetainInstance = true; | |
this.WhenActivated(d => | |
{ | |
try | |
{ | |
var activatedLifetime = Lifetime.BeginLifetimeScope(); | |
this.OnViewActivated(activatedLifetime); | |
d(Disposable.Create(OnViewDeActivated)); | |
d(activatedLifetime); | |
} | |
catch (Exception exception) | |
{ | |
this.Log().Error(exception).Throw(exception); | |
} | |
}); | |
} | |
static RxAppCompatFragment() | |
{ | |
var type = typeof(RxAppCompatFragment<TViewModel>); | |
GetControlView = type.GetMethod("GetControl", new[] { typeof(string) }); | |
} | |
public T GetControl<T>([CallerMemberName] string propertyName = null) where T : global::Android.Views.View | |
{ | |
return (T)_view.GetControl<T>(propertyName); | |
} | |
public override void OnCreate(Bundle savedInstanceState) | |
{ | |
base.OnCreate(savedInstanceState); | |
if (savedInstanceState == null) | |
{ | |
OnFirstCreate(); | |
} | |
else | |
{ | |
OnRestore(savedInstanceState); | |
} | |
} | |
private ILifetime _viewLifetime; | |
private global::Android.Views.View _view; | |
public override global::Android.Views.View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) | |
{ | |
_view = CreateView(inflater, container); | |
_viewLifetime = Lifetime.BeginLifetimeScope(); | |
this.WireUpControls(); | |
OnViewCreated(savedInstanceState); | |
CreateBindings(container,_viewLifetime); | |
return _view; | |
} | |
public override void OnDestroyView() | |
{ | |
_viewLifetime?.Dispose(); | |
_viewLifetime = null; | |
_view = null; | |
base.OnDestroyView(); | |
} | |
public virtual void OnFirstCreate() | |
{ | |
} | |
public virtual void OnRestore(Bundle savedInstanceState) | |
{ | |
OnLoad(savedInstanceState); | |
} | |
public override void OnStop() | |
{ | |
base.OnStop(); | |
} | |
public override void OnSaveInstanceState(Bundle outState) | |
{ | |
OnSave(outState); | |
base.OnSaveInstanceState(outState); | |
} | |
public virtual void OnLoad(Bundle savedState) | |
{ | |
var contractResolver = ApplicationContext.GetService<IContractResolver>(); | |
this.ViewModel = this.LoadInstance<TViewModel>(contractResolver, savedState); | |
} | |
public virtual void OnSave(Bundle outState) | |
{ | |
this.SaveInstance(outState); | |
} | |
/// <summary> | |
/// Default Create Layout | |
/// </summary> | |
/// <remarks> | |
/// Looks for RxLayoutAttribute on the class if we don't have a value</remarks> | |
public virtual global::Android.Views.View CreateView(LayoutInflater inflater,ViewGroup container) | |
{ | |
if (_layoutResourceId == 0) | |
{ | |
_layoutResourceId = this.GetContentLayoutResourceId(); | |
} | |
if (_layoutResourceId <= 0) | |
{ | |
this.Log().Error("Missing Layout Resource Id").Throw(l => new InvalidOperationException(l.Instance.Message)); | |
} | |
var view = inflater.Inflate(_layoutResourceId, null); | |
return view; | |
} | |
/// <summary> | |
/// The view has been created. Typically the layout is constructed from here | |
/// </summary> | |
/// <param name="savedInstanceState"></param> | |
public virtual void OnViewCreated(Bundle savedInstanceState) | |
{ | |
} | |
public virtual void CreateBindings(ViewGroup container,ILifetime viewLifetime) | |
{ | |
} | |
/// <summary> | |
/// Called upon Fragment activation. | |
/// </summary> | |
public virtual void OnViewActivated(ILifetime lifetime) | |
{ | |
} | |
// ReSharper disable once CSharpWarnings::CS1998 | |
/// <summary> | |
/// Called when the fragment is deactivated. | |
/// </summary> | |
/// <returns></returns> | |
public virtual void OnViewDeActivated() | |
{ | |
// need to let the presenter know that the view is going... | |
// and finally return | |
} | |
/// <summary> | |
/// Wireup the controls. | |
/// </summary> | |
public void WireUpControls() | |
{ | |
var members = | |
this.GetType() | |
.GetRuntimeProperties() | |
.Where(m => m.PropertyType.IsSubclassOf(typeof(global::Android.Views.View))); | |
members.ToList().ForEach(m => | |
{ | |
try | |
{ | |
// Find the android control with the same name | |
var view = this.GetControlInternal(_view, m.PropertyType, m.Name); | |
// Set the activity field's value to the view with that identifier | |
m.SetValue(this, view); | |
} | |
catch (Exception ex) | |
{ | |
throw new MissingFieldException("Failed to wire up the Property " | |
+ m.Name + | |
" to a View in your layout with a corresponding identifier", ex); | |
} | |
}); | |
} | |
private global::Android.Views.View GetControlInternal(global::Android.Views.View parent, Type viewType, string name) | |
{ | |
var mi = GetControlView.MakeGenericMethod(new[] { viewType }); | |
return (global::Android.Views.View)mi.Invoke(this, new object[] { name }); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment