Как получить размер экрана Android программно, раз и навсегда?

132

Как я могу программно узнать размер экрана в единицах измерения, используемых для событий касания и измерения / макета просмотра? Другими словами, мне нужны координаты правого нижнего угла экрана в системе координат, используемой событиями касания ' getRawX()/getRawY()и View.getLocationOnScreen().

Я не решаюсь называть желаемые единицы события / просмотра «пикселями», поскольку, очевидно, в моем телефоне есть несколько понятий «пиксели» в различных режимах, и они не складываются в единый рассказ.

Я вижу, что об этом много спрашивали и на него много отвечали в stackoverflow и в других местах, но ни один из ответов на самом деле не работает на моем телефоне (droid 4, android 4.1.2) во всех режимах:

(Вот это да!)

Это для кода библиотеки, который должен работать независимо от того, находится ли приложение в «режиме совместимости с экраном» (т.е. targetSdkVersion <= 10 в манифесте) или нет, и я также не хочу делать никаких предположений о положении, размере , или наличие строки состояния или любых других подобных украшений экрана.


Вот факты:

  1. мой телефон (дроид 4 под управлением Android 4.1.2) имеет физические пиксели 540x960, то есть маленькие цветные светящиеся точки.

  2. размер экрана в требуемых единицах измерения, исходя из событий касания и просмотра измерений, составляет 360x640, когда приложение находится в режиме совместимости с экраном, и 540x960, когда приложение не находится в режиме совместимости с экраном. Это числа, которые мне нужно найти программно, не прибегая к событиям касания или представлениям, чтобы найти их, но мне очень трудно найти какой-либо API, который вернет эти числа.

  3. Все объекты Display и DisplayMetrics, полученные различными способами, заявляют, что размер экрана составляет 540x960 "пикселей" (независимо от того, находится ли он в режиме совместимости с экраном или нет). Если быть точным, все следующие элементы всегда говорят 540x960: DisplayMetrics. {Width, height} Pixels, Display.getSize (), Display.getRealSize (), Display.get {Width, Height} (),

  4. Все объекты конфигурации, полученные различными способами, говорят screen {Width, Height} Dp = 360x614 (в режиме совместимости с экраном или нет). Я не верю, что это представляет собой весь экран, поскольку соотношение сторон неправильное. (Я думаю, что это весь экран за вычетом строки состояния; мне нужен весь экран.) Я думаю, можно с уверенностью сказать, что весь экран имеет разрешение 360x640 dp, хотя я не знаю ни одного API, возвращающего это 640.

  5. DisplayMetrics, полученные различными способами, говорят, что "плотность" составляет 1,0f в режиме совместимости с экраном и 1,5f, когда не в режиме совместимости с экраном.

  6. Действия getWindow().getAttributes().{width,height} бесполезны, поскольку обычно содержат, MATCH_PARENT а не фактические размеры. Но я, по-видимому, могу получить желаемый ответ от действия getWindow().getDecorView().getMeasured{Width,Height}() (что на самом деле удивительно, поскольку decorView окна действия не выглядит так, как будто он занимает весь экран; похоже, что он занимает экран без строки состояния). Но я не хочу полагаться на это, потому что, если когда-либо изменится размер окна (появится мягкая клавиатура? Кто-то вызывает window.setAttributes ()? Или, может быть, я вообще не в Activity), это явно будет все неправильно.

Я понимаю, что должна выполняться следующая формула: пиксели = dp * плотность Это похоже на все указанные числа ((3), (4), (5) выше), когда не в режиме совместимости с экраном: 540x960 = 360x640 * 1,5 А вот в режиме совместимости экранов не складывается: 540x960! = 360x640 * 1 Значит, что-то не так.

Я думаю, что самое простое объяснение состоит в том, что методы, перечисленные в пункте (3) выше, просто дают неправильный ответ для «пикселей» в режиме совместимости экрана - то есть они были предназначены для возврата «пикселей» в размере 360x640, но они ошибочны. вместо этого возвращается 540x960. Но могут быть другие взгляды на это.

В любом случае, получение желаемых чисел независимо от режима из приведенных выше частей головоломки, безусловно, является сложной задачей. Я нашел способ, который, кажется, работает на моем телефоне в обоих режимах, но он чрезвычайно окольный и основан на двух предположениях, которые все еще кажутся довольно шаткими (как описано в комментариях к коду ниже).

Вот мой рецепт:

/** get screen size in "pixels", i.e. touchevent/view units.
* on my droid 4, this is 360x640 or 540x960
* depending on whether the app is in screen compatibility mode
* (i.e. targetSdkVersion<=10 in the manifest) or not. */
public void getScreenSizePixels(int widthHeightInPixels[/*2*/])
{
    Resources resources = getResources();
    Configuration config = resources.getConfiguration();
    DisplayMetrics dm = resources.getDisplayMetrics();
    // Note, screenHeightDp isn't reliable
    // (it seems to be too small by the height of the status bar),
    // but we assume screenWidthDp is reliable.
    // Note also, dm.widthPixels,dm.heightPixels aren't reliably pixels
    // (they get confused when in screen compatibility mode, it seems),
    // but we assume their ratio is correct.
    double screenWidthInPixels = (double)config.screenWidthDp * dm.density;
    double screenHeightInPixels = screenWidthInPixels * dm.heightPixels / dm.widthPixels;
    widthHeightInPixels[0] = (int)(screenWidthInPixels + .5);
    widthHeightInPixels[1] = (int)(screenHeightInPixels + .5);
}

Есть ли лучший / более чистый способ узнать размер экрана?

Дон хэтч
источник
29
+1 Отличный вопрос, и показывает много исследовательских усилий.
Alex Gittemeier 05
1
@Don В документации объясняется, что режим совместимости перестанет выполнять операцию "масштабирование до соответствия" в старых настройках. Однако неясно, «как это работает», из ваших наблюдений я думаю, что они просто отключают масштабирование плотности, т.е. 1px становится 1dp. Возможно, это изменение касается только части визуализации и DisplayMetricsникогда не обновляется такими изменениями. Есть такие вопросы , как это уже подали.
SD
@ user117 - интересно. Ну как минимум обновилась часть DisplayMetrics - плотность изменилась на 1.0. Но, похоже, widthPixels / heightPixels не изменились. Что касается указанной проблемы, похоже, что поведение вернулось к поведению 3.1 (то есть я вижу, что heightPixels включает строку состояния ... но, похоже, в неправильных единицах)
Дон Хэтч
@DonHatch Спасибо, любезно - я на самом деле пытался похвалить продуманный вопрос, который, как ни странно, не так приветствуется на StackExchange - обратите внимание, как вопрос, который является более чем "настоящим", ничего не дает в ответах, говоря о (низкий ) качество знаний аудитории по теме. Я могу быть травмирован тем, что мои собственные вопросы закрываются один за другим из-за того, что они «не настоящие», и чувство юмора может подводить меня, но это начинает выглядеть как вопросы, на которые есть «приятные» ответы, которые действительно во что это все превращается ... И, конечно же, я проголосовал за вашу :)
mlvljr
Кстати! вот что-то вроде моих подозрений: michael.richter.name/blogs/… (особенно см. его (1) пункт, касающийся быстро составленных ответов - на самом деле как раз тот случай с этим Q!). В любом случае, счастливого Нью-Йорка (мой приходит через 11 минут)! :)
mlvljr

Ответы:

41
Display display = getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
int width = size.x;
int height = size.y;

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

Cobra47
источник
3
Согласно Op (в частности, пункт № 3), не было бы это неправильно в режиме совместимости экрана?
Tom
9

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

DisplayMetrics metrics; 
int width = 0, height = 0;

В вашем методе onCreate

 metrics = new DisplayMetrics();        
 getWindowManager().getDefaultDisplay().getMetrics(metrics);

 height = Math.min(metrics.widthPixels, metrics.heightPixels); //height
 width = Math.max(metrics.widthPixels, metrics.heightPixels); //width

Попробуй это.

Preet_Android
источник
2
нет, как я сказал в (3), метрики. {width, height} Пиксели не дают правильного ответа в режиме совместимости с экраном.
Don Hatch
Также я обнаружил: кажется, что lite metrics.heightPixels может дать ширину в случае поворота экрана и наоборот!
JohnyTex
8

В своем №6 вы отмечаете, что DecorView, кажется, предоставляет то, что вы хотите, но вы не думаете, что это реально. Я считаю, что это так близко, насколько это возможно. Согласно документации на Window.getDecorView:

Получите представление оформления окна верхнего уровня (содержащее стандартную оконную рамку / украшения и содержимое клиента внутри этого), которое можно добавить как окно в диспетчер окон.

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

Надеюсь, это поможет.

Дейв
источник
5

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

    DisplayMetrics metrics = new DisplayMetrics();
    getWindowManager().getDefaultDisplay().getMetrics(metrics);   
    width = metrics.heightPixels;
    height = metrics.widthPixels;

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

abhyudayasrinet
источник
Я думаю, это потому, что ваш экран повернулся.
JohnyTex
Мне не поверили.
abhyudayasrinet
3

Если вы используете api уровня 17 или выше, проверьте getRealMetrics и getRealSize в Display

Для уровней API 14,15 и 16 смотрите здесь

Exoit
источник
3

Я использовал теорему Пифагора, чтобы найти размер диагонали экрана телефона / планшета Android, тот же принцип можно применить к экрану iPhone или Blackberry.

DisplayMetrics met = new DisplayMetrics();                
this.getWindowManager().getDefaultDisplay().getMetrics(met);// get display metrics object
String strSize = 
new DecimalFormat("##.##").format(Math.sqrt(((met.widthPixels / met.xdpi) *
(met.widthPixels / met.xdpi)) +
((met.heightPixels / met.ydpi) * (met.heightPixels / met.ydpi))));
// using Dots per inches with width and height

Пифагор, должно быть, был гением, он много лет назад знал программирование смартфонов: p

Наим А. Малик
источник
1

Я использую метод ниже, чтобы получить ширину устройства:

public static int getDeviceWidth(Context context){
    WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
    Display display = wm.getDefaultDisplay();
    int width=display.getWidth();
    return width;
}
Ганшьям Патидар
источник
Этот метод устарел. Пожалуйста, используйте метод displaymetrics.
Саймон
0

Я думаю, эта функция поможет вам просто получить ширину и высоту экрана вашего Android. Функция возвращает ширину и высоту в виде массива целых чисел, как показано ниже.

private int[] getScreenSIze(){
        DisplayMetrics displaymetrics = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
        int h = displaymetrics.heightPixels;
        int w = displaymetrics.widthPixels;

        int[] size={w,h};
        return size;

    }

и в вашем методе создания выведите свою ширину и высоту, как показано ниже

 int[] screenSize= getScreenSIze();
        int width=screenSize[0];
        int height=screenSize[1];
        screenSizes.setText("Phone Screen sizes \n\n  width = "+width+" \n Height = "+height);

Загрузите исходный код и протестируйте его на своей студии Android

Даниэль Ньямасьо
источник
0

Это работает для меня:

int width = Resources.getSystem (). getDisplayMetrics (). widthPixels;

int height = Resources.getSystem (). getDisplayMetrics (). heightPixels;

Я использовал это, чтобы обновить ширину моих предметов в горизонтальном представлении ресайклера. (Согласно моим требованиям) Надеюсь, это кому-то поможет!

Гаган Дип
источник
-1

Для этого решения в Котлине попробуйте следующее:

private fun yourFunction(){
   val metrics = DisplayMetrics()
   windowManager.defaultDisplay.getMetrics(metrics)
   val width = metrics.widthPixels
   val height = metrics.heightPixels
}
Sup.Ia
источник