WebView 에서 JavaScript 실행은 간단히 아래와 같히 펑션을 호출하면 된다.

this.webView.Eval("TestFunction()");

 

하지만 리턴이 있는 JavaSecript funtion 실행결과는 저 형태로는 안된다.

따로 Renderer 를 이용해 구현하는 방법을 소개한다.

 

먼저 .Net Standard 프로젝트에 아래와 같이 WebViewer 를 만든다.

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;

namespace Test
{
    public class WebViewer : WebView
    {
        public static BindableProperty EvaluateJavascriptProperty =
        BindableProperty.Create(nameof(EvaluateJavascript), typeof(Func<string, Task<string>>), typeof(WebViewer), null, BindingMode.OneWayToSource);

        public Func<string, Task<string>> EvaluateJavascript
        {
            get { return (Func<string, Task<string>>)GetValue(EvaluateJavascriptProperty); }
            set { SetValue(EvaluateJavascriptProperty, value); }
        }
    }
}

이제 위 항목을 상속받아서 각 기기별로 Renderer 를 생성하자.

 

Android

using System;
using System.Threading;
using System.Threading.Tasks;
using Android.Content;
using Android.Webkit;
using Test;
using Test.Droid;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;

[assembly: ExportRenderer(typeof(WebViewer), typeof(WebViewRender))]
namespace Test.Droid
{
    public class WebViewRender : WebViewRenderer
    {
        public WebViewRender(Context context) : base(context)
        { }

        protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.WebView> e)
        {
            base.OnElementChanged(e);

            if (e.NewElement is WebViewer webView)
            {
                webView.EvaluateJavascript = async (js) =>
                {
                    var reset = new ManualResetEvent(false);
                    var response = string.Empty;
                    Device.BeginInvokeOnMainThread(() =>
                    {
                        Control?.EvaluateJavascript(js, new JavascriptCallback((r) => { response = r; reset.Set(); }));
                    });
                    await Task.Run(() => { reset.WaitOne(); });
                    return response;
                };
            }
        }
    }

    internal class JavascriptCallback : Java.Lang.Object, IValueCallback
    {
        public JavascriptCallback(Action<string> callback)
        {
            _callback = callback;
        }

        private Action<string> _callback;
        public void OnReceiveValue(Java.Lang.Object value)
        {
            _callback?.Invoke(Convert.ToString(value));
        }
    }
}

 

iOS

using System.Threading.Tasks;
using Test;
using Test.iOS;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;

[assembly: ExportRenderer(typeof(WebViewer), typeof(WebViewRender))]
namespace Test.iOS
{
    public class WebViewRender : WebViewRenderer
    {
        protected override void OnElementChanged(VisualElementChangedEventArgs e)
        {
            base.OnElementChanged(e);

            if (e.NewElement is WebViewer webView)
            {
                webView.EvaluateJavascript = (js) =>
                {
                    return Task.FromResult(this.EvaluateJavascript(js));
                };
            }
        }
    }
}

 

UWP

using System;
using Test;
using Test.UWP;
using Xamarin.Forms;
using Xamarin.Forms.Platform.UWP;

[assembly: ExportRenderer(typeof(WebViewer), typeof(WebViewRender))]
namespace Test.UWP
{
    public class WebViewRender : WebViewRenderer
    {
        protected override void OnElementChanged(ElementChangedEventArgs<WebView> e)
        {
            base.OnElementChanged(e);
            if (e.NewElement is WebViewer webView)
            {
                webView.EvaluateJavascript = async (js) =>
                {
                    return await Control.InvokeScriptAsync("eval", new[] { js });
                };
            }
        }
    }
}

 

이제 화면에서 아래와 같이 컨트롤을 넣고

<test:WebViewer x:Name="webView" HorizontalOptions="FillAndExpand"  VerticalOptions="FillAndExpand"/>

아래와 같이 코딩하면 function 의 결과 값을 받아올수 있다.

var result = await this.webView.EvaluateJavascript("GetTest();");

 

WebView 를 사용해 보니 cache 때문에 반영된 최신 정보를 못가져오는 경우가 있다

아래 코드를 이용하면 항상 cache 를 clear 하고 가져오므로 이런 현상을 막을수 있다.

 

PCL

namespace Project.Controls
{
    public class NoCachedWebView : WebView
    {
    }
}

 

Android

[assembly: ExportRenderer(typeof(NoCachedWebView), typeof(NoCachedWebViewRenderer))]
namespace Project.Droid.Renderer
{
    class NoCachedWebViewRenderer : WebViewRenderer
    {
        protected override void OnElementChanged(ElementChangedEventArgs<WebView> e)
        {
            base.OnElementChanged(e);
            if (Control == null) return;

            Control.ClearCache(true);
            Control.Settings.SetAppCacheEnabled(false);
            Control.Settings.CacheMode = Android.Webkit.CacheModes.NoCache;
        }
    }
}

 

iOS

[assembly: ExportRenderer(typeof(NoCachedWebView), typeof(NoCachedWebViewRenderer))]
namespace Project.iOS.Renderer
{
    class NoCachedWebViewRenderer : WebViewRenderer
    {
        protected override void OnElementChanged(VisualElementChangedEventArgs e)
        {
            base.OnElementChanged(e);
            if (Element == null) return;

            NSUrlCache.SharedCache.RemoveAllCachedResponses();
            NSUrlCache.SharedCache.MemoryCapacity = 0;
            NSUrlCache.SharedCache.DiskCapacity = 0;
        }
    }
}

이번시간에는 Picker 와 WebView 컨트롤을 알아보겠습니다.

(추가로 DisplayActionSheet 메서드 사용법도 알아볼께요)

 

Picker 는 윈폼으로 말하면 ComboBox 와 같은 형태로 여러 항목중에 하나를 선택할수 있는 컨트롤입니다.

https://developer.xamarin.com/api/type/Xamarin.Forms.Picker/

 

WebView 는 컨트롤 명에서도 유추할수 있듯이 웹페이지를 보는 컨트롤입니다.

WebView 에 url 을 넣어도 되지만 직접 html 코딩을 넣을수도 있습니다.

반드시 Android 에서 실행할때는 INTERNET 권한을 줘야합니다.

https://developer.xamarin.com/guides/xamarin-forms/user-interface/webview/

 

위 두 컨트롤의 예시 입니다.

WebPickerPage.xaml

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XamarinFormsStudy.WebPickerPage">
    <ContentPage.Content>
        <StackLayout>
            <Label Text="Picker"/>
            <Picker x:Name="picker" SelectedIndexChanged="picker_SelectedIndexChanged"/>
            <Button x:Name="button" Text="DisplayActionSheet" Clicked="button_Clicked"/>
            <Label Text="WebView"/>
            <WebView x:Name="webView" VerticalOptions="FillAndExpand"/>
        </StackLayout>
    </ContentPage.Content>
</ContentPage>

WebPickerPage.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace XamarinFormsStudy
{
 [XamlCompilation(XamlCompilationOptions.Compile)]
 public partial class WebPickerPage : ContentPage
 {
  public WebPickerPage()
  {
   InitializeComponent ();

            this.Padding = new Thickness(10, Device.OnPlatform(40, 20, 20), 10, 5);

            // Picker 에 데이터를 Add 합니다.
            this.picker.Items.Add("Easy");
            this.picker.Items.Add("Normal");
            this.picker.Items.Add("Hard");


            this.webView.Source = new UrlWebViewSource { Url = "http://m.naver.com" };
        }

        private void picker_SelectedIndexChanged(object sender, EventArgs e)
        {
            // 선택한 항목을 팝업으로 띄워줍니다.
            string selectData = this.picker.Items[this.picker.SelectedIndex];
            DisplayAlert(selectData, "SelectValue", "OK");
        }

        private async void button_Clicked(object sender, EventArgs e)
        {
            // 선택 팝업이 뜨고 선택한 항목에 따라 버튼 색을 바꿔줍니다.
            string color = await DisplayActionSheet("선택하세요", "취소", "닫기", "BLUE", "YELLOW", "RED", "GREEN");
            switch (color)
            {
                case "BLUE": this.button.BackgroundColor = Color.Blue; break;
                case "YELLOW": this.button.BackgroundColor = Color.Yellow; break;
                case "RED": this.button.BackgroundColor = Color.Red; break;
                case "GREEN": this.button.BackgroundColor = Color.Green; break;
            }
        }
    }
}

결과

Normal 을 선택한 경우

 

추가로 DisplayActionSheet 함수는 목록 데이터를 팝업으로 보여주고 선택할 수 있게 합니다.

선택에 따른 처리도 가능합니다.

* 아래처럼 선택 항목들이 나열되고 선택하게되면 버튼 색이 선택한 색으로 변경됩니다.

 

GitHub > https://github.com/kjundev/XamarinForms

+ Recent posts

티스토리 툴바