Xamarin Android - 如何在表单页面上将事件从MainActivity传递给ViewModel?

问题描述 投票:0回答:3

我有一个Xamarin Forms应用程序,我想阅读NFC标签。我已经创建了一个名为INFC的接口来读取标签。

/// <summary>
/// This interface defines NFC relating functions that are cross platform.
/// </summary>
public interface INFC
{
    /// <summary>
    /// Inits the object.
    /// </summary>
    void Init();

    /// <summary>
    /// Starts the process for scanning for the included platform.
    /// </summary>       
    /// <param name="tagInformation">Optional related tag information that you may need for the scan.</param>
    void StartNFCScan(object tagInformation = null);

    /// <summary>
    /// Called when the tag is finished scanning and we have the content.
    /// </summary>
    event EventHandler<String> TagScanned;
}

我创建了以下Android特定实现。

[assembly: Dependency(typeof(INFCImplementation))]
namespace Test.Droid.Models
{
/// <summary>
/// The android implementation of the NFC platform.
/// </summary>
public class INFCImplementation : INFC
{        
    public event EventHandler<String> TagScanned;
    public static NfcAdapter adapter { get; set; }

    /// <summary>
    /// Called to init the object.
    /// </summary>        
    public void Init()
    {
        //Set the adapter.
        adapter = NfcAdapter.GetDefaultAdapter(Forms.Context);
    }

    /// <summary>
    /// Starts the process for scanning for the included platform.
    /// </summary>       
    /// <param name="tagInformation">Optional related tag information that you may need for the scan.</param>
    public void StartNFCScan(object tagInformation = null)
    {
        //Create a variable to hold the tag content.
        String tagContent = null;

        try
        {                
            //Process the NDEF tag and get the content as a String.
            tagContent = "http://stackoverflow.com";
        }
        catch (Exception e)
        {
        }

        //Raise the tag content with the scanned event.
        TagScanned?.Invoke(this, tagContent);
    }
}

}

我的主要活动如下。

/// <summary>
/// The main activity for the app.
/// </summary>
[Activity(Label = "Test", Icon = "@drawable/icon", Theme = "@style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
    INFCImplementation nfcImplementation;        

    protected override void OnCreate(Bundle bundle)
    {
        TabLayoutResource = Resource.Layout.Tabbar;
        ToolbarResource = Resource.Layout.Toolbar;

        base.OnCreate(bundle);

        //Enable experimental fast renderers.
        Forms.SetFlags("FastRenderers_Experimental");

        Forms.Init(this, bundle);

        //Load up the zxing framework.
        ZXing.Net.Mobile.Forms.Android.Platform.Init();

        //Load up the user dialogs plugin.
        UserDialogs.Init(() => (Activity)Forms.Context);

        //Init the tinted image renderer.
        TintedImageRenderer.Init();            

        //Store our NFC interface class.
        nfcImplementation = DependencyService.Get<INFCImplementation>() as INFCImplementation;

        //Init our NFC interface.
        nfcImplementation.Init();            

        LoadApplication(new App());
    }

    protected override void OnResume()
    {
        //Call the base method.
        base.OnResume();

        //Create the intent for NFC reading.
        Intent intent = new Intent(this, GetType()).AddFlags(ActivityFlags.SingleTop);

        //Start a dispatch on our NFC adapter.
        INFCImplementation.adapter?.EnableForegroundDispatch
        (
            this,
            PendingIntent.GetActivity(this, 0, intent, 0),
            new[] { new IntentFilter(NfcAdapter.ActionTechDiscovered) },
            new String[][]
            {
                new string[]
                {
                    "android.nfc.tech.Ndef"
                },
                new string[] {
                        "android.nfc.tech.MifareClassic"
                    },
            }
        );
    }

    protected override void OnPause()
    {
        //Call the base method.
        base.OnPause();

        //Stop the dispatch on our NFC adapter.
        INFCImplementation.adapter?.DisableForegroundDispatch(this);
    }

    protected override void OnNewIntent(Intent intent)
    {
        //Call the base method.
        base.OnNewIntent(intent);

        //Check if this is the NFC intent.
        if (intent != null && (NfcAdapter.ActionNdefDiscovered.Equals(intent.Action) || NfcAdapter.ActionTechDiscovered.Equals(intent.Action) || NfcAdapter.ActionTagDiscovered.Equals(intent.Action)))
        {
            var test = intent.GetParcelableExtra(NfcAdapter.ExtraTag) as Tag;
            nfcImplementation.StartNFCScan(test);
        }
    }

    public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Permission[] grantResults)
    {
        //Call the base method.
        base.OnRequestPermissionsResult(requestCode, permissions, grantResults);

        //Check with the permissions plugin.
        PermissionsImplementation.Current.OnRequestPermissionsResult(requestCode, permissions, grantResults);

        //Check with the zxing plugin.
        ZXing.Net.Mobile.Android.PermissionsHandler.OnRequestPermissionsResult(requestCode, permissions, grantResults);
    }
}

在我的主页的视图模型中,它是主页面的绑定上下文,我在构造函数中添加以下内容。

    /// <summary>
    /// Constructs the scanner view model with the scanner view we want to use.
    /// </summary>
    public ScannerPageViewModel()
    {
        //Subscribe to the tag scanned event.
        CrossNFC.Current.TagScanned += ProcessNFCScanResult;
    }

    private void ProcessNFCScanResult(object sender, string e)
    {
        SetLabel(e);
    }

好的,对于这个问题。我相信这应该是这样的,所以OnNewIntent函数将调用接口上的启动NFC扫描,然后这将调用将一直飞到视图模型并允许我处理内容的事件。我这样做是因为我只想在应用程序位于前台时在应用程序的一个页面上扫描NFC标签。每次进入invoke调用时,TagScanned事件都为null。

放置断点我发现扫描标签时会发生以下情况:

MainActivity OnPause获取调用 - >扫描程序页面OnDisappearing获取调用 - > OnNewIntent获取调用并调用null事件 - > MainActivity OnResume被调用 - >扫描程序页面OnAppearing获取调用

我相信OnDisappearing调用正在使事件无法处理。我在NFCForms Github项目(https://github.com/poz1/NFCForms)上编写了很多代码,在下载示例项目并运行它时,不会触发OnDisappearing和OnAppearing。它只调用OnPause,OnNewIntent和OnResume,事件进入他的页面。

为什么我的页面被卸载并且事件没有被调用?如果我做错了什么,在扫描标签时如何通知我的ViewModel特定页面?我认为这是我对NFC的意图请求的方式或与NFC无关的问题,因为示例NFCForms应用程序在同一部手机上正常工作,我正在处理视图事件错误。

编辑

我使用相同的基本代码创建了一个全新的项目,它按照我认为应该具有的方式工作。现在我想弄清楚为什么在页面上调用OnAppearing和OnDisappearing。

编辑2

我发现,如果页面包含在导航页面中,则会调用OnAppearing和OnDisappearing。这就是为什么单个视图的新项目没有调用它,当我添加它确实调用它的导航页面。

但是,即使将我的项目更改为单个页面,我正在处理的旧项目仍将事件视为null,而新测试项目将事件视为有效。

所以我想我不知道我做的事情不对吗?

c# android xamarin xamarin.forms xamarin.android
3个回答
3
投票

这会对你的情况有所帮助吗?

// In ScannerPage
protected override void OnAppearing ()
{
    base.OnAppearing ();
    MessagingCenter.Subscribe<string>(this, "eventName", (label) => {
        // do something whenever the message is sent
        Device.BeginInvokeOnMainThread (() => {
           MyScannerPageViewModel.SetLabel(label);
        });
    });
}

protected override void OnDisappearing ()
{
    base.OnDisappearing ();
    MessagingCenter.Unsubscribe<string> (this, "eventName");
}

在qazxsw poi中选择您想要放置此线的位置

MainActivity

编辑:改变了一点代码


1
投票

最后一个成功的帖子是2018年1月。任何人都有这个工作在2019年?订阅者从未听过我尝试匿名MessagingCenter消息的每个测试。

与原始海报相同的用例:从Android的MainActivty发送消息,在共享/不可知层的ViewModel中监听它。

在我的测试中,甚至在同一个类或同一层中都没有听到匿名消息。

更新:来自社区的进一步输入找到了解决方案:您必须指定类型Xamarin.Forms.MessagingCenter.Send("LabelName","eventName"); ApplicationApplication.Current

因此,以前工作的语法在2019年似乎已经破解 - 有一种解决方法。

Android层(MainActivity或广播监听器):https://forums.xamarin.com/discussion/comment/370364#Comment_370364共享层视图模型MessagingCenter.Send(Xamarin.Forms.Application.Current,"ISENGINEON", result.ToString());


0
投票

虽然这样的答案让我非常难过,但它确实最终起作用了。

自从我发现新项目工作以来,我创建了一个与我同名的新表单项目,然后从我的解决方案中删除了Android项目并将其替换为新项目。然后我重新安装了所有的nuget包,并将我的所有代码复制并粘贴到新项目中。

它现在有效.....

所以我猜测在VS项目的核心部分还有什么东西破坏了。我讨厌有点波浪般的答案,但这对我有用。我上面发布的所有代码都没有改变,它开始工作。

© www.soinside.com 2019 - 2024. All rights reserved.