jak programowo uzyskać rozmiar ekranu Androida raz na zawsze?

132

Jak mogę programowo sprawdzić rozmiar ekranu w jednostkach używanych przez zdarzenia dotyku i wyświetlić pomiar / układ? Innymi słowy, chcę mieć współrzędne w prawym dolnym rogu ekranu, w układzie współrzędnych używanym przez zdarzenia dotykowe ” getRawX()/getRawY()i View.getLocationOnScreen().

Waham się, czy nazywać żądane jednostki zdarzenia / widoku „pikselami”, ponieważ najwyraźniej w moim telefonie jest kilka pojęć „pikseli” w różnych trybach i nie składają się one na spójną historię.

Widzę, że zadawano to wiele pytań i odpowiadano na nie w trybie stackoverflow i gdzie indziej, ale żadna z odpowiedzi nie działa na moim telefonie (droid 4, android 4.1.2) we wszystkich trybach:

(łał!)

Dotyczy to kodu biblioteki, który musi działać niezależnie od tego, czy aplikacja jest w „trybie zgodności ekranu” (tj. TargetSdkVersion <= 10 w manifeście), czy nie. Nie chcę też robić żadnych założeń dotyczących pozycji, rozmiaru lub istnienie paska stanu lub innych podobnych dekoracji ekranu.


Oto fakty:

  1. mój telefon (droid 4 z systemem Android 4.1.2) ma 540x960 pikseli fizycznych, tj. małe kolorowe, świecące kropki.

  2. rozmiar ekranu w żądanych jednostkach, liczony od zdarzeń dotykowych i pomiarów podglądu, to 360x640, gdy aplikacja jest w trybie zgodności ekranu, 540x960, gdy aplikacja nie jest w trybie zgodności z ekranem. Są to liczby, które muszę znaleźć programowo, bez manipulowania zdarzeniami dotykowymi lub widokami, aby je znaleźć, ale mam ogromne trudności ze znalezieniem jakiegokolwiek interfejsu API, który zwróci te liczby.

  3. Obiekty Display i DisplayMetrics uzyskane na różne sposoby twierdzą, że rozmiar ekranu wynosi 540x960 „pikseli” (niezależnie od tego, czy jest w trybie zgodności z ekranem, czy nie). Mówiąc dokładniej, wszystkie poniższe mówią cały czas 540x960: DisplayMetrics. {Width, height} Pixels, Display.getSize (), Display.getRealSize (), Display.get {Width, Height} (),

  4. Wszystkie obiekty konfiguracyjne uzyskane na różne sposoby mówią screen {Width, Height} Dp = 360x614 (czy to w trybie zgodności ekranu, czy nie). Nie sądzę, żeby to reprezentowało cały ekran, ponieważ proporcje są nieprawidłowe. (Myślę, że to cały ekran bez paska stanu; potrzebuję całego ekranu.) Myślę, że można bezpiecznie powiedzieć, że cały ekran ma 360x640 dp, chociaż nie znam żadnego interfejsu API, który zwraca tę 640.

  5. DisplayMetrics otrzymane na różne sposoby mówią, że „gęstość” wynosi 1,0 f w trybie zgodności ekranu i 1,5 f, gdy nie jest w trybie zgodności ekranu.

  6. Działanie getWindow().getAttributes().{width,height} nie jest pomocne, ponieważ zazwyczaj zawiera MATCH_PARENT raczej niż rzeczywiste rozmiary. Ale najwyraźniej mogę uzyskać pożądaną odpowiedź z działania getWindow().getDecorView().getMeasured{Width,Height}() (co jest w rzeczywistości zaskakujące, ponieważ decorView okna działania nie wygląda tak, jakby zajmował cały ekran; wygląda na to, że zajmuje ekran bez paska stanu). Ale nie chcę na tym polegać, ponieważ jeśli rozmiar okna kiedykolwiek zostanie zmieniony (pojawia się miękka klawiatura? Ktoś wywołuje window.setAttributes ()? Lub może w ogóle nie jestem w działaniu), to najwyraźniej wszystko źle.

Rozumiem, że powinna obowiązywać następująca formuła: piksele = dp * gęstość Wydaje się, że zgadza się ze wszystkimi zgłoszonymi liczbami ((3), (4), (5) powyżej), gdy nie jest w trybie zgodności ekranu: 540x960 = 360x640 * 1,5 Ale w trybie zgodności ekranu nie sumuje się: 540x960! = 360x640 * 1 Coś jest nie tak.

Myślę, że najprostszym wyjaśnieniem jest to, że metody wymienione w (3) powyżej po prostu dają błędną odpowiedź na „piksele” w trybie zgodności z ekranem - to znaczy miały zwracać 360x640 „pikseli”, ale są błędne zamiast tego zwraca 540x960. Ale mogą istnieć inne sposoby spojrzenia na to.

W każdym razie uzyskanie pożądanych liczb, niezależnie od trybu, z powyższych elementów układanki, jest z pewnością trudną łamigłówką. Znalazłem sposób, który wydaje się działać na moim telefonie w obu trybach, ale jest niezwykle okrężny i opiera się na dwóch założeniach, które nadal wydają się dość chwiejne (jak opisano w komentarzach do kodu poniżej).

Oto mój przepis:

/** 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);
}

Czy istnieje lepszy / czystszy sposób na znalezienie rozmiaru ekranu?

Don Hatch
źródło
29
+1 Świetne pytanie i pokazuje dużo wysiłku badawczego.
Alex Gittemeier,
1
@Don W dokumentach wyjaśniono, że tryb zgodności przestanie wykonywać operację „powiększania w celu dopasowania” na starszych konfiguracjach. Jednak nie jest jasno wyjaśnione "jak to działa", z twoich obserwacji myślę, że po prostu wyłączają skalowanie gęstości, tj.: 1px staje się 1dp. Może się zdarzyć, że ta zmiana dotyczy tylko części renderującej i DisplayMetricsnigdy nie jest aktualizowana o takie zmiany. Takie sprawy są już zgłoszone.
SD,
@ user117 - interesujące. Cóż, część DisplayMetrics została przynajmniej zaktualizowana - gęstość zmieniono na 1.0. Wygląda na to, że widthPixels / heightPixels nie zostały zmienione w celu dopasowania. Jeśli chodzi o cytowany problem, wygląda na to, że zachowanie zostało przywrócone do zachowania 3.1 (tj. Widzę, że piksele wysokości zawierają pasek stanu ... ale wydaje się, że w niewłaściwych jednostkach)
Don Hatch,
@DonHatch Dziękuję, uprzejmie - właściwie próbowałem pochwalić przemyślane pytanie, dziwnie niezbyt mile widziane na StackExchange - zwróć uwagę, że pytanie, które jest czymś więcej niż „prawdziwym”, nie daje nic w odpowiedziach, mówiąc o (niski ) jakość wiedzy odbiorców na dany temat. Teraz mogę być zszokowany tym, że moje własne pytania są zamykane jeden po drugim za to, że jestem „nieprawdziwy”, a poczucie humoru może mnie zawodzić, ale zaczyna wyglądać tak, jakby pytania, na które są „ładne” odpowiedzi, są naprawdę co to wszystko się dzieje ... I oczywiście zagłosowałem za twoją :)
mlvljr
przy okazji! oto coś na wzór moich podejrzeń: michael.richter.name/blogs/… (szczególnie zobacz jego (1) punkt, ten dotyczący szybko opracowanych odpowiedzi - właściwie tak jest z tym pytaniem!). W każdym razie szczęśliwego Nowego Jorku (mój jest za 11 minut)! :)
mlvljr

Odpowiedzi:

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

Teraz możesz zmierzyć rozmiar ekranu w pikselach, co jest lepszą jednostką miary niż centymetr, ponieważ wszystkie przyciski, widoki tekstu itp. Są mierzone w tej jednostce. Tego używam normalnie

Cobra47
źródło
3
Zgodnie z Operą (konkretnie punkt # 3), czy nie byłoby to niepoprawne w trybie zgodności ekranu?
Tom
9

Korzystając z DisplayMetrics, możesz uzyskać wysokość i szerokość ekranu dowolnego urządzenia. Zwróć uwagę, że szerokość i wysokość są wrażliwe na obrót. tutaj jest kod.

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

W swojej metodzie onCreate

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

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

Spróbuj tego.

Preet_Android
źródło
3
nie, jak powiedziałem w (3), metryki. {szerokość, wysokość} Piksele nie dają właściwej odpowiedzi w trybie zgodności ekranu.
Don Hatch,
Odkryłem również: Wydaje się, że metric. HeightPixels może dawać szerokość w przypadku obrócenia ekranu i odwrotnie!
JohnyTex
8

W swoim # 6 zauważasz, że DecorView wydaje się zapewniać to, czego chcesz, ale uważasz, że nie jest to wiarygodne. Uważam, że jest to tak blisko, jak tylko możesz. Zgodnie z dokumentacją dla Window.getDecorView:

Pobierz widok dekoracji okna najwyższego poziomu (zawierający standardową ramę okna / dekoracje i zawartość klienta w nich), który można dodać jako okno do menedżera okien.

Pasek stanu jest częścią wspomnianej tam dekoracji okna. Klawiatury programowe i inne nakładki otrzymają własne okno, więc nie powinny kolidować z odpowiednimi wartościami. To prawda, że ​​nie są to dokładnie metryki wyświetlania, ale jeśli twoja aplikacja jest pełnoekranowa, zawsze powinna to być wielkość pełnego ekranu przefiltrowana przez tłumacz zgodności. Inne aplikacje nie będą miały dostępu do Twojej aplikacji Window, więc nie musisz się martwić, że inna aplikacja zmieni atrybuty, gdy nie patrzysz.

Mam nadzieję, że to pomoże.

Dave
źródło
5

Powinienem dodać to jako komentarz, ale nie mam za to przedstawiciela.
To może nie być całkowicie poprawna odpowiedź, ale moje wymiary uzyskałem, kiedy zmieniłem wysokość i szerokość w metodzie DisplayMetrics.

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

jak powiedziałem, może to nie być poprawne, ale nie wiem dlaczego, ale zadziałało.

abhyudayasrinet
źródło
To dlatego, że wydaje mi się, że ekran się obrócił.
JohnyTex
Nie ufał mi.
abhyudayasrinet
3

Jeśli używasz interfejsu API na poziomie 17 lub wyższym, sprawdź getRealMetrics i getRealSize w Display

Dla poziomu API 14, 15 i 16 zajrzyj tutaj

Exoit
źródło
3

Użyłem twierdzenia Pitagorasa, aby znaleźć przekątną ekranu telefonu / tabletu z Androidem, tę samą zasadę można zastosować do ekranu iPhone'a lub 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

Pitagoras musiał być geniuszem, programowanie smartfonów znał wiele lat temu: str

Naeem A. Malik
źródło
1

Używam poniższej metody, aby uzyskać szerokość urządzenia:

public static int getDeviceWidth(Context context){
    WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
    Display display = wm.getDefaultDisplay();
    int width=display.getWidth();
    return width;
}
Ghanshyam Patidar
źródło
Ta metoda jest przestarzała. Proszę skorzystać z metody displaymetrics.
Simon
0

Myślę, że ta funkcja pomoże ci po prostu uzyskać szerokość i wysokość rozmiaru ekranu Androida. Funkcja zwraca szerokość i wysokość jako tablicę liczb całkowitych, jak pokazano poniżej

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;

    }

a w metodzie tworzenia podaj szerokość i wysokość, jak pokazano poniżej

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

Pobierz kod źródłowy i przetestuj go w swoim studiu Android

Daniel Nyamasyo
źródło
0

To działa dla mnie:

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

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

Użyłem tego do aktualizacji szerokości moich przedmiotów w widoku poziomym recyklera. (Zgodnie z moim wymaganiem) Mam nadzieję, że to komuś pomoże!

Gagan Deep
źródło
-1

W przypadku tego rozwiązania w Kotlinie wypróbuj to:

private fun yourFunction(){
   val metrics = DisplayMetrics()
   windowManager.defaultDisplay.getMetrics(metrics)
   val width = metrics.widthPixels
   val height = metrics.heightPixels
}
Sup.Ia
źródło