Одинаковый размер представления для всех устройств

#xamarin #xamarin.forms #xamarin.android

#xamarin #xamarin.forms #xamarin.android

Вопрос:

В моем приложении Xamarin, которое у меня есть Grid с HeightRequest = 200 , я проверил его на устройствах с разным размером экрана на Android, высота отличается на каждом устройстве, возможно, из-за использования xamarin dp . Я хочу, чтобы он был одинаковым на каждом устройстве, как я могу сделать так, чтобы он был одинакового размера на каждом устройстве.Вот ссылочный снимок экрана из моего приложения

Комментарии:

1. Для пояснения: dp указан как примерно 1/160 дюйма. Проблема в том, что каждое устройство должно сообщать о своей плотности, на чем основан dp. Устройства используют «общие» значения плотности (например, 1, 2, 3, 3.5), потому что в противном случае будут небольшие дефекты внешнего вида значков, поскольку приложения пытаются масштабироваться до точной плотности. ЕДИНСТВЕННЫЙ (близкий к) правильный ответ — это тот, который использует Xdpi и Ydpi для точного вычисления размера. И даже это НЕ ГАРАНТИРОВАНО: Некоторые устройства предоставляют для них только приблизительные значения.

Ответ №1:

Я хочу, чтобы он был одинаковым на каждом устройстве, как я могу сделать так, чтобы он был одинакового размера на каждом устройстве.

Я думаю, вам нужно использовать AbsoluteLayout или исправить размер по шкале dp устройства.

160 * 1 dp = 1 дюйм.

Вам нужно рассчитать высоту сетки по dp, чтобы отобразить тот же размер.

Этот пример приобретает масштаб dp.

IServiceDp.cs

 using Xamarin.Forms;
namespace test_view.Service
{
    public interface IServiceDp
    {
        double GetDpScale();
    }
}
  

.droid

 using Android.Util;
using test_view.Service;
using Xamarin.Forms;
using Size = Xamarin.Forms.Size;

[assembly: Dependency(typeof(test_view.Droid.Service.ServiceDp))]
namespace test_view.Droid.Service
{
    public class ServiceDp : IServiceDp
    {
        public double GetDpScale()
        {
            double dp;
            
            dp = Android.App.Application.Context.Resources.DisplayMetrics.Density;

            return dp;
        }
    }
}
  

главная страница

     public MainPage()
    {
        InitializeComponent();

        var dp = DependencyService.Get<IServiceDp>().GetDpScale();
        //TODO: fix Grid height by dp scale .
    }
  

Комментарии:

1. Как рассчитать? В 1 телефоне, который я получаю 3.5 , и в другом, который я получаю 1.5

2. Этот ответ НЕВЕРЕН. Xamarin использует устройства, НЕЗАВИСИМЫЕ от устройства. НЕ умножайте на плотность.

Ответ №2:

Я достиг этого, используя предложение @deirahi выше. Вот моя реализация.

 public interface IServiceDp
    {
        double GetHeightInInches(double inch); // if you want to set height in inches
        double GetHeightInDp(double dp); // If you want to set height in Dp (Density-independent Pixels)
    } 
  

Реализация интерфейса в проекте Droid.

 using System;
using Android.Content;
using Android.Util;
using TestApp.Droid.Services;
using TestApp.Interfaces;
using Xamarin.Forms;
using Xamarin.Forms.Internals;

[assembly: Dependency(typeof(ServiceDp))]
namespace TestApp.Droid.Services
{
    [Preserve(AllMembers = true)]
    public class ServiceDp : IServiceDp
    {
        Context Context => Android.App.Application.Context;
        public double GetScreenHiehgtInInches()
        {
            DisplayMetrics dm = new DisplayMetrics();
            MainActivity.windowManager.DefaultDisplay.GetMetrics(dm);
            double density = dm.Density * 160;
            var mWidthPixels = dm.WidthPixels;
            var mHeightPixels = dm.HeightPixels;
            MainActivity.windowManager.DefaultDisplay.GetMetrics(dm);
            double x = Math.Pow(mWidthPixels / dm.Xdpi, 2);
            double y = Math.Pow(mHeightPixels / dm.Ydpi, 2);
            double screenInches = Math.Sqrt(x   y);

            //DisplayMetrics dm = new DisplayMetrics();
            //MainActivity.windowManager.DefaultDisplay.GetMetrics(dm);
            //int width = dm.WidthPixels;
            //int height = dm.HeightPixels;
            //int dens = (int) dm.DensityDpi;
            //double wi = (double)width / (double)dens;
            //double hi = (double)height / (double)dens;
            //double x = Math.Pow(wi, 2);
            //double y = Math.Pow(hi, 2);
            //double screenInches = Math.Sqrt(x   y);
            //width in inches = (WidthPixels / Xdpi)
            //height in inches = (HeightPixels / Ydpi)
            //var width = dm.WidthPixels / dm.Xdpi;
            //var height = dm.HeightPixels / dm.Ydpi;
            return screenInches;
        }

        public double GetHeightInInches(double inch)
        {
            var screenHeight = GetScreenHiehgtInInches();
            DisplayMetrics dm = new DisplayMetrics();
            MainActivity.windowManager.DefaultDisplay.GetMetrics(dm); // I'm referencing window manager from mainActivity as static variable
            var pixPerInch = (dm.HeightPixels / screenHeight) / dm.Density;
            return pixPerInch * inch;
        }

        public double GetHeightInDp(double dp)
        {
            var screenHeight = GetScreenHiehgtInInches();
            DisplayMetrics dm = new DisplayMetrics();
            MainActivity.windowManager.DefaultDisplay.GetMetrics(dm);
            var pixPerInch = (dm.HeightPixels / screenHeight) / dm.Density;
            return pixPerInch / 160 * dp;
        }
    }
}

  

использование в моем проекте PCL

 <?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="TestApp.Pages.AbsoluteLayoutWithDisplayInfoPage">
    <ContentPage.Content>
        <StackLayout Spacing="0">
            <Grid BackgroundColor="Red" x:Name="Grid1"/>
            <Grid BackgroundColor="Green" x:Name="Grid2"/>
            <Grid BackgroundColor="Blue" x:Name="Grid3"/>
        </StackLayout>
        </ContentPage.Content>
</ContentPage>

  
 var service = DependencyService.Get<IServiceDp>();
            Grid1.HeightRequest = service.GetHeightInDp(100);
            Grid2.HeightRequest = service.GetHeightInDp(200);
            Grid3.HeightRequest = service.GetHeightInDp(150);
  

Вот вывод приведенного выше кода

Вывод приведенного выше кода

Комментарии:

1. Извините, я не показал вам, как вычислять. Но вы сделали это!

2. Нет проблем, братан. Я не смог бы этого сделать без вашего предложения, кстати, спасибо.

Ответ №3:

Я бы рекомендовал вам использовать AbsoluteLayout:

 An AbsoluteLayout is used to position and size children using explicit values.
  

AbsoluteLayout.LayoutBounds Прикрепленное свойство может быть установлено с использованием двух форматов, независимо от того, используются абсолютные или пропорциональные значения:

  • x, y. В этом формате значения x и y указывают положение верхнего левого угла дочернего элемента относительно его родительского элемента. Дочерний элемент не ограничен и сам определяет размер.
  • x, y, ширина, высота. В этом формате значения x и y указывают положение верхнего левого угла дочернего элемента относительно его родительского элемента, в то время как значения ширины и высоты указывают размер дочернего элемента.

Например:

 <BoxView Color="Silver"
                 AbsoluteLayout.LayoutBounds="0, 10, 200, 5" />
  

Кстати, если вы хотите получить плотность устройств, вы можете использовать Xamarin.Основы: информация об устройстве

Комментарии:

1. Я пробовал абсолютный макет с SizePropotional, там размер по-прежнему отличается.

2. Вы не можете использовать SizePropotional, потому что при использовании SizePropotional ширина / высота зависят от размера экрана, поэтому они будут отличаться при разных размерах экрана. Вместо этого используйте фиксированное значение, как я показал в своем ответе.

3. Спасибо за ваш ответ, к сожалению, он ведет себя одинаково, размер отличается на разных устройствах. Вот код, который я опробовал.

4. « <Абсолютное отображение> <Цвет фона сетки =»Красный» Абсолютное отображение. layoutBounds=»0, 0, 420, 100″/> < Абсолютный цвет фона сетки =»Зеленый». Границы разметки=»0, 100, 420, 100″/> < Цвет фона сетки=»Синий» AbsoluteLayout. layoutBounds=»0, 200, 420, 100″/> </ AbsoluteLayout> «

5. Не могли бы вы, пожалуйста, рассказать, как я могу рассчитать плотность экрана?

Ответ №4:

Вам следует избегать использования жестко закодированных значений для запросов высоты / ширины и т.д. То же самое и со шрифтами, если честно. Формы Xamarin имеют удобные макеты и элементы управления сеткой, которые позволяют изменять размер содержимого в зависимости от того, сколько места занимают элементы без этих макетов. Однако, если вам действительно нужно это сделать, лучший способ сделать это — изменить ширину и высоту экрана, а затем настроить их значения:

             var screenHeight = (int)(Resources.DisplayMetrics.HeightPixels / Resources.DisplayMetrics.Density);
        var screenWidth = (int)(Resources.DisplayMetrics.WidthPixels / Resources.DisplayMetrics.Density);
  

Ответ №5:

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

Создайте список (скажем, «_screenSizes») возможных размеров экрана и сопоставьте (классифицируйте) каждый элемент в списке как «Маленький», «Средний», «Большой», «сверхбольшой» и так далее. Четвертый.

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

И с помощью Xamarin.Главное, вы можете сравнить размеры экрана фактического устройства, на котором запущено приложение, которое соответствует списку _screenSizes.

 public class OnScreenSize : IMarkupExtension
    {

        private static List<ScreenInfo> _screenSizes = new List<ScreenInfo>
        {
            { new ScreenInfo(480,800, eScreenSizes.ExtraSmall)}, //Samsung Galaxy S,
            { new ScreenInfo(720,1280, eScreenSizes.Small)}, //Nesus S
            { new ScreenInfo(828,1792, eScreenSizes.Medium)}, //iphone 11
            { new ScreenInfo(1284,2778, eScreenSizes.Large)}, //Apple iPhone 12 Pro Max
            { new ScreenInfo(1440,3200, eScreenSizes.ExtraLarge)}, //Samsung Galaxy S20     
            { new ScreenInfo(2732,2048, eScreenSizes.ExtraLarge)}, //Apple iPad Pro 12.9
        };

        private Dictionary<eScreenSizes, object> _values = new Dictionary<eScreenSizes, object>() {
            { eScreenSizes.ExtraSmall, null},
            { eScreenSizes.Small, null},
            { eScreenSizes.Medium,  null},
            { eScreenSizes.Large,  null},
            { eScreenSizes.ExtraLarge,  null},
        };



        public OnScreenSize()
        {
        }


        /// <summary>
        /// Screen-size do device.
        /// </summary>
        private static eScreenSizes? deviceScreenSize;

        /// <summary>
        /// Tamanho-padrao na tela que deve ser assumido quando não for possivel determinar o tamanho dela com base na lista <see cref="_screenSizes"/>
        /// </summary>
        public object DefaultSize { get; set; } 


        public object ExtraSmall
        {
            get
            {

                return _values[eScreenSizes.ExtraSmall];
            }
            set
            {
                _values[eScreenSizes.ExtraSmall] = value;
            }
        }

        public object Small
        {
            get
            {

                return _values[eScreenSizes.Small];
            }
            set
            {
                _values[eScreenSizes.Small] = value;
            }
        }
        public object Medium
        {
            get
            {

                return _values[eScreenSizes.Medium];
            }
            set
            {
                _values[eScreenSizes.Medium] = value;
            }
        }

        public object Large
        {
            get
            {

                return _values[eScreenSizes.Large];
            }
            set
            {
                _values[eScreenSizes.Large] = value;
            }
        }

        public object ExtraLarge
        {
            get
            {

                return _values[eScreenSizes.ExtraLarge];
            }
            set
            {
                _values[eScreenSizes.ExtraLarge] = value;
            }
        }



        public object ProvideValue(IServiceProvider serviceProvider)
        {
            var valueProvider = serviceProvider?.GetService<IProvideValueTarget>() ?? throw new ArgumentException();

            BindableProperty bp;
            PropertyInfo pi = null;
            Type propertyType = null;

            if (valueProvider.TargetObject is Setter setter)
            {
                bp = setter.Property;
            }
            else
            {
                bp = valueProvider.TargetProperty as BindableProperty;
                pi = valueProvider.TargetProperty as PropertyInfo;
            }

            propertyType = bp?.ReturnType ?? pi?.PropertyType ?? throw new InvalidOperationException("Não foi posivel determinar a propriedade para fornecer o valor.");

            var value = GetValue(serviceProvider);

            return value.ConvertTo(propertyType, bp);
        }


        private object GetValue(IServiceProvider serviceProvider)
        {
            var screenSize = GetScreenSize();
            if (screenSize != eScreenSizes.NotSet)
            {
                if (_values[screenSize] != null)
                {
                    return _values[screenSize];
                }
            }

            if (DefaultSize == null)
            {
                throw new XamlParseException("OnScreenExtension requires a DefaultSize set.");
            }
            else
            {
                return DefaultSize;
            }
        }


        private eScreenSizes GetScreenSize()
        {
            if (TryGetScreenSize(out var screenSize))
            {
                return screenSize;
            }

            return  eScreenSizes.NotSet;
        }


        private static bool TryGetScreenSize(out eScreenSizes screenSize)
        {
            if (deviceScreenSize != null)
            {
                if (deviceScreenSize.Value == eScreenSizes.NotSet)
                {
                    screenSize = deviceScreenSize.Value;
                    return false;
                }
                else
                {
                    screenSize = deviceScreenSize.Value;
                    return true;
                }
            }


            var device = DeviceDisplay.MainDisplayInfo;

            var deviceWidth = device.Width;
            var deviceHeight = device.Height;


            if (Xamarin.Essentials.DeviceInfo.Idiom == Xamarin.Essentials.DeviceIdiom.Tablet)
            {
                deviceWidth = Math.Max(device.Width, device.Height);
                deviceHeight = Math.Min(device.Width, device.Height);
            }


            foreach (var sizeInfo in _screenSizes)
            {
                if (deviceWidth <= sizeInfo.Width amp;amp;
                    deviceHeight <= sizeInfo.Height)
                {
                    deviceScreenSize = sizeInfo.ScreenSize;
                    screenSize = deviceScreenSize.Value;
                    return true;
                }
            }

            deviceScreenSize = eScreenSizes.NotSet;
            screenSize = deviceScreenSize.Value;
            return false;
        }

    }
  

И в вашем XAML вы устанавливаете значения для каждого размера экрана следующим образом:

 <Grid Padding="10">
  <Grid.RowDefinitions>
    <RowDefinition Height="{markups:OnScreenSize DefaultSize='Auto', Medium='30', ExtraLarge='Auto'}" />
    <RowDefinition Height="{markups:OnScreenSize DefaultSize='Auto', Medium='30', ExtraLarge='Auto'}" />
</Grid.RowDefinitions>
      <!-- your Views goes here... -->
</Grid>
  

Пожалуйста, смотрите здесь для получения дополнительной информации.