Přizpůsobení zobrazení WebView
A Xamarin.FormsWebView
je zobrazení, které zobrazuje webový obsah a obsah HTML ve vaší aplikaci. Tento článek vysvětluje, jak vytvořit vlastní renderer, který rozšiřuje WebView
povolení vyvolání kódu jazyka C#z JavaScriptu.
Každé Xamarin.Forms zobrazení má doprovodný renderer pro každou platformu, která vytvoří instanci nativního ovládacího prvku. WebView
Když aplikace v iOSu Xamarin.Forms vykresluje aplikaci, WkWebViewRenderer
vytvoří se instance třídy, která následně vytvoří instanci nativního WkWebView
ovládacího prvku. Na platformě Android třída WebViewRenderer
vytvoří instanci nativního WebView
ovládacího prvku. V Univerzální platforma Windows (UPW) třída WebViewRenderer
vytvoří instanci nativního WebView
ovládacího prvku. Další informace o rendereru a nativních tříd ovládacích prvcích, které Xamarin.Forms řídí mapování, naleznete v tématu Renderer Základní třídy a nativní ovládací prvky.
Následující diagram znázorňuje vztah mezi View
a odpovídajícími nativními ovládacími prvky, které ho implementují:
Proces vykreslování lze použít k implementaci přizpůsobení platformy vytvořením vlastního rendereru pro každou platformu WebView
. Postup je následující:
HybridWebView
Vytvořte vlastní ovládací prvek.- Spotřebujte
HybridWebView
z Xamarin.Forms. - Vytvořte vlastní renderer pro každou platformu
HybridWebView
.
Každá položka se teď bude zabývat implementací rendereru HybridWebView
Xamarin.FormsWebView
, který vylepšuje povolení vyvolání kódu jazyka C# z JavaScriptu. Instance HybridWebView
se použije k zobrazení stránky HTML, která uživatele požádá o zadání jeho jména. Když uživatel klikne na tlačítko HTML, vyvolá funkce JavaScriptu jazyk C# Action
, která zobrazí automaticky otevírané okno obsahující jméno uživatele.
Další informace o procesu vyvolání jazyka C# z JavaScriptu najdete v tématu Vyvolání jazyka C# z JavaScriptu. Další informace o stránce HTML naleznete v tématu Vytvoření webové stránky.
Poznámka:
Funkce WebView
JavaScriptu může vyvolat z jazyka C# a vrátit jakýkoli výsledek volajícímu kódu C#. Další informace najdete v tématu Vyvolání JavaScriptu.
Vytvoření HybridWebView
Vlastní HybridWebView
ovládací prvek lze vytvořit podtřídou WebView
třídy:
public class HybridWebView : WebView
{
Action<string> action;
public static readonly BindableProperty UriProperty = BindableProperty.Create(
propertyName: "Uri",
returnType: typeof(string),
declaringType: typeof(HybridWebView),
defaultValue: default(string));
public string Uri
{
get { return (string)GetValue(UriProperty); }
set { SetValue(UriProperty, value); }
}
public void RegisterAction(Action<string> callback)
{
action = callback;
}
public void Cleanup()
{
action = null;
}
public void InvokeAction(string data)
{
if (action == null || data == null)
{
return;
}
action.Invoke(data);
}
}
Vlastní HybridWebView
ovládací prvek se vytvoří v projektu knihovny .NET Standard a definuje pro tento ovládací prvek následující rozhraní API:
- Vlastnost
Uri
, která určuje adresu webové stránky, která se má načíst. - Metoda
RegisterAction
, která zaregistruje ovládacíAction
prvek. Registrovaná akce bude vyvolána z JavaScriptu obsaženého v souboru HTML odkazovaném prostřednictvímUri
vlastnosti. - Metoda
CleanUp
, která odebere odkaz na zaregistrovanýAction
. - Metoda
InvokeAction
, která vyvolá zaregistrovanýAction
. Tato metoda bude volána z vlastního rendereru v každém projektu platformy.
Využití HybridWebView
Vlastní HybridWebView
ovládací prvek lze odkazovat v XAML v projektu knihovny .NET Standard deklarováním oboru názvů pro jeho umístění a použitím předpony oboru názvů u vlastního ovládacího prvku. Následující příklad kódu ukazuje, jak HybridWebView
může vlastní ovládací prvek využívat stránka XAML:
<ContentPage ...
xmlns:local="clr-namespace:CustomRenderer;assembly=CustomRenderer"
x:Class="CustomRenderer.HybridWebViewPage"
Padding="0,40,0,0">
<local:HybridWebView x:Name="hybridWebView"
Uri="index.html" />
</ContentPage>
Předponu local
oboru názvů lze pojmenovat cokoli. clr-namespace
Hodnoty ale assembly
musí odpovídat podrobnostem vlastního ovládacího prvku. Jakmile je obor názvů deklarován, předpona se použije k odkazování na vlastní ovládací prvek.
Následující příklad kódu ukazuje, jak HybridWebView
může vlastní ovládací prvek využívat stránka jazyka C#:
public HybridWebViewPageCS()
{
var hybridWebView = new HybridWebView
{
Uri = "index.html"
};
// ...
Padding = new Thickness(0, 40, 0, 0);
Content = hybridWebView;
}
Instance HybridWebView
se použije k zobrazení nativního webového ovládacího prvku na každé platformě. Uri
Vlastnost je nastavena na soubor HTML, který je uložen v každém projektu platformy a který se zobrazí nativním webovým ovládacím prvku. Vykreslený kód HTML požádá uživatele, aby zadal své jméno, přičemž funkce JavaScriptu vyvolá jazyk C# Action
v reakci na tlačítko HTML.
Zaregistruje HybridWebViewPage
akci, která se má vyvolat z JavaScriptu, jak je znázorněno v následujícím příkladu kódu:
public partial class HybridWebViewPage : ContentPage
{
public HybridWebViewPage()
{
// ...
hybridWebView.RegisterAction(data => DisplayAlert("Alert", "Hello " + data, "OK"));
}
}
Tato akce volá metodu DisplayAlert
k zobrazení modální automaticky otevírané okno, které zobrazí název zadaný na stránce HTML zobrazené HybridWebView
instancí.
Do každého projektu aplikace je teď možné přidat vlastní renderer, který vylepšuje webové ovládací prvky platformy tím, že umožní vyvolání kódu jazyka C# z JavaScriptu.
Vytvoření vlastního rendereru na jednotlivých platformách
Proces vytvoření vlastní třídy rendereru je následující:
- Vytvořte podtřídu
WkWebViewRenderer
třídy v iOSu a třídu v AndroiduWebViewRenderer
a UPW, která vykreslí vlastní ovládací prvek. - Přepište metodu
OnElementChanged
, která vykreslí logiku zápisuWebView
, aby ji přizpůsobila. Tato metoda je volána při vytvoření objektuHybridWebView
. ExportRenderer
Přidejte atribut do vlastní třídy rendereru nebo AssemblyInfo.cs a určete, že se použije k vykreslení vlastního Xamarin.Forms ovládacího prvku. Tento atribut se používá k registraci vlastního rendereru v Xamarin.Forms.
Poznámka:
Pro většinu Xamarin.Forms prvků je volitelné poskytnout vlastní renderer v každém projektu platformy. Pokud není zaregistrovaný vlastní renderer, použije se výchozí renderer základní třídy ovládacího prvku. Vlastní renderery jsou však vyžadovány v každém projektu platformy při vykreslování prvku View .
Následující diagram znázorňuje zodpovědnosti jednotlivých projektů v ukázkové aplikaci spolu s relacemi mezi nimi:
Vlastní HybridWebView
ovládací prvek se vykresluje pomocí tříd rendereru platformy, které jsou odvozeny od WkWebViewRenderer
třídy v iOSu a třídy WebViewRenderer
v Androidu a UPW. Výsledkem je vykreslení každého HybridWebView
vlastního ovládacího prvku s nativními webovými ovládacími prvky, jak je znázorněno na následujících snímcích obrazovky:
Třídy WkWebViewRenderer
WebViewRenderer
zpřístupňují metodu OnElementChanged
, která je volána při Xamarin.Forms vytvoření vlastního ovládacího prvku pro vykreslení odpovídajícího nativního webového ovládacího prvku. Tato metoda přebírá VisualElementChangedEventArgs
parametr, který obsahuje OldElement
a NewElement
vlastnosti. Tyto vlastnosti představují Xamarin.Forms prvek, ke kterému byl renderer připojen, a Xamarin.Forms prvek, ke kterému je renderer připojen, v uvedeném pořadí. V ukázkové aplikaci OldElement
bude vlastnost a NewElement
vlastnost bude obsahovat odkaz na HybridWebView
null
instanci.
Přepsáná verze OnElementChanged
metody, v každé třídě rendereru platformy, je místo k provedení přizpůsobení nativního webového ovládacího prvku. Odkaz na Xamarin.Forms vykreslovaný ovládací prvek lze získat prostřednictvím Element
vlastnosti.
Každá vlastní třída rendereru je zdobena ExportRenderer
atributem, který registruje renderer s Xamarin.Forms. Atribut má dva parametry – název typu vykreslovaného vlastního Xamarin.Forms ovládacího prvku a název typu vlastního rendereru. Předpona assembly
atributu určuje, že atribut se vztahuje na celé sestavení.
Následující části popisuje strukturu webové stránky načtené jednotlivými nativními webovými ovládacími prvky, proces vyvolání jazyka C# z JavaScriptu a jeho implementaci v každé třídě vlastního vykreslovacího modulu platformy.
Vytvoření webové stránky
Následující příklad kódu ukazuje webovou stránku, která se zobrazí vlastním ovládacím HybridWebView
prvku:
<html>
<body>
<script src="http://code.jquery.com/jquery-2.1.4.min.js"></script>
<h1>HybridWebView Test</h1>
<br />
Enter name: <input type="text" id="name">
<br />
<br />
<button type="button" onclick="javascript: invokeCSCode($('#name').val());">Invoke C# Code</button>
<br />
<p id="result">Result:</p>
<script type="text/javascript">function log(str) {
$('#result').text($('#result').text() + " " + str);
}
function invokeCSCode(data) {
try {
log("Sending Data:" + data);
invokeCSharpAction(data);
}
catch (err) {
log(err);
}
}</script>
</body>
</html>
Webová stránka umožňuje uživateli zadat jeho jméno do input
elementu a poskytuje button
prvek, který při kliknutí vyvolá kód jazyka C#. Proces, jak toho dosáhnout, je následující:
- Když uživatel klikne na
button
prvek,invokeCSCode
volá se javascriptová funkce s hodnotouinput
prvku, který se předá funkci. - Funkce
invokeCSCode
volálog
funkci, aby zobrazila data, která odesílá do jazyka C#Action
. Potom volá metoduinvokeCSharpAction
k vyvolání jazyka C#Action
a předání parametru přijatého z elementuinput
.
Funkce JavaScriptu není definována invokeCSharpAction
na webové stránce a bude do ní vložena jednotlivými vlastními renderery.
V iOSu se tento soubor HTML nachází ve složce Obsah projektu platformy s akcí sestavení BundleResource. V Androidu se tento soubor HTML nachází ve složce Assets/Content projektu platformy s akcí sestavení AndroidAsset.
Vyvolání jazyka C# z JavaScriptu
Proces vyvolání jazyka C# z JavaScriptu je stejný na každé platformě:
- Vlastní renderer vytvoří nativní webový ovládací prvek a načte soubor HTML určený
HybridWebView.Uri
vlastností. - Po načtení webové stránky vlastní renderer vloží
invokeCSharpAction
funkci JavaScriptu na webovou stránku. - Když uživatel zadá své jméno a klikne na element HTML
button
,invokeCSCode
vyvolá se funkce, která zase vyvoláinvokeCSharpAction
funkci. - Funkce
invokeCSharpAction
vyvolá metodu ve vlastním rendereru, která následně vyvolá metoduHybridWebView.InvokeAction
. - Metoda
HybridWebView.InvokeAction
vyvolá zaregistrovanýAction
.
V následujících částech se dozvíte, jak se tento proces implementuje na jednotlivých platformách.
Vytvoření vlastního rendereru v iOSu
Následující příklad kódu ukazuje vlastní renderer pro platformu iOS:
[assembly: ExportRenderer(typeof(HybridWebView), typeof(HybridWebViewRenderer))]
namespace CustomRenderer.iOS
{
public class HybridWebViewRenderer : WkWebViewRenderer, IWKScriptMessageHandler
{
const string JavaScriptFunction = "function invokeCSharpAction(data){window.webkit.messageHandlers.invokeAction.postMessage(data);}";
WKUserContentController userController;
public HybridWebViewRenderer() : this(new WKWebViewConfiguration())
{
}
public HybridWebViewRenderer(WKWebViewConfiguration config) : base(config)
{
userController = config.UserContentController;
var script = new WKUserScript(new NSString(JavaScriptFunction), WKUserScriptInjectionTime.AtDocumentEnd, false);
userController.AddUserScript(script);
userController.AddScriptMessageHandler(this, "invokeAction");
}
protected override void OnElementChanged(VisualElementChangedEventArgs e)
{
base.OnElementChanged(e);
if (e.OldElement != null)
{
userController.RemoveAllUserScripts();
userController.RemoveScriptMessageHandler("invokeAction");
HybridWebView hybridWebView = e.OldElement as HybridWebView;
hybridWebView.Cleanup();
}
if (e.NewElement != null)
{
string filename = Path.Combine(NSBundle.MainBundle.BundlePath, $"Content/{((HybridWebView)Element).Uri}");
LoadRequest(new NSUrlRequest(new NSUrl(filename, false)));
}
}
public void DidReceiveScriptMessage(WKUserContentController userContentController, WKScriptMessage message)
{
((HybridWebView)Element).InvokeAction(message.Body.ToString());
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
((HybridWebView)Element).Cleanup();
}
base.Dispose(disposing);
}
}
}
Třída HybridWebViewRenderer
načte webovou stránku zadanou ve HybridWebView.Uri
vlastnosti do nativního WKWebView
ovládacího prvku a invokeCSharpAction
funkce JavaScriptu se vloží na webovou stránku. Jakmile uživatel zadá své jméno a klikne na element HTML button
, invokeCSharpAction
spustí se funkce JavaScriptu s DidReceiveScriptMessage
metodou, která se volá po přijetí zprávy z webové stránky. Tato metoda zase vyvolá metodu HybridWebView.InvokeAction
, která vyvolá zaregistrovanou akci, která zobrazí automaticky otevírané okno.
Tato funkce se dosahuje následujícím způsobem:
- Konstruktor rendereru
WkWebViewConfiguration
vytvoří objekt a načte jehoWKUserContentController
objekt. ObjektWkUserContentController
umožňuje publikovat zprávy a vkládat uživatelské skripty na webovou stránku. - Konstruktor rendereru
WKUserScript
vytvoří objekt, který vložíinvokeCSharpAction
funkci JavaScriptu na webovou stránku po načtení webové stránky. - Konstruktor renderer volá metodu
WKUserContentController.AddUserScript
pro přidáníWKUserScript
objektu do kontroleru obsahu. - Konstruktor renderer volá metodu
WKUserContentController.AddScriptMessageHandler
pro přidání obslužné rutiny zprávy skriptu pojmenovanéinvokeAction
do objektuWKUserContentController
, což způsobí, že javascriptová funkcewindow.webkit.messageHandlers.invokeAction.postMessage(data)
bude definována ve všech rámcích ve všechWebView
instancích, které objekt používajíWKUserContentController
. - Za předpokladu, že je vlastní renderer připojený k novému Xamarin.Forms prvku:
- Metoda
WKWebView.LoadRequest
načte soubor HTML určenýHybridWebView.Uri
vlastností. Kód určuje, že soubor je uložen veContent
složce projektu. Po zobrazeníinvokeCSharpAction
webové stránky se funkce JavaScriptu vloží na webovou stránku.
- Metoda
- Prostředky jsou uvolněny, když je prvek renderer připojen ke změnám.
- Prvek Xamarin.Forms se vyčistí při uvolnění rendereru.
Poznámka:
Třída WKWebView
je podporována pouze v iOSu 8 a novějším.
Kromě toho musí být soubor Info.plist aktualizován tak, aby obsahoval následující hodnoty:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
Vytvoření vlastního rendereru v Androidu
Následující příklad kódu ukazuje vlastní renderer pro platformu Android:
[assembly: ExportRenderer(typeof(HybridWebView), typeof(HybridWebViewRenderer))]
namespace CustomRenderer.Droid
{
public class HybridWebViewRenderer : WebViewRenderer
{
const string JavascriptFunction = "function invokeCSharpAction(data){jsBridge.invokeAction(data);}";
Context _context;
public HybridWebViewRenderer(Context context) : base(context)
{
_context = context;
}
protected override void OnElementChanged(ElementChangedEventArgs<WebView> e)
{
base.OnElementChanged(e);
if (e.OldElement != null)
{
Control.RemoveJavascriptInterface("jsBridge");
((HybridWebView)Element).Cleanup();
}
if (e.NewElement != null)
{
Control.SetWebViewClient(new JavascriptWebViewClient(this, $"javascript: {JavascriptFunction}"));
Control.AddJavascriptInterface(new JSBridge(this), "jsBridge");
Control.LoadUrl($"file:///android_asset/Content/{((HybridWebView)Element).Uri}");
}
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
((HybridWebView)Element).Cleanup();
}
base.Dispose(disposing);
}
}
}
Třída HybridWebViewRenderer
načte webovou stránku zadanou ve HybridWebView.Uri
vlastnosti do nativního WebView
ovládacího prvku a invokeCSharpAction
funkce JavaScriptu se vloží na webovou stránku po dokončení načítání webové stránky s OnPageFinished
přepsání ve JavascriptWebViewClient
třídě:
public class JavascriptWebViewClient : FormsWebViewClient
{
string _javascript;
public JavascriptWebViewClient(HybridWebViewRenderer renderer, string javascript) : base(renderer)
{
_javascript = javascript;
}
public override void OnPageFinished(WebView view, string url)
{
base.OnPageFinished(view, url);
view.EvaluateJavascript(_javascript, null);
}
}
Jakmile uživatel zadá své jméno a klikne na element HTML button
, invokeCSharpAction
spustí se funkce JavaScriptu. Tato funkce se dosahuje následujícím způsobem:
- Za předpokladu, že je vlastní renderer připojený k novému Xamarin.Forms prvku:
- Metoda
SetWebViewClient
nastaví novýJavascriptWebViewClient
objekt jako implementaciWebViewClient
. - Metoda
WebView.AddJavascriptInterface
vloží novouJSBridge
instanci do hlavního rámce kontextu JavaScript WebView a pojme jejjsBridge
. To umožňuje přístup k metodám veJSBridge
třídě z JavaScriptu. - Metoda
WebView.LoadUrl
načte soubor HTML určenýHybridWebView.Uri
vlastností. Kód určuje, že soubor je uložen veContent
složce projektu. JavascriptWebViewClient
Ve tříděinvokeCSharpAction
se funkce JavaScriptu vloží na webovou stránku, jakmile se stránka dokončí načítání.
- Metoda
- Prostředky jsou uvolněny, když je prvek renderer připojen ke změnám.
- Prvek Xamarin.Forms se vyčistí při uvolnění rendereru.
invokeCSharpAction
Když je funkce JavaScriptu spuštěna, pak vyvolá metoduJSBridge.InvokeAction
, která je znázorněna v následujícím příkladu kódu:
public class JSBridge : Java.Lang.Object
{
readonly WeakReference<HybridWebViewRenderer> hybridWebViewRenderer;
public JSBridge(HybridWebViewRenderer hybridRenderer)
{
hybridWebViewRenderer = new WeakReference<HybridWebViewRenderer>(hybridRenderer);
}
[JavascriptInterface]
[Export("invokeAction")]
public void InvokeAction(string data)
{
HybridWebViewRenderer hybridRenderer;
if (hybridWebViewRenderer != null && hybridWebViewRenderer.TryGetTarget(out hybridRenderer))
{
((HybridWebView)hybridRenderer.Element).InvokeAction(data);
}
}
}
Třída musí být odvozena od Java.Lang.Object
a metody, které jsou vystaveny JavaScriptu, musí být zdobeny [JavascriptInterface]
a [Export]
atributy. Proto když je javascriptová invokeCSharpAction
funkce vložena na webovou stránku a je spuštěna, zavolá metodu JSBridge.InvokeAction
kvůli dekorování pomocí [JavascriptInterface]
a [Export("invokeAction")]
atributů. Metoda zase InvokeAction
vyvolá metodu HybridWebView.InvokeAction
, která vyvolá zaregistrovanou akci, která zobrazí automaticky otevírané okno.
Důležité
Projekty Androidu, které používají [Export]
atribut, musí obsahovat odkaz na Mono.Android.Export
, nebo chyba kompilátoru bude mít za následek.
Všimněte si, že třída JSBridge
udržuje WeakReference
třídu HybridWebViewRenderer
. Tím se vyhnete vytváření cyklických odkazů mezi těmito dvěma třídami. Další informace naleznete v tématu Slabé odkazy.
Vytvoření vlastního rendereru v UPW
Následující příklad kódu ukazuje vlastní renderer pro UPW:
[assembly: ExportRenderer(typeof(HybridWebView), typeof(HybridWebViewRenderer))]
namespace CustomRenderer.UWP
{
public class HybridWebViewRenderer : WebViewRenderer
{
const string JavaScriptFunction = "function invokeCSharpAction(data){window.external.notify(data);}";
protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.WebView> e)
{
base.OnElementChanged(e);
if (e.OldElement != null)
{
Control.NavigationCompleted -= OnWebViewNavigationCompleted;
Control.ScriptNotify -= OnWebViewScriptNotify;
}
if (e.NewElement != null)
{
Control.NavigationCompleted += OnWebViewNavigationCompleted;
Control.ScriptNotify += OnWebViewScriptNotify;
Control.Source = new Uri($"ms-appx-web:///Content//{((HybridWebView)Element).Uri}");
}
}
async void OnWebViewNavigationCompleted(Windows.UI.Xaml.Controls.WebView sender, WebViewNavigationCompletedEventArgs args)
{
if (args.IsSuccess)
{
// Inject JS script
await Control.InvokeScriptAsync("eval", new[] { JavaScriptFunction });
}
}
void OnWebViewScriptNotify(object sender, NotifyEventArgs e)
{
((HybridWebView)Element).InvokeAction(e.Value);
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
((HybridWebView)Element).Cleanup();
}
base.Dispose(disposing);
}
}
}
Třída HybridWebViewRenderer
načte webovou stránku zadanou ve HybridWebView.Uri
vlastnosti do nativního WebView
ovládacího prvku a invokeCSharpAction
funkce JavaScriptu se vloží na webovou stránku po načtení webové stránky s metodou WebView.InvokeScriptAsync
. Jakmile uživatel zadá své jméno a klikne na element HTML button
, invokeCSharpAction
spustí se funkce JavaScriptu s OnWebViewScriptNotify
metodou, která se volá po přijetí oznámení z webové stránky. Tato metoda zase vyvolá metodu HybridWebView.InvokeAction
, která vyvolá zaregistrovanou akci, která zobrazí automaticky otevírané okno.
Tato funkce se dosahuje následujícím způsobem:
- Za předpokladu, že je vlastní renderer připojený k novému Xamarin.Forms prvku:
- Obslužné rutiny událostí a
NavigationCompleted
ScriptNotify
události jsou registrovány. UdálostNavigationCompleted
se aktivuje, když nativníWebView
ovládací prvek dokončil načítání aktuálního obsahu nebo pokud se navigace nezdařila. UdálostScriptNotify
se aktivuje, když obsah v nativnímWebView
ovládacím prvku používá JavaScript k předání řetězce do aplikace. Webová stránka aktivujeScriptNotify
událost volánímwindow.external.notify
při předávání parametrustring
. - Vlastnost
WebView.Source
je nastavena na identifikátor URI souboru HTML, který je určen vlastnostíHybridWebView.Uri
. Kód předpokládá, že soubor je uložený veContent
složce projektu. Po zobrazeníNavigationCompleted
webové stránky se událost aktivuje aOnWebViewNavigationCompleted
vyvolá se metoda. FunkceinvokeCSharpAction
JavaScriptu se pak vloží na webovou stránku s metodouWebView.InvokeScriptAsync
za předpokladu, že navigace byla úspěšně dokončena.
- Obslužné rutiny událostí a
- Událost se odhlásí, když je prvek, který renderer připojí ke změnám.
- Prvek Xamarin.Forms se vyčistí při uvolnění rendereru.