Xamarin.Forms UI/UX Challenges — Micuna Food — Часть3

В этой третьей части челленджа я выйду за рамки дизайна, я добавлю функциональность иконке share, используя две самые популярные библиотеки Xamarin.Forms — Xamarin Community Toolkit и Xamarin.Essentials.

Эта публикация является частью второго адвент-календаря Xamarin на испанском языке, организованного доктором Луисом Белтраном, и третьего адвент-календаря C# на испанском языке, организованного инж. Бенджамин Камачо, спасибо вам большое за участие в этих замечательных инициативах.


Скриншот и тост мультиплатформы

Ранее на странице FoodDetailPage была кнопка поделиться, так сделал дизайнер, но если мы подумаем, как пользователи, первое, что приходит на ум, это ¿Чем я собираюсь поделиться?, в связи с этой дилеммой, она начнется с кнопки скриншота, чтобы устранить ложное восприятие.

Углубившись в код, в файле Icons.xaml, расположенном в папке Styles, мы настроим значок скриншота с соответствующими свойствами, включая его размер и цвет.

<!--#region FontImageSource INTERFACES-->
<FontImageSource
    x:Key="icon_screenshot_solid"
    FontFamily="MonettelliFontIcons"
    Glyph="{x:Static FontAwesome:MonettelliFontIcons.icon_screenshot_solid}"
    Size="26"
    Color="{DynamicResource colPrim}" />
<!--#endregion-->
Вход в полноэкранный режим Выход из полноэкранного режима

Для замены одной иконки на другую в той же кнопке я использую Visual State Manager, эти иконки связаны в свойстве Source с TargetType «Image», стоит отметить, что необходимо добавить x:Key, чтобы использовать ее в элементе управления или макете, аналогичном TargetType.

<!--#region Visual States Manager-->
<Style
    x:Key="vsm_fontIconChanged"
    TargetType="Image">
    <Setter Property="VisualStateManager.VisualStateGroups">
        <VisualStateGroupList>
            <VisualStateGroup x:Name="CommonStates">

                <VisualState x:Name="Normal">
                    <VisualState.Setters>
                        <Setter Property="Source" Value="{StaticResource icon_screenshot_solid}" />
                    </VisualState.Setters>
                </VisualState>

                <VisualState x:Name="Selected">
                    <VisualState.Setters>
                        <Setter Property="Source" Value="{StaticResource icon_share_solid}" />
                    </VisualState.Setters>
                </VisualState>

            </VisualStateGroup>
        </VisualStateGroupList>
    </Setter>
</Style>
<!--#endregion-->
Вход в полноэкранный режим Выход из полноэкранного режима

В FoodDetailPage.xaml я реализую невидимый элемент управления CachedImage, отвечающий за хранение снимка изображения, и с x:Name «imageForScreenshot» мы будем работать над кодом, лежащим в основе этого xaml-файла.

<!--  Image for Screenshot  -->
<ffimageloading:CachedImage
    x:Name="imageForScreenshot"
    Grid.Row="3"
    Grid.Column="2"
    Grid.ColumnSpan="5"
    IsVisible="false" />
Вход в полноэкранный режим Выход из полноэкранного режима

В режиме Xamarin.Essentials.Screenshot

Используя GestureRecognizers, я создаю событие под названием ScreenshotAndShareFile_Tapped, и через оператор if указываю, что если свойство Source в imageForScreenshot равно null, то выполняется следующий процесс:

Переменная screenshot отвечает за захват экрана в соответствии с размерами физического устройства или эмулятора (высота и ширина).

Переменная stream получит поток данных снимка экрана с помощью метода OpenReadAsync().

Наконец, метод FromStream() является стержнем для создания изображения из указанной переменной потока, все это хранится в свойстве Source переменной imageForScreenshot.

private async void ScreenshotAndShareFile_Tapped(object sender, EventArgs e)
{
    if (imageForScreenshot.Source == null)
    {
        // Mode Xamarin.Essentials.Screenshot
        var screenshot = await Screenshot.CaptureAsync();
        var stream = await screenshot.OpenReadAsync();
        imageForScreenshot.Source = ImageSource.FromStream(() => stream);

        // Visual State Manager here...

        // Toast Multiplatform here...
    }
    else
    {
        // File Share here...
    }
}
Вход в полноэкранный режим Выход из полноэкранного режима

В режиме ImageFromXamarinUI

Эта библиотека настолько универсальна, потому что мы можем создавать специфические скриншоты на уровне интерфейса, и ее можно применять для различных сценариев использования. Очень благодарен Диме Димову за создание этого удивительного пакета nuget.

Из следующего фрагмента кода видно, что x:Name под названием layoutMainParent является родительским GridLayout, поэтому мой скриншот будет всем, что инкапсулирует этот макет.

<ContentPage.Content>

    <ScrollView>

        <Grid x:Name="layoutMainParent">

        <!--  Rest of the code here...  -->

        </Grid>

    </ScrollView>

</ContentPage.Content>
Вход в полноэкранный режим Выход из полноэкранного режима

Продолжая в событии ScreenshotAndShareFile_Tapped, переменная stream отвечает за добавление потока данных, но теперь он будет зависеть от размеров layoutMainParent, параметр метода CaptureImageAsync() предполагает цвет фона, наконец, изображение создается по той же логике, что и Xamarin.Essentials.Screenshot.

private async void ScreenshotAndShareFile_Tapped(object sender, EventArgs e)
{
    if (imageForScreenshot.Source == null)
    {
        // Mode ImageFromXamarinUI
        var stream = await layoutMainParent.CaptureImageAsync(Color.White);
        imageForScreenshot.Source = ImageSource.FromStream(() => stream);

        // Visual State Manager here...

        // Toast Multiplatform here...
    }
    else
    {
        // File Share here...
    }
}
Вход в полноэкранный режим Выход из полноэкранного режима

При касании кнопки она переходит в состояние «Selected» (иконка акции), этот процесс выполняется методом GoToState() VisualStateManager.

Теперь я вступаю на территорию Xamarin Community Toolkit, и я рад, что они добавили Toast Multiplatform, с помощью нескольких строк кода мы можем указать сообщение и продолжительность уведомления, все благодаря методу DisplayToastAsync().

<StackLayout
    Grid.Row="1"
    Grid.Column="6"
    Grid.ColumnSpan="2"
    xct:TouchEffect.NativeAnimation="True"
    xct:TouchEffect.AnimationDuration="300">
    <StackLayout.GestureRecognizers>
        <TapGestureRecognizer Tapped="ScreenshotAndShareFile_Tapped" />
    </StackLayout.GestureRecognizers>

    <Image
        x:Name="stateFontIcon"
        Style="{StaticResource vsm_fontIconChanged}"
        HorizontalOptions="Center"
        VerticalOptions="CenterAndExpand" />
</StackLayout>
Вход в полноэкранный режим Выход из полноэкранного режима
private async void ScreenshotAndShareFile_Tapped(object sender, EventArgs e)
{
    if (imageForScreenshot.Source == null)
    {
        // Mode ImageFromXamarinUI here...

        // Mode Xamarin.Essentials.Screenshot here...

        VisualStateManager.GoToState(stateFontIcon, "Selected");

        await this.DisplayToastAsync(message: "Successful screenshot", durationMilliseconds: 2000);
    }
    else
    {
        // File Share here...
    }
}
Вход в полноэкранный режим Выход из полноэкранного режима

Общий доступ к файлу

В Xamarin.Essentials есть еще один замечательный API под названием Share, который позволяет отправлять различные типы файлов установленным приложениям физического устройства или эмулятора.

Теперь, когда у нас есть захват изображения и кнопка share, мы переходим к потоку отправки файлов:

FileName отвечает за именование изображения.

FullPath объединяет fileName со свойством CacheDirectory, которое получает место, где могут храниться временные данные, а также помогает быстрее отправить файл.

Переменная byteArray хранит вес изображения в байтах, стоит отметить, что CachedImage имеет два метода для получения png и jpg изображений (в данном случае я использую метод GetImageAsPngAsync()), большое спасибо Daniel Luberda за создание этой фантастической библиотеки.

Для инкапсуляции byteArray в fullPath используется метод WriteAllBytes(), с его помощью мы уже имеем полностью упакованный файл.

В методе RequestAsync() я генерирую новый экземпляр ShareFileRequest(), там мы добавляем название и соответствующий файл, стоит отметить, что «image/png» только для того, чтобы подчеркнуть, что это png файл, добавлять его не обязательно.

И ВОТ!!!, теперь мы можем отправить наше изображение, все благодаря Xamarin.Essentials.Share.

private async void ScreenshotAndShareFile_Tapped(object sender, EventArgs e)
{
    if (imageForScreenshot.Source == null)
    {
        // Mode ImageFromXamarinUI here...

        // Mode Xamarin.Essentials.Screenshot here...

        // Visual State Manager here...

        // Toast Multiplatform here...
    }
    else
    {
        string fileName = "MicunaFood - " + foodDetailViewModel.SelectedFood.Name_Food + ".png";
        string fullPath = Path.Combine(FileSystem.CacheDirectory, fileName);

        var byteArray = await imageForScreenshot.GetImageAsPngAsync();
        File.WriteAllBytes(fullPath, byteArray);

        await Share.RequestAsync(new ShareFileRequest
        {
            Title = "Data Transfer " + foodDetailViewModel.SelectedFood.Name_Food,
            File = new ShareFile(fullPath, "image/png")
        });
    }
}
Вход в полноэкранный режим Выход из полноэкранного режима

Отправка и сохранение файла

На изображении выше видно, что устройство Android отправляет файл через приложение Microsoft Outlook, а на iPhone сохраняет скриншот, причем оба способа вполне осуществимы.

Идея заключается в том, чтобы пользователь мог делиться и сохранять различные продукты питания, которые предлагает MicunaFood, для будущих покупок.


Другие функции

Класс репозитория

Чтобы улучшить управление данными MicunaFood для различных ViewModels, они были перенесены в статический класс под названием repository.

Совет по производительности в FFImageLoading

В видеоролике Даниэля Люберды показана важность добавления свойства CacheType для повышения производительности изображений.


Результат


Получить код

Весь код является открытым, вы можете посмотреть его на моем github.


Ресурсы

  • MonettelliUIKIT.
  • Material Design.
  • Xamarin Community Toolkit.
  • ImageFromXamarinUI — Дима Димов.
  • Делайте частичные скриншоты вашего приложения Xamarin.Forms — Youtube-канал Gerald Versluis.
  • Xamarin.Essentials: Скриншот.
  • Xamarin.Essentials: Поделиться.
  • Xamarin.Forms Visual State Manager.
  • Dribbble.
  • Unsplash.
  • Pexels.

Выводы

  • Я люблю Xamarin.Essentials.Screenshot и ImageFromXamarinUI, разные функции, одна цель.
  • Одной строчкой кода можно добавить Toast Multiplatform, мое почтение Xamarin.Community.Toolkit.
  • Этот пост демонстрирует, что Xamarin.Essentials.Share отправляет не только текст (как пример в документации), но и любой тип файла.
  • Изменение состояния также может быть сделано в одном элементе управления, и Visual State Manager помогает мне добиться этого.

Помните, что вы можете дать обратную связь и с помощью моего репозитория сделать собственные выводы, если у вас есть вопросы или конструктивная критика пишите мне под этой публикацией, большое спасибо.

Оставьте комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *