MediaPicker — когда я выбираю фотографию, она не отображается на странице, а только на Android

#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. Я более подробно рассмотрю навигацию и попытаюсь понять, где я допустил ошибку.