【发布时间】:2018-06-11 08:39:32
【问题描述】:
我有一个想要读取 NFC 标签的 Xamarin Forms 应用程序。我制作了一个名为 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 标签。每次调用调用时,TagScanned 事件为空。
在周围放置断点我发现扫描一个标签时会出现以下情况:
调用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,而新的测试项目将事件设为有效。
所以我在想,不知怎么的,我做的事情不对?
【问题讨论】:
-
您是否考虑过使用
MessagingCenter class。另外你为什么在这里编码而不是 Pcl,它只适用于 Android 这个特定事件? -
NFC 的读取是特定于 Android 的,因此我必须将事件结果从 Android 项目传递到我的表单 PCL。我对消息中心类不熟悉。
-
MessagingCenter允许您将来自平台特定项目的消息发送到共享代码。我相信它可以满足您的需求 -
由于我不知道你想在哪里触发
Event和ViewModel听它,我无法提供代码示例。或者至少我在你的代码中没有发现它很明显 -
我用事件函数更新了视图模型代码。它目前所做的只是更新标签。阅读时我的印象是消息中心不如 c# 事件好,因此要避免它。事件方法在我发布的代码的全新表单项目中对我有用。我已经几个月的旧项目不会触发 Android 项目的事件,但如果我从 PCL 调用它,它会触发事件。
标签: c# android xamarin xamarin.forms xamarin.android