summaryrefslogtreecommitdiff
path: root/Xamarin.Forms.Platform.Android/AppCompat/MasterDetailContainer.cs
blob: 1b85b42ff8979e74f26448edbabfdda46fc7d00c (plain)
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
41
42
43
44
45
46
47
48
49
50
51
52
53
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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
using Android.App;
using Android.Content;
using Fragment = Android.Support.V4.App.Fragment;
using FragmentManager = Android.Support.V4.App.FragmentManager;
using FragmentTransaction = Android.Support.V4.App.FragmentTransaction;

namespace Xamarin.Forms.Platform.Android.AppCompat
{
	internal class MasterDetailContainer : Xamarin.Forms.Platform.Android.MasterDetailContainer, IManageFragments
	{
		PageContainer _pageContainer;
		FragmentManager _fragmentManager;
		readonly bool _isMaster;
		MasterDetailPage _parent;
		Fragment _currentFragment;
		bool _disposed;

		public MasterDetailContainer(MasterDetailPage parent, bool isMaster, Context context) : base(parent, isMaster, context)
		{
			Id = FormsAppCompatActivity.GetUniqueId();
			_parent = parent;
			_isMaster = isMaster;
		}

		FragmentManager FragmentManager => _fragmentManager ?? (_fragmentManager = ((FormsAppCompatActivity)Context).SupportFragmentManager);

		protected override void OnLayout(bool changed, int l, int t, int r, int b)
		{
			base.OnLayout(changed, l, t, r, b);

			// If we're using a PageContainer (i.e., we've wrapped our contents in a Fragment),
			// Make sure that it gets laid out
			if (_pageContainer != null)
			{
				if (_isMaster)
				{
					var controller = (IMasterDetailPageController)_parent;
					var width = (int)Context.ToPixels(controller.MasterBounds.Width);
					// When the base class computes the size of the Master container, it starts at the top of the 
					// screen and adds padding (_parent.MasterBounds.Top) to leave room for the status bar
					// When this container is laid out, it's already starting from the adjusted y value of the parent,
					// so we subtract _parent.MasterBounds.Top from our starting point (to get 0) and add it to the 
					// bottom (so the master page stretches to the bottom of the screen)
					var height = (int)Context.ToPixels(controller.MasterBounds.Height + controller.MasterBounds.Top);
					_pageContainer.Layout(0, 0, width, height);
				}
				else
				{
					_pageContainer.Layout(l, t, r, b);
				}

				_pageContainer.Child.UpdateLayout();
			}
		}

		protected override void AddChildView(VisualElement childView)
		{
			_pageContainer = null;

			Page page = childView as NavigationPage ?? (Page)(childView as TabbedPage);

			if (page == null)
			{
				// The thing we're adding is not a NavigationPage or TabbedPage, so we can just use the old AddChildView 

				if (_currentFragment != null)
				{
					// But first, if the previous occupant of this container was a fragment, we need to remove it properly
					FragmentTransaction transaction = FragmentManager.BeginTransaction();
					transaction.DisallowAddToBackStack();
					transaction.Remove(_currentFragment);
					transaction.SetTransition((int)FragmentTransit.None);
					transaction.Commit();

					_currentFragment = null;
				}
				
				base.AddChildView(childView);
			}
			else
			{
				// The renderers for NavigationPage and TabbedPage both host fragments, so they need to be wrapped in a 
				// FragmentContainer in order to get isolated fragment management
				Fragment fragment = FragmentContainer.CreateInstance(page);
				
				var fc = fragment as FragmentContainer;

				fc?.SetOnCreateCallback(pc =>
				{
					_pageContainer = pc;
					SetDefaultBackgroundColor(pc.Child);
				});

				FragmentTransaction transaction = FragmentManager.BeginTransaction();
				transaction.DisallowAddToBackStack();

				if (_currentFragment != null)
				{
					transaction.Remove(_currentFragment);
				}

				transaction.Add(Id, fragment);
				transaction.SetTransition((int)FragmentTransit.FragmentOpen);
				transaction.Commit();

				_currentFragment = fragment;
			}
		}

		protected override void Dispose(bool disposing)
		{
			if (_disposed)
			{
				return;
			}

			_disposed = true;

			if (disposing)
			{
				if (_currentFragment != null)
				{
					FragmentTransaction transaction = FragmentManager.BeginTransaction();
					transaction.DisallowAddToBackStack();
					transaction.Remove(_currentFragment);
					transaction.SetTransition((int)FragmentTransit.None);
					transaction.CommitAllowingStateLoss();
					FragmentManager.ExecutePendingTransactions();

					_currentFragment = null;
				}

				_parent = null;
				_pageContainer = null;
				_fragmentManager = null;
			}

			base.Dispose(disposing);
		}

		public void SetFragmentManager(FragmentManager fragmentManager)
		{
			if (_fragmentManager == null)
				_fragmentManager = fragmentManager;
		}
	}
}