#c# #xamarin.forms #xamarin.essentials
#c# #xamarin.forms #xamarin.essentials
Вопрос:
Я пытаюсь загрузить изображение в приложение. Я следую документации MS https://docs.microsoft.com/en-us/xamarin/essentials/media-picker?tabs=android . В то время как UWP отлично работает (тестировался только с эмулятором) Я не могу заставить его работать на Android (также тестировался только с эмулятором). Приложение сохранит местоположение с фотографией. Я не знаю, имеет ли это значение, но первая страница — это карта, показывающая текущее местоположение, а вторая страница — страница с ошибкой, где я хочу сохранить местоположение и фотографию.
Когда я выбираю фотографию, она очищает страницу (на случай, если что-то написано в entry и editor, оба очищаются), и изображение не отображается. Даже ярлык с путем к фотографии ничего не возвращает. Это похоже на то, что он перезагружает страницу. Я реализовал возможность сделать снимок с тем же результатом.
В режиме отладки все свойства имеют значение, но когда я выбираю / снимаю значения свойств фотографий, они не отображаются в пользовательском интерфейсе.
Модель представления
using iVanApp.Model;
using iVanApp.Services;
using System;
using System.Collections.Generic;
using System.Text;
using Xamarin.Forms;
using Xamarin.Essentials;
using System.Threading;
using System.Linq;
using Xamarin.Forms.Maps;
using System.Threading.Tasks;
using System.IO;
namespace iVanApp.ViewModels
{
class AddNightViewModel : BaseViewModel
{
public AddNightViewModel(INavService navService)
: base(navService)
{
}
CancellationTokenSource cts;
string comment;
public string Comment
{
get => comment;
set
{
comment = value;
OnPropertyChanged();
}
}
string nameNight;
public string NameNight
{
get => nameNight;
set
{
nameNight = value;
OnPropertyChanged();
}
}
private async Task<string> GetAddressName(Location location)
{
Geocoder geoCoder = new Geocoder();
// Position position = new Position(latitude.Value, longitude.Value);
Position position = new Position(location.Latitude, location.Longitude);
string address = String.Empty;
try
{
IEnumerable<string> possibleAddress = await geoCoder.GetAddressesForPositionAsync(position);
address = possibleAddress.FirstOrDefault();
await Application.Current.MainPage.DisplayAlert("Succes!", "Saved. Address is " address, "OK");
}
catch (Exception ex)
{
await Application.Current.MainPage.DisplayAlert("Error!", "Something went wrongnError:n" ex.Message, "OK");
}
return address;
}
public Command SaveLocation
{
get
{
return new Command(async () =>
{
var location = await Geolocation.GetLastKnownLocationAsync();
if (location == null)
{
var request = new GeolocationRequest(GeolocationAccuracy.Medium, TimeSpan.FromSeconds(10));
cts = new CancellationTokenSource();
location = await Geolocation.GetLocationAsync(request, cts.Token);
}
string addressName = string.Empty;
if (location != null)
{
addressName = await GetAddressName(location);
}
Night nightData = new Night
{
NightDate = DateTime.Now.Date,
NightLatitude = location.Latitude,
NightLongitute = location.Longitude,
NightName = NameNight,
Address = addressName,
Comments = Comment,
NightImage = PhotoPath
};
await App.Database.SaveNightAsync(nightData);
});
}
}
string photoPath;
public string PhotoPath
{
get => photoPath;
set
{
photoPath = value;
OnPropertyChanged();
}
}
public Command SelectPhoto
{
get
{
return new Command(async () =>
{
try
{
var photo = await MediaPicker.PickPhotoAsync();
await LoadPhotoAsync(photo);
Console.WriteLine($"CapturePhotoAsync COMPLETED: {PhotoPath}");
await Application.Current.MainPage.DisplayAlert("Done",
$"CapturePhotoAsync COMPLETED: {PhotoPath}",
"OK");
}
catch (Exception ex)
{
Console.WriteLine($"CapturePhotoAsync THREW: {ex.Message}");
await Application.Current.MainPage.DisplayAlert("ERROR",
$"CapturePhotoAsync THREW: {ex.Message}",
"OK");
}
});
}
}
public Command TakePhoto
{
get
{
return new Command(async () =>
{
try
{
var photo = await MediaPicker.CapturePhotoAsync();
await LoadPhotoAsync(photo);
//Console.WriteLine($"CapturePhotoAsync COMPLETED: {PhotoPath}");
await Application.Current.MainPage.DisplayAlert("Done",
$"CapturePhotoAsync COMPLETED: {PhotoPath}",
"OK");
}
catch (Exception ex)
{
//Console.WriteLine($"CapturePhotoAsync THREW: {ex.Message}");
await Application.Current.MainPage.DisplayAlert("Error",
$"CapturePhotoAsync THREW: {ex.Message}",
"OK");
}
});
}
}
async Task LoadPhotoAsync(FileResult photo)
{
// canceled
if (photo == null)
{
PhotoPath = null;
return;
}
// save the file into local storage
var newFile = Path.Combine(FileSystem.CacheDirectory, photo.FileName);
using (var stream = await photo.OpenReadAsync())
using (var newStream = File.OpenWrite(newFile))
await stream.CopyToAsync(newStream);
PhotoPath = newFile;
}
}
}
Вид
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="iVanApp.Views.AddNightView">
<ContentPage.Content>
<StackLayout>
<Entry Text="{Binding NameNight}"/>
<Editor Placeholder="Comments"
Text="{Binding Comment}"/>
<Button Text="Choose photo"
Command="{Binding SelectPhoto}"
/>
<Button Text="Take photo"
Command="{Binding TakePhoto}"
/>
<Button Text="Save location"
Command="{Binding SaveLocation}"/>
<Label Text="{Binding PhotoPath}"/>
<Image Source="{Binding PhotoPath}"
HeightRequest="150"
Aspect="AspectFit"/>
</StackLayout>
</ContentPage.Content>
</ContentPage>
Права Андоида- AssemblyInfo
// Needed for Picking photo/video
[assembly: UsesPermission(Android.Manifest.Permission.ReadExternalStorage)]
// Needed for Taking photo/video
[assembly: UsesPermission(Android.Manifest.Permission.WriteExternalStorage)]
[assembly: UsesPermission(Android.Manifest.Permission.Camera)]
And here is a log — when I click button to select picture and when I select picture.
02-25 01:10:00.293 W/ActivityThread(28782): handleWindowVisibility: no activity for token android.os.BinderProxy@f3053de
02-25 01:10:00.299 E/SchedPolicy(28782): set_timerslack_ns write failed: Operation not permitted
02-25 01:10:00.440 D/EGL_emulation(28782): eglMakeCurrent: 0xc7cd21e0: ver 3 0 (tinfo 0xc7cfc0d0)
02-25 01:10:00.499 I/chatty (28782): uid=10092(com.companyname.ivanapp) RenderThread identical 2 lines
02-25 01:10:00.524 D/EGL_emulation(28782): eglMakeCurrent: 0xc7cd21e0: ver 3 0 (tinfo 0xc7cfc0d0)
02-25 01:10:01.270 D/EGL_emulation(28782): eglMakeCurrent: 0xc7cd21e0: ver 3 0 (tinfo 0xc7cfc0d0)
CapturePhotoAsync COMPLETED: /data/user/0/com.companyname.ivanapp/cache/IMG_20210224_063027.jpg
02-25 01:10:02.231 I/mono-stdout(28782): CapturePhotoAsync COMPLETED: /data/user/0/com.companyname.ivanapp/cache/IMG_20210224_063027.jpg
02-25 01:10:02.312 W/StaticLayout(28782): maxLineHeight should not be -1. maxLines:2 lineCount:2
02-25 01:10:02.320 I/chatty (28782): uid=10092(com.companyname.ivanapp) identical 5 lines
02-25 01:10:02.320 W/StaticLayout(28782): maxLineHeight should not be -1. maxLines:2 lineCount:2
02-25 01:10:02.499 D/EGL_emulation(28782): eglMakeCurrent: 0xc7cd21e0: ver 3 0 (tinfo 0xc7cfc0d0)
02-25 01:10:02.708 D/EGL_emulation(28782): eglMakeCurrent: 0xc7cd21e0: ver 3 0 (tinfo 0xc7cfc0d0)
02-25 01:10:04.406 D/EGL_emulation(28782): eglMakeCurrent: 0xc7cd21e0: ver 3 0 (tinfo 0xc7cfc0d0)
02-25 01:10:04.408 D/OpenGLRenderer(28782): endAllActiveAnimators on 0xc12e6400 (RippleDrawable) with handle 0xc12a9d20
02-25 01:10:04.418 E/SchedPolicy(28782): set_timerslack_ns write failed: Operation not permitted
02-25 01:10:04.440 D/EGL_emulation(28782): eglMakeCurrent: 0xc7cd21e0: ver 3 0 (tinfo 0xc7cfc0d0)
Any help/insight would be much appreciated.
UPDATE
I tried to found an issue and build an testing app from scratch and testing when does MediaPicker stop working. This happens when I implement navigation. I did navigation based on book Mastering Xamarin.Forms (only up to part when it start using Framework. So I am navigation without framework).
My navigation is following
INavService
public interface INavService
{
// Navigatze to another page without passing any parameter
Task NavigateTo<TVM>()
where TVM : BaseViewModel;
}
XamarinFormsNavService
[assembly: Dependency(typeof(XamarinFormsNavService))]
namespace TestMediaPicker_v2.Services
{
public class XamarinFormsNavService : INavService
{
readonly IDictionary<Type, Type> _map = new Dictionary<Type, Type>();
// Creates dictionary of all ViewModels and coresponding Views
public void RegistryViewMapping(Type viewModel, Type view)
{
_map.Add(viewModel, view);
}
public INavigation XamarinFormsNav { get; set; }
// Navigation task for each VIewModel and coresponding View
public async Task NavigateTo<TVM>() where TVM : BaseViewModel
{
await NavigateToView(typeof(TVM));
if (XamarinFormsNav.NavigationStack.Last().BindingContext is BaseViewModel)
{
((BaseViewModel)XamarinFormsNav.NavigationStack.Last().BindingContext).Init();
}
}
async Task NavigateToView(Type ViewModelType)
{
if (!_map.TryGetValue(ViewModelType, out Type viewType))
{
throw new ArgumentException("No view found in view mapping for " ViewModelType.FullName ".");
}
// Use reflection to get the View's constructor and create an instance of the View
var constructor = viewType.GetTypeInfo()
.DeclaredConstructors
.FirstOrDefault(dc => !dc.GetParameters().Any());
var view = constructor.Invoke(null) as Page;
await XamarinFormsNav.PushAsync(view, true);
}
}
}
Код, лежащий в основе моего представления
protected override async void OnAppearing()
{
var viewModel = new AddNightViewModel(DependencyService.Get<INavService>());
BindingContext = viewModel;
}
Словарь представлений и моделей представления в app.xaml.cs
public App()
{
InitializeComponent();
var mainPage = new NavigationPage(new AddNightView());
var navService = DependencyService.Get<INavService>() as XamarinFormsNavService;
navService.XamarinFormsNav = mainPage.Navigation;
navService.RegistryViewMapping(typeof(AddNightViewModel), typeof(AddNightView));
navService.RegistryViewMapping(typeof(AllNightsViewModel), typeof(AllNightsView));
MainPage = mainPage;
}
Комментарии:
1. Я протестировал его с вашим кодом выше, и он работал нормально. Какая у вас версия эмулятора Android?
2. Спасибо за ответ. Я также попробовал с новым проектом, и он работает. Так что я думаю, что это не из-за версии эмулятора. Также развернут оригинальный проект на 2 телефонах Android, и он не работает (приложение вылетает). После того, как я добавил другие вещи в новый проект, он перестал работать. Я добавил MVVM и SQLite. Теперь я буду добавлять вещь за вещью и тестировать, чтобы я мог видеть, что вызывает эту ошибку. Или у вас есть какая-нибудь идея получше, как подойти к этому вопросу?
3. После гораздо более тщательных исследований я обнаружил, что это происходит, когда я добавляю навигационный сервис. Обратите внимание, что я не использую никаких фреймворков — навигация выполняется на основе книги Mastering Xamarin Forms. Я более подробно рассмотрю навигацию и попытаюсь понять, где я допустил ошибку.