[Android] Fix previewer exceptions with shell (#5955)

* shell preivewer

* nonappcompat hack

* - moove up null check

* internal IsDesignerContext
This commit is contained in:
Shane Neuville 2019-05-02 04:26:42 -06:00 коммит произвёл Rui Marinho
Родитель dbbc5d5413
Коммит 8a7ee8632e
14 изменённых файлов: 184 добавлений и 59 удалений

Просмотреть файл

@ -101,7 +101,7 @@ namespace Xamarin.Forms.Platform.Android.AppCompat
{
base.OnElementChanged(e);
var activity = (FormsAppCompatActivity)Context;
var activity = (FormsAppCompatActivity)Context.GetActivity();
if (e.OldElement != null)
((IPageController)e.OldElement).InternalChildren.CollectionChanged -= OnChildrenCollectionChanged;

Просмотреть файл

@ -26,7 +26,7 @@ namespace Xamarin.Forms.Platform.Android.AppCompat
public bool MarkedForDispose { get; internal set; } = false;
FragmentManager FragmentManager => _fragmentManager ?? (_fragmentManager = ((FormsAppCompatActivity)Context).SupportFragmentManager);
FragmentManager FragmentManager => _fragmentManager ?? (_fragmentManager = Context.GetFragmentManager());
protected override void OnLayout(bool changed, int l, int t, int r, int b)
{

Просмотреть файл

@ -88,7 +88,7 @@ namespace Xamarin.Forms.Platform.Android.AppCompat
{
if (_platform == null)
{
if (Context is FormsAppCompatActivity activity)
if (Context.GetActivity() is FormsAppCompatActivity activity)
{
_platform = activity.Platform;
}
@ -122,7 +122,7 @@ namespace Xamarin.Forms.Platform.Android.AppCompat
}
}
FragmentManager FragmentManager => _fragmentManager ?? (_fragmentManager = ((FormsAppCompatActivity)Context).SupportFragmentManager);
FragmentManager FragmentManager => _fragmentManager ?? (_fragmentManager = Context.GetFragmentManager());
IPageController PageController => Element;
@ -745,7 +745,7 @@ namespace Xamarin.Forms.Platform.Android.AppCompat
void SetupToolbar()
{
Context context = Context;
var activity = (FormsAppCompatActivity)context;
var activity = context.GetActivity();
AToolbar bar;
if (FormsAppCompatActivity.ToolbarResource != 0)
@ -935,7 +935,6 @@ namespace Xamarin.Forms.Platform.Android.AppCompat
return;
Context context = Context;
var activity = (FormsAppCompatActivity)context;
AToolbar bar = _toolbar;
ActionBarDrawerToggle toggle = _drawerToggle;
@ -954,8 +953,9 @@ namespace Xamarin.Forms.Platform.Android.AppCompat
toggle.SyncState();
}
if (NavigationPage.GetHasBackButton(currentPage))
if (NavigationPage.GetHasBackButton(currentPage) && !Context.IsDesignerContext())
{
var activity = (global::Android.Support.V7.App.AppCompatActivity)context.GetActivity();
var icon = new DrawerArrowDrawable(activity.SupportActionBar.ThemedContext);
icon.Progress = 1;
bar.NavigationIcon = icon;

Просмотреть файл

@ -369,8 +369,7 @@ namespace Xamarin.Forms.Platform.Android.AppCompat
void LayoutRootPage(Page page, int width, int height)
{
var activity = (FormsAppCompatActivity)_context;
page.Layout(new Rectangle(0, 0, activity.FromPixels(width), activity.FromPixels(height)));
page.Layout(new Rectangle(0, 0, _context.FromPixels(width), _context.FromPixels(height)));
}
Task PresentModal(Page modal, bool animated)
@ -457,9 +456,7 @@ namespace Xamarin.Forms.Platform.Android.AppCompat
{
if (changed)
{
var activity = (FormsAppCompatActivity)Context;
_modal.Layout(new Rectangle(0, 0, activity.FromPixels(r - l), activity.FromPixels(b - t)));
_modal.Layout(new Rectangle(0, 0, Context.FromPixels(r - l), Context.FromPixels(b - t)));
_backgroundView.Layout(0, 0, r - l, b - t);
}

Просмотреть файл

@ -73,7 +73,7 @@ namespace Xamarin.Forms.Platform.Android.AppCompat
}
}
FragmentManager FragmentManager => _fragmentManager ?? (_fragmentManager = ((FormsAppCompatActivity)Context).SupportFragmentManager);
FragmentManager FragmentManager => _fragmentManager ?? (_fragmentManager = Context.GetFragmentManager());
bool IsBottomTabPlacement => (Element != null) ? Element.OnThisPlatform().GetToolbarPlacement() == ToolbarPlacement.Bottom : false;
public Color BarItemColor
@ -231,7 +231,7 @@ namespace Xamarin.Forms.Platform.Android.AppCompat
{
base.OnElementChanged(e);
var activity = (FormsAppCompatActivity)Context;
var activity = Context.GetActivity();
if (e.OldElement != null)
((IPageController)e.OldElement).InternalChildren.CollectionChanged -= OnChildrenCollectionChanged;

Просмотреть файл

@ -5,6 +5,7 @@ using Android.Util;
using Android.Views.InputMethods;
using AApplicationInfoFlags = Android.Content.PM.ApplicationInfoFlags;
using AActivity = Android.App.Activity;
using AFragmentManager = Android.Support.V4.App.FragmentManager;
namespace Xamarin.Forms.Platform.Android
{
@ -96,5 +97,49 @@ namespace Xamarin.Forms.Platform.Android
return null;
}
internal static Context GetThemedContext(this Context context)
{
if (context == null)
return null;
if (context.IsDesignerContext())
return context;
if (context is global::Android.Support.V7.App.AppCompatActivity activity)
return activity.SupportActionBar.ThemedContext;
if (context is ContextWrapper contextWrapper)
return contextWrapper.BaseContext.GetThemedContext();
return null;
}
internal static bool IsDesignerContext(this Context context)
{
if (context == null)
return false;
if ($"{context.ToString()}".Contains("com.android.layoutlib.bridge.android.BridgeContext"))
return true;
if (context is ContextWrapper contextWrapper)
return contextWrapper.BaseContext.IsDesignerContext();
return false;
}
public static AFragmentManager GetFragmentManager(this Context context)
{
if (context == null)
return null;
var activity = context.GetActivity();
if (activity is global::Android.Support.V4.App.FragmentActivity fa)
return fa.SupportFragmentManager;
return null;
}
}
}

Просмотреть файл

@ -50,7 +50,7 @@ namespace Xamarin.Forms.Platform.Android
if (disposing)
{
_renderer.Dispose();
_renderer?.Dispose();
_renderer = null;
_view = null;
_context = null;

Просмотреть файл

@ -38,13 +38,48 @@ namespace Xamarin.Forms.Platform.Android
protected virtual void LoadView(IShellContext shellContext)
{
var context = shellContext.AndroidContext;
var coordinator = LayoutInflater.FromContext(context).Inflate(Resource.Layout.FlyoutContent, null);
var recycler = coordinator.FindViewById<RecyclerView>(Resource.Id.flyoutcontent_recycler);
var appBar = coordinator.FindViewById<AppBarLayout>(Resource.Id.flyoutcontent_appbar);
var coordinator = (ViewGroup)LayoutInflater.FromContext(context).Inflate(Resource.Layout.FlyoutContent, null);
var recycler = coordinator.FindViewById<RecyclerView>(Resource.Id.flyoutcontent_recycler);
var appBar = coordinator.FindViewById<ViewGroup>(Resource.Id.flyoutcontent_appbar);
_rootView = coordinator;
_rootView = coordinator;
appBar.AddOnOffsetChangedListener(this);
if((recycler == null || appBar == null) && !context.IsDesignerContext())
{
if (recycler == null)
throw new ArgumentNullException("flyoutcontent_recycler", "Unable to find layout for flyoutcontent_recycler");
// PREVIEWER HACK for some reason previewer pulls this out as a FrameLayout and ignores the internal resources
if (appBar == null)
throw new ArgumentNullException("flyoutcontent_appbar", "Unable to find layout for flyoutcontent_recycler");
}
try
{
// PREVIEWER HACK for some reason previewer can't find the resources for the recycler or the appBar
if (recycler == null)
recycler = (RecyclerView)coordinator.GetChildAt(1);
if (appBar == null)
appBar = (AppBarLayout)coordinator.GetChildAt(0);
}
catch
{
// PReviewer hack.
// appcompat and non appcompat initialize this whole thing differently so the above are for appcompat the below are for non
}
// PREVIEWER HACK for some reason previewer can't find the resources for the recycler or the appBar
if (recycler == null)
recycler = coordinator.FindViewById<RecyclerView>(context.Resources.GetIdentifier("flyoutcontent_recycler", "id", context.PackageName));
// PREVIEWER HACK for some reason previewer pulls this out as a FrameLayout and ignores the internal resources
if (appBar == null)
appBar = coordinator.FindViewById<ViewGroup>(context.Resources.GetIdentifier("flyoutcontent_appbar", "id", context.PackageName));
(appBar as AppBarLayout)?.AddOnOffsetChangedListener(this);
int actionBarHeight = (int)context.ToPixels(56);
@ -68,11 +103,14 @@ namespace Xamarin.Forms.Platform.Android
var metrics = context.Resources.DisplayMetrics;
var width = Math.Min(metrics.WidthPixels, metrics.HeightPixels);
TypedValue tv = new TypedValue();
if (context.Theme.ResolveAttribute(global::Android.Resource.Attribute.ActionBarSize, tv, true))
{
actionBarHeight = TypedValue.ComplexToDimensionPixelSize(tv.Data, metrics);
}
using (TypedValue tv = new TypedValue())
{
if (context.Theme.ResolveAttribute(global::Android.Resource.Attribute.ActionBarSize, tv, true))
{
actionBarHeight = TypedValue.ComplexToDimensionPixelSize(tv.Data, metrics);
}
}
width -= actionBarHeight;
coordinator.LayoutParameters = new LP(width, LP.MatchParent);

Просмотреть файл

@ -36,7 +36,10 @@ namespace Xamarin.Forms.Platform.Android
VisualElementTracker IVisualElementRenderer.Tracker => null;
AView IVisualElementRenderer.View => _flyoutRenderer.AndroidView;
ViewGroup IVisualElementRenderer.ViewGroup => _flyoutRenderer.AndroidView as ViewGroup;
// Used by Previewer
[EditorBrowsable(EditorBrowsableState.Never)]
public ViewGroup ViewGroup => _flyoutRenderer.AndroidView as ViewGroup;
SizeRequest IVisualElementRenderer.GetDesiredSize(int widthConstraint, int heightConstraint)
{
@ -59,7 +62,9 @@ namespace Xamarin.Forms.Platform.Android
{
}
void IVisualElementRenderer.UpdateLayout()
// Used by Previewer
[EditorBrowsable(EditorBrowsableState.Never)]
public void UpdateLayout()
{
var width = (int)AndroidContext.ToPixels(Element.Width);
var height = (int)AndroidContext.ToPixels(Element.Height);
@ -151,7 +156,7 @@ namespace Xamarin.Forms.Platform.Android
protected Context AndroidContext { get; }
protected Shell Element { get; private set; }
private FragmentManager FragmentManager => ((FormsAppCompatActivity)AndroidContext).SupportFragmentManager;
FragmentManager FragmentManager => AndroidContext.GetFragmentManager();
protected virtual IShellObservableFragment CreateFragmentForPage(Page page)
{
@ -202,9 +207,7 @@ namespace Xamarin.Forms.Platform.Android
protected virtual void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == Shell.CurrentItemProperty.PropertyName)
{
SwitchFragment(FragmentManager, _frameLayout, Element.CurrentItem);
}
_elementPropertyChanged?.Invoke(sender, e);
}
@ -223,13 +226,18 @@ namespace Xamarin.Forms.Platform.Android
((IShellController)shell).AddAppearanceObserver(this, shell);
SwitchFragment(FragmentManager, _frameLayout, shell.CurrentItem, false);
// Previewer Hack
if(AndroidContext.GetActivity() != null)
SwitchFragment(FragmentManager, _frameLayout, shell.CurrentItem, false);
}
IShellItemRenderer _currentRenderer;
protected virtual void SwitchFragment(FragmentManager manager, AView targetView, ShellItem newItem, bool animate = true)
{
if (AndroidContext.IsDesignerContext())
return;
var previousRenderer = _currentRenderer;
_currentRenderer = CreateShellItemRenderer(newItem);
_currentRenderer.ShellItem = newItem;
@ -266,10 +274,10 @@ namespace Xamarin.Forms.Platform.Android
void UpdateStatusBarColor(ShellAppearance appearance)
{
var activity = ((FormsAppCompatActivity)AndroidContext);
var window = activity.Window;
var decorView = window.DecorView;
var resources = activity.Resources;
var activity = AndroidContext.GetActivity();
var window = activity?.Window;
var decorView = window?.DecorView;
var resources = AndroidContext.Resources;
int statusBarHeight = 0;
int resourceId = resources.GetIdentifier("status_bar_height", "dimen", "android");
@ -285,19 +293,23 @@ namespace Xamarin.Forms.Platform.Android
navigationBarHeight = resources.GetDimensionPixelSize(resourceId);
}
// we are using the split drawable here to avoid GPU overdraw.
// All it really is is a drawable that only draws under the statusbar/bottom bar to make sure
// we dont draw over areas we dont need to. This has very limited benefits considering its
// only saving us a flat color fill BUT it helps people not freak out about overdraw.
if (appearance != null)
// TODO Previewer Hack
if (decorView != null)
{
var color = appearance.BackgroundColor.ToAndroid(Color.FromHex("#03A9F4"));
decorView.SetBackground(new SplitDrawable(color, statusBarHeight, navigationBarHeight));
}
else
{
var color = Color.FromHex("#03A9F4").ToAndroid();
decorView.SetBackground(new SplitDrawable(color, statusBarHeight, navigationBarHeight));
// we are using the split drawable here to avoid GPU overdraw.
// All it really is is a drawable that only draws under the statusbar/bottom bar to make sure
// we dont draw over areas we dont need to. This has very limited benefits considering its
// only saving us a flat color fill BUT it helps people not freak out about overdraw.
if (appearance != null)
{
var color = appearance.BackgroundColor.ToAndroid(Color.FromHex("#03A9F4"));
decorView.SetBackground(new SplitDrawable(color, statusBarHeight, navigationBarHeight));
}
else
{
var color = Color.FromHex("#03A9F4").ToAndroid();
decorView.SetBackground(new SplitDrawable(color, statusBarHeight, navigationBarHeight));
}
}
}

Просмотреть файл

@ -236,23 +236,21 @@ namespace Xamarin.Forms.Platform.Android
var backButtonHandler = Shell.GetBackButtonBehavior(page);
toolbar.SetNavigationOnClickListener(this);
var activity = (FormsAppCompatActivity)context;
if (backButtonHandler != null)
{
await UpdateDrawerArrowFromBackButtonBehavior(context, toolbar, drawerLayout, backButtonHandler, activity);
await UpdateDrawerArrowFromBackButtonBehavior(context, toolbar, drawerLayout, backButtonHandler);
}
else
{
await UpdateDrawerArrow(context, toolbar, drawerLayout, activity);
await UpdateDrawerArrow(context, toolbar, drawerLayout);
}
}
protected virtual async Task UpdateDrawerArrow(Context context, Toolbar toolbar, DrawerLayout drawerLayout, FormsAppCompatActivity activity)
protected virtual async Task UpdateDrawerArrow(Context context, Toolbar toolbar, DrawerLayout drawerLayout)
{
if (_drawerToggle == null)
if (_drawerToggle == null && !context.IsDesignerContext())
{
_drawerToggle = new ActionBarDrawerToggle((Activity)context, drawerLayout, toolbar,
_drawerToggle = new ActionBarDrawerToggle(context.GetActivity(), drawerLayout, toolbar,
R.String.Ok, R.String.Ok)
{
ToolbarNavigationClickListener = this,
@ -267,7 +265,7 @@ namespace Xamarin.Forms.Platform.Android
if (CanNavigateBack)
{
_drawerToggle.DrawerIndicatorEnabled = false;
using (var icon = new DrawerArrowDrawable(activity.SupportActionBar.ThemedContext))
using (var icon = new DrawerArrowDrawable(context.GetThemedContext()))
{
icon.SetColorFilter(TintColor.ToAndroid(Color.White), PorterDuff.Mode.SrcAtop);
icon.Progress = 1;
@ -288,7 +286,7 @@ namespace Xamarin.Forms.Platform.Android
_drawerToggle.SyncState();
}
protected virtual async Task UpdateDrawerArrowFromBackButtonBehavior(Context context, Toolbar toolbar, DrawerLayout drawerLayout, BackButtonBehavior backButtonHandler, FormsAppCompatActivity activity)
protected virtual async Task UpdateDrawerArrowFromBackButtonBehavior(Context context, Toolbar toolbar, DrawerLayout drawerLayout, BackButtonBehavior backButtonHandler)
{
var behavior = backButtonHandler;
var command = behavior.Command;
@ -307,7 +305,7 @@ namespace Xamarin.Forms.Platform.Android
if (CanNavigateBack && icon == null)
{
icon = new DrawerArrowDrawable(activity.SupportActionBar.ThemedContext);
icon = new DrawerArrowDrawable(context.GetThemedContext());
(icon as DrawerArrowDrawable).Progress = 1;
}

Просмотреть файл

@ -9,13 +9,13 @@
>
<android.support.design.widget.AppBarLayout
android:id="@+id/flyoutcontent.appbar"
android:id="@+id/flyoutcontent_appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" />
<android.support.v7.widget.RecyclerView
android:id="@+id/flyoutcontent.recycler"
android:id="@+id/flyoutcontent_recycler"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />

Просмотреть файл

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8" ?>
<Shell xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Xamarin.Forms.Sandbox.ShellPage">
<ShellContent Title="test">
<ContentPage></ContentPage>
</ShellContent>
</Shell>

Просмотреть файл

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace Xamarin.Forms.Sandbox
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class ShellPage : Shell
{
public ShellPage()
{
InitializeComponent();
}
}
}

Просмотреть файл

@ -13,6 +13,12 @@
<DebugType>pdbonly</DebugType>
<DebugSymbols>true</DebugSymbols>
</PropertyGroup>
<ItemGroup>
<None Include="ShellPage.xaml">
<Generator>MSBuild:Compile</Generator>
</None>
</ItemGroup>
<ItemGroup>
<!-- A reference to the entire .NET Framework is automatically included -->