I'm developing a codebar app for android with Xamarin and the Zxing library. My objective was to have in the same view half of the screen with the codebar view, and the other half with the buttons to add or delete the scanned object to a list.
In MainActivity OnCreate function I have:
scanFragment = new ZXingScannerFragment();
FragmentTransaction fragmentTx = this.FragmentManager.BeginTransaction();
fragmentTx.Replace(Resource.Id.fragment, scanFragment);
fragmentTx.SetTransition(FragmentTransit.FragmentFade);
fragmentTx.Commit();
In ZXingScannerFragment OnCreate I have
frame = (FrameLayout)layoutInflater.Inflate(Resource.Layout.zxingscannerfragmentlayout, viewGroup, false);
return frame;
What I want it that, when the user scans something the view of the camera shut off, and then when the user has decided if wants to keep or discard the scanned object the camera shows again.
So I have a scann function called when the code is detected on MainActivity, who calls the OnPause method in the Zxing fragment and enables the buttons with this code:
var opts = new MobileBarcodeScanningOptions {
PossibleFormats = new List<ZXing.BarcodeFormat> {
ZXing.BarcodeFormat.All_1D,
}
};
scanFragment.StartScanning(result => {
if (result == null || string.IsNullOrEmpty(result.Text)) {
Toast.MakeText(this, "Scanning Cancelled", ToastLength.Long).Show();
return;
}
else
{
_player.Start();
RunOnUiThread(() => codBox.Text = result.Text);
RunOnUiThread(() => addBut.Enabled = true);
RunOnUiThread(() => delBut.Enabled = true);
RunOnUiThread(() => masBut.Enabled = true);
RunOnUiThread(() => menBut.Enabled = true);
RunOnUiThread(() => buttonDate.Enabled = true);
RunOnUiThread(() => finishBut.Enabled = false);
scanFragment.OnPause();
}
}, opts);
And then I have another function who calls the OnResume from Zxing fragment.
The OnPause function looks like:
base.OnPause ();
if (scanner != null)
{
frame.RemoveView(scanner);
if (!UseCustomOverlayView)
frame.RemoveView(zxingOverlay);
else if (CustomOverlayView != null)
frame.RemoveView(CustomOverlayView);
scanner.ShutdownCamera();
scanner = null;
}
The problem is: With this code the OnPause function gives a "Only the original thread that created a view hierarchy can touch its views" Exception, but if I ignore it all works well some random time. I can take a code, camera vanishes, then add or remove object, and call camera again and all works fine 5... 10... 15 times in a row until I get a "Unhandled Exception: Java.Lang.NullPointerException" with no Idea were is being fired and no more info.
If I do something to prevent the hierarchy exception like:
if (scanner != null)
{
var myActivity = (MainActivity)this.Activity;
myActivity.RunOnUiThread(() =>
{
frame.RemoveView(scanner);
if (!UseCustomOverlayView)
frame.RemoveView(zxingOverlay);
else if (CustomOverlayView != null)
frame.RemoveView(CustomOverlayView);
});
scanner.ShutdownCamera();
scanner = null;
}
The camera vanishes, no exception is throwed but when I call the OnResume I get a static image of the last code detected.
At the end after many attempts I realized I was using a unmanaged crash as a correct behaviour of my app.
What I do is instead of calling the OnResume and OnPause functions (who have clearly objectives) of the Zxingfragment call directly scan function and stopscanning function.