Compatibilidade com diferentes densidades de pixels

Os dispositivos Android não têm apenas tamanhos de tela diferentes, como celulares, tablets, TVs, etc., mas também têm telas com diferentes tamanhos de pixel. Um dispositivo pode ter 160 pixels por polegada, enquanto outro dispositivo cabe 480 pixels no mesmo espaço. Se você não considerar essas variações densidade de pixel, o sistema pode dimensionar suas imagens, resultando em imagens borradas, ou as imagens podem aparecem no tamanho errado.

Esta página mostra como você pode projetar seu aplicativo para oferecer suporte densidades de pixel diferentes usando unidades de medidas independentes de resolução e fornecer recursos de bitmap alternativos para cada densidade de pixel.

Assista ao vídeo a seguir para ter uma visão geral dessas técnicas.

Para saber mais sobre o design de recursos de ícones, consulte as Diretrizes de ícones do Material Design (em inglês).

Usar pixels de densidade independente

Evite usar pixels para definir distâncias ou tamanhos. Definir dimensões com pixels é um problema porque telas diferentes têm densidades de pixel diferentes, de modo que o mesmo número de pixels corresponde a diferentes tamanhos físicos em dispositivos diferentes.

Uma imagem mostrando dois exemplos de telas de dispositivos com densidades diferentes
Figura 1: duas telas do mesmo tamanho podem ter um número diferente de pixels.

Para preservar o tamanho visível da interface em telas com diferentes densidades, projete sua interface usando pixels de densidade independente (dp) como unidade de medida. Um dp é um unidade virtual de pixel que é aproximadamente igual a um pixel em uma tela de densidade média (160 dpi, a densidade "básica"). O Android converte esse valor para o número apropriado de pixels reais para cada densidade.

Considere os dois dispositivos da Figura 1. Uma visualização que é 100 pixels de largura parece muito maior no dispositivo à esquerda. Uma visualização definido como 100 dp de largura, tem o mesmo tamanho nas duas telas.

Ao definir tamanhos de texto, você pode usar ferramentas escalonáveis pixels (sp) como unidades. A unidade sp é é do mesmo tamanho de um dp, por padrão, mas é redimensionado com base na preferência do usuário tamanho do texto. Nunca use sp para tamanhos de layout.

Por exemplo, para especificar o espaçamento entre duas visualizações, use dp:

<Button android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/clickme"
    android:layout_marginTop="20dp" />

Ao especificar o tamanho do texto, use sp:

<TextView android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:textSize="20sp" />

Converter unidades de dp em unidades de pixel

Em alguns casos, você precisa expressar dimensões em dp e depois convertê-los em pixels. Conversão de unidades dp em pixels da tela é o seguinte:

px = dp * (dpi / 160)

Observação:nunca codifique essa equação para calcular pixels. Em vez disso, use TypedValue.applyDimension(), que converte vários tipos de dimensões (dp, sp etc.) em pixels.

Imagine um app em que um gesto de rolagem ou de deslize rápido é reconhecido depois que o dedo do usuário tiver movido pelo menos 16 pixels. Com base na linha de base na tela, o dedo do usuário precisa se mover 16 pixels / 160 dpi, o que equivale a 2,5 mm (1/10 de polegada), antes de o gesto for reconhecido.

Em um dispositivo Em uma tela de alta densidade (240 dpi), o dedo do usuário precisa se mover 16 pixels / 240 dpi, que igual a 1,7 mm (1/15 de polegada). A distância é muito mais curta, e o aplicativo, portanto, parece mais sensível para o usuário.

Para corrigir esse problema, expresse o limite de gestos no código em dp e e depois converter em pixels reais. Exemplo:

Kotlin

// The gesture threshold expressed in dp
private const val GESTURE_THRESHOLD_DP = 16.0f

private var gestureThreshold: Int = 0

// Convert the dps to pixels, based on density scale
gestureThreshold = TypedValue.applyDimension(
  COMPLEX_UNIT_DIP,
  GESTURE_THRESHOLD_DP + 0.5f,
  resources.displayMetrics).toInt()

// Use gestureThreshold as a distance in pixels...

Java

// The gesture threshold expressed in dp
private final float GESTURE_THRESHOLD_DP = 16.0f;

// Convert the dps to pixels, based on density scale
int gestureThreshold = (int) TypedValue.applyDimension(
  COMPLEX_UNIT_DIP,
  GESTURE_THRESHOLD_DP + 0.5f,
  getResources().getDisplayMetrics());

// Use gestureThreshold as a distance in pixels...

O campo DisplayMetrics.density especifica o fator de escala usado para converter unidades dp em de acordo com a densidade de pixels atual. Em uma tela de densidade média, DisplayMetrics.density é igual a 1,0 e em uma tela de alta densidade é igual a 1,5. Em uma tela de densidade extra-alta, ela equivale a 2,0 e, em uma tela de baixa densidade, igual a 0,75. Esse número é usado por TypedValue.applyDimension() para para consultar a contagem real de pixels da tela atual.

Usar valores de configuração pré-dimensionados

É possível usar a classe ViewConfiguration para acessar recursos distâncias, velocidades e tempos usados pelo sistema Android. Por exemplo, o distância em pixels usada pelo framework, porque o limite de rolagem pode ser obtido com getScaledTouchSlop():

Kotlin

private val GESTURE_THRESHOLD_DP = ViewConfiguration.get(myContext).scaledTouchSlop

Java

private final int GESTURE_THRESHOLD_DP = ViewConfiguration.get(myContext).getScaledTouchSlop();

Métodos em ViewConfiguration que começam com o prefixo getScaled têm a garantia de retornar um valor em pixels exibido corretamente, independentemente da densidade de pixels.

Dar preferência a gráficos vetoriais

Uma alternativa à criação de diversas versões de densidade específica de uma imagem é criar apenas um gráfico vetorial. Gráficos vetoriais criam uma imagem usando XML para define caminhos e cores em vez de usar bitmaps de pixels. Assim, o vetor gráficos podem ser dimensionados para qualquer tamanho sem artefatos de dimensionamento, embora sejam normalmente melhor para ilustrações como ícones, não para fotografias.

Gráficos vetoriais costumam ser fornecidos como arquivos SVG (Scalable Vector Graphics), mas o Android não oferece suporte a esse formato, por isso você deve converter os arquivos SVG Vetor do Android drawable.

É possível converter um SVG em um drawable vetorial usando o Vector Asset Studio da seguinte forma:

  1. Na janela Project, clique com o botão direito do mouse no diretório res e selecione Novo > Vector Asset.
  2. Selecione Local file (SVG, PSD).
  3. Localize o arquivo que você quer importar e faça os ajustes necessários.

    Uma imagem mostrando como importar SVGs no Android Studio
    Figura 2: importar um SVG com o Android Studio

    Talvez você encontre alguns erros na janela do Asset Studio. indicando que drawables vetoriais não oferecem suporte a algumas propriedades do arquivo. Isso não impede que você importe o arquivo; as propriedades sem suporte são ignorados.

  4. Clique em Next.

  5. Na próxima tela, confirme o conjunto de origem em que você quer colocar o arquivo no projeto e clique em Concluir.

    Como um drawable vetorial pode ser usado em todas as densidades de pixel, esse arquivo entra no seu diretório de drawables padrão, como mostrado no exemplo a seguir. hierarquia. Você não precisa usar diretórios específicos de densidade.

    res/
      drawable/
        ic_android_launcher.xml
    

Para mais informações sobre como criar gráficos vetoriais, leia drawable vetorial na documentação do Google Cloud.

Fornecer bitmaps alternativos

Para oferecer uma boa qualidade gráfica em dispositivos com densidades de pixel diferentes, fornecer várias versões de cada bitmap em seu app, uma para cada em um bucket de densidade específica, em uma resolução correspondente. Caso contrário, o Android precisa ser escalonado seu bitmap para que ele ocupe o mesmo espaço visível em cada tela, resultando em Artefatos de dimensionamento, como desfoque.

Uma imagem que mostra tamanhos relativos para bitmaps em diferentes densidades
Figura 3: tamanhos relativos para bitmaps em diferentes buckets de densidade.

Há vários intervalos de densidade disponíveis para uso nos seus apps. Tabela 1 descreve os diferentes qualificadores de configuração disponíveis e quais tipos de tela a que se aplicam.

Tabela 1. Qualificadores de configuração para diferentes densidades de pixels ideais.

Qualificador de densidade Descrição
ldpi Recursos para telas de baixa densidade (ldpi) (cerca de 120 dpi).
mdpi Recursos para telas de média densidade (mdpi) (cerca de 160 dpi). Essa é a linha de base densidade
hdpi Recursos para telas de alta densidade (hdpi) (cerca de 240 dpi).
xhdpi Recursos para telas de densidade extra-alta (xhdpi) (cerca de 320 dpi).
xxhdpi Recursos para telas de densidade extra-extra-alta (xxhdpi) (cerca de 480 dpi).
xxxhdpi Recursos para telas de densidade extra-extra-extra-alta (xxxhdpi) (cerca de 640 dpi).
nodpi Recursos para todas as densidades. Esses são recursos independentes de densidade. O sistema não dimensionar recursos marcados com esse qualificador, independentemente da densidade da tela atual.
tvdpi recursos para telas entre mdpi e hdpi; aproximadamente aproximadamente 213 dpi. Não é considerado um grupo de densidade "principal".' Ele é destinado principalmente para televisões, e a maioria dos apps não precisa deles, fornecendo mdpi e hdpi. recursos é suficiente para a maioria dos aplicativos, e o sistema os escalona conforme apropriados. Se você achar necessário fornecer recursos tvdpi, dimensione-os a um fator de 1,33 * mdpi. Por exemplo, uma imagem de 100 x 100 pixels para mdpi é 133 x 133 pixels para tvdpi.

Para criar drawables bitmap alternativos para diferentes densidades, siga as 3:4:6:8:12:16 entre as seis densidades principais. Por exemplo, se você tiver um drawable de bitmap de 48 x 48 pixels para telas de densidade média, os tamanhos serão:

  • 36 x 36 (0,75 x) para densidade baixa (ldpi)
  • 48 x 48 (1,0 x - configuração básica) para densidade média (mdpi)
  • 72 x 72 (1,5 x) para alta densidade (hdpi)
  • 96 x 96 (2,0 x) para densidade extra-alta (xhdpi)
  • 144 x 144 (3,0 x) para densidade extra-extra-alta (xxhdpi)
  • 192 x 192 (4,0 x) para densidade extra-extra-extra-alta (xxxhdpi)

Colocar os arquivos de imagem gerados no subdiretório adequado em res/:

res/
  drawable-xxxhdpi/
    awesome_image.png
  drawable-xxhdpi/
    awesome_image.png
  drawable-xhdpi/
    awesome_image.png
  drawable-hdpi/
    awesome_image.png
  drawable-mdpi/
    awesome_image.png

Assim, sempre que você fizer referência a @drawable/awesomeimage, o sistema selecionará o bitmap apropriado com base no dpi da tela. Se você não fornecerem um recurso específico de densidade, o sistema localizará a próxima melhor correspondência e a dimensiona para caber na tela.

Dica:se você tiver recursos drawable que você não quer que o sistema escalone, por exemplo, ao realizar fazer alguns ajustes na imagem durante a execução, colocá-los em um com o qualificador de configuração nodpi. Recursos com esse qualificador são considerados agnósticos de densidade, e o sistema não os dimensiona.

Para mais informações sobre outros qualificadores de configuração e como o Android seleciona os recursos adequados para a configuração da tela atual, consulte a Visão geral dos recursos de app.

Colocar ícones de app em diretórios mipmap

Assim como acontece com outros recursos de bitmap, você precisa fornecer versões específicas para densidades de ícone do seu app. No entanto, alguns inicializadores de apps exibem o ícone do app em até 25% maior do que o exigido pelo intervalo de densidade do dispositivo.

Por exemplo, se o intervalo de densidade de um dispositivo for xxhdpi e o maior ícone de app que você fornecer esteja em drawable-xxhdpi, o Acesso rápido aos apps dimensionará esse ícone, o que faz com que pareça menos nítido.

Para evitar isso, coloque todos os ícones do app nos diretórios mipmap em vez de drawable. Não gostei Diretórios drawable, todos os diretórios mipmap serão mantidos no APK, mesmo se você criar APKs específicos para densidades. Assim, os apps de tela de início escolhem as melhores ícone de resolução a ser exibido na tela inicial.

res/
  mipmap-xxxhdpi/
    launcher_icon.png
  mipmap-xxhdpi/
    launcher_icon.png
  mipmap-xhdpi/
    launcher_icon.png
  mipmap-hdpi/
    launcher_icon.png
  mipmap-mdpi/
    launcher_icon.png

No exemplo anterior de um dispositivo xxhdpi, é possível fornecer uma ícone de maior densidade na tela de início no diretório mipmap-xxxhdpi.

Para orientações sobre criação de ícones, consulte Ícones do sistema.

Se precisar de ajuda para criar ícones, consulte Criar ícones com o Image Asset Studio.

Orientações para problemas de densidade incomuns

Esta seção descreve como o Android realiza o escalonamento para bitmaps em diferentes densidades de pixel e como é possível ter mais controle bitmaps são desenhados em diferentes densidades. A menos que seu aplicativo manipule gráficos ou tiver encontrado problemas ao executar em densidades de pixel diferentes, ignore esta seção.

Para entender melhor como oferecer suporte a várias densidades ao manipular gráficos no ambiente de execução, você precisa saber como o sistema ajuda a garantir o escalonamento adequado para bitmaps. Isso é feito das seguintes maneiras:

  1. Pré-dimensionamento de recursos, como drawables de bitmap

    Com base na densidade da tela atual, o sistema usa qualquer densidade específica recursos do seu app. Se os recursos não estiverem disponíveis a densidade correta, o sistema carrega os recursos padrão e os aumenta ou diminui conforme necessário. O sistema assume que os recursos padrão (aqueles de uma sem qualificadores de configuração) são projetados para o valor de referência densidade de pixel (mdpi) e redimensiona esses bitmaps para o tamanho apropriado para a densidade de pixels atual.

    Se você solicitar as dimensões de um recurso pré-dimensionado, o sistema retornará valores que representa as dimensões após o redimensionamento. Por exemplo, um bitmap projetado para 50 x 50 pixels para uma tela mdpi é dimensionada para 75 x 75 pixels em uma tela hdpi (se não houver um recurso alternativo) para hdpi). O sistema informa o tamanho dessa forma.

    Existem algumas situações em que você pode não querer que o Android pré-dimensione de um recurso. A maneira mais fácil de evitar o pré-escalonamento é colocar o recurso em um diretório de recursos com o qualificador de configuração nodpi. Exemplo:

    res/drawable-nodpi/icon.png

    Quando o sistema usa o bitmap icon.png dessa pasta, ele não o dimensiona com base na densidade atual do dispositivo.

  2. Escalonamento automático de dimensões e coordenadas em pixels

    Para desativar as dimensões e imagens de pré-dimensionamento, defina android:anyDensity como "false" no manifesto ou de forma programática para um Bitmap definindo inScaled como "false". Em neste caso, o sistema automaticamente dimensiona todas as coordenadas absolutas de pixel e valores de dimensão no momento do desenho. Isso garante que os pixels definidos os elementos da tela ainda são exibidos com aproximadamente o mesmo tamanho físico para que eles possam ser exibidos com a densidade de pixel básica (mdpi). O sistema gerencia esse dimensionamento de forma transparente para o aplicativo e informa o pixel dimensionado dimensões de pixels ao aplicativo, em vez de dimensões físicas em pixels.

    Por exemplo, suponha que um dispositivo tenha uma tela WVGA de alta densidade, de 480 x 800 mesmo tamanho que uma tela HVGA tradicional, mas está executando um aplicativo desativado pré-escalonamento. Nesse caso, o sistema "mentira" para o app quando ele consultar dimensões e relatórios de 320 x 533, a conversão aproximada em mdpi para a densidade de pixels.

    Então, quando o aplicativo realiza operações de desenho, como invalidar um retângulo de (10,10) a (100, 100), o sistema transforma as coordenadas dimensionando-as para o valor adequado e, invalida a região (15,15) para (150, 150). Essa discrepância pode causar um comportamento inesperado se seu app manipula diretamente o bitmap dimensionado, mas isso é considerado uma para garantir o melhor desempenho possível do app. Se você encontrar esse situação, leia Converter unidades dp em pixel de unidade de medida.

    Normalmente, você não desativa o pré-dimensionamento. A melhor maneira de dar suporte a vários é seguir as técnicas básicas descritas nesta página.

Se o app manipular bitmaps ou interagir diretamente com pixels na tela de outra forma, pode ser necessário tomar medidas adicionais para dar suporte a diferentes densidades de pixels ideais. Por exemplo, se você responder a gestos de toque contando os número de pixels cruzados por um dedo, você precisa usar o método valores de pixel de densidade independente, em vez de pixels reais, mas você pode fazer a conversão entre valores dp e px.

Testar em todas as densidades de pixel

Testar seu app em vários dispositivos com pixels diferentes densidades diferentes para que você possa garantir que a interface seja dimensionada corretamente. O teste em um dispositivo físico dispositivo, quando possível. use a plataforma Emulador, se você não tiver acesso a recursos dispositivos para todas as densidades de pixel diferentes.

Se você quiser testar em dispositivos físicos, mas Se você não quiser comprar os dispositivos, poderá usar o Firebase Test Lab para acessar dispositivos em um data center do Google.