Os dispositivos Android não só têm diferentes tamanhos de tela, como celulares, tablets, TVs etc., mas também telas com diferentes tamanhos de pixel. Um dispositivo pode ter 160 pixels por polegada, enquanto outro cabe em 480 pixels no mesmo espaço. Se você não considerar essas variações em densidade de pixels, o sistema pode dimensionar as imagens, resultando em imagens desfocadas, ou elas podem aparecer no tamanho errado.
Esta página mostra como projetar seu app para oferecer suporte a diferentes densidades de pixel, usando unidades de medidas independentes de resolução e fornecendo recursos de bitmap alternativos para cada densidade de pixel.
Assista o vídeo a seguir para ter uma visão geral dessas técnicas.
Para saber mais sobre como criar recursos de ícone, consulte as diretrizes para í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 distintas, de modo que o mesmo número de pixels corresponde a diferentes tamanhos físicos em dispositivos diferentes.
Para preservar o tamanho visível da interface em telas com diferentes densidades, crie sua interface usando pixels de densidade independente (dp, na sigla em inglês) como unidade de medida. Um dp é uma unidade de pixel virtual aproximadamente igual a um pixel em uma tela de média densidade (160 dpi, ou a densidade "valor de referência"). O Android converte esse valor para o número adequado de pixels reais para cada densidade.
Considere os dois dispositivos na Figura 1. Uma visualização com 100 pixels de largura aparece muito maior no dispositivo à esquerda. Uma visualização definida como 100 dp de largura aparece do mesmo tamanho nas duas telas.
Ao definir tamanhos de texto, você pode usar pixels escalonáveis (sp) como suas unidades. Por padrão, a unidade de sp é do mesmo tamanho que um dp, mas é redimensionada com base no tamanho de texto preferido do usuário. Nunca usar 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 convertê-las em pixels. A conversão de unidades dp em pixels da tela é esta:
px = dp * (dpi / 160)
Observação:nunca fixe essa equação no código para calcular pixels. Em vez disso, use
TypedValue.applyDimension()
,
que converte muitos tipos de dimensões (dp, sp etc.) em pixels.
Imagine um app em que um gesto de rolagem ou rolagem rápida seja reconhecido
depois que o dedo do usuário tiver se movido pelo menos 16 pixels. Em uma tela
de referência, o dedo do usuário precisa mover 16 pixels
/ 160 dpi
, o que equivale a 2,5 mm (ou 1/10 de polegada), para
que o gesto seja reconhecido.
Em um dispositivo
com uma tela de alta densidade (240 dpi), o dedo do usuário precisa mover
16 pixels / 240 dpi
, o que
igual a 1,7 mm (ou 1/15 de polegada). A distância é muito mais curta e,
portanto, o app parece ser mais sensível para o usuário.
Para corrigir esse problema, expresse o limite de gestos no código em dp e o converta em pixels reais. Por 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 de dp em pixels de acordo com a densidade de pixels atual. Em uma tela de densidade média,
DisplayMetrics.density
equivale a
1,0 e em uma tela de alta densidade, ele equivale a 1,5. Em uma tela de densidade extra-alta,
ele equivale a 2,0 e em uma tela de densidade baixa, ele equivale a 0,75. Essa figura é
usada pelo TypedValue.applyDimension()
para
conseguir a contagem real de pixels para a tela atual.
Usar valores de configuração pré-dimensionados
Você pode usar a classe ViewConfiguration
para acessar distâncias,
velocidades e tempos comuns usados pelo sistema Android. Por exemplo, a
distância em pixels usada pelo framework como limite de rolagem pode ser obtida
com getScaledTouchSlop()
:
Kotlin
private val GESTURE_THRESHOLD_DP = ViewConfiguration.get(myContext).scaledTouchSlop
Java
private final int GESTURE_THRESHOLD_DP = ViewConfiguration.get(myContext).getScaledTouchSlop();
Os métodos em ViewConfiguration
que começam com o prefixo getScaled
retornam um valor em pixels exibido corretamente, independentemente da densidade
de pixels atual.
Preferir gráficos vetoriais
Uma alternativa à criação de várias versões específicas de densidade de uma imagem é criar apenas um gráfico vetorial. Gráficos vetoriais criam uma imagem usando XML para definir caminhos e cores, em vez de usar bitmaps de pixel. Dessa forma, gráficos vetoriais podem ser dimensionados para qualquer tamanho sem artefatos de escalonamento, embora geralmente sejam melhores para ilustrações como ícones, não fotos.
Os gráficos vetoriais geralmente são fornecidos como arquivos SVG (Elementos gráficos vetoriais escaláveis), mas o Android não é compatível com esse formato. Por isso, é necessário converter os arquivos SVG no formato de drawable vetorial do Android.
É possível converter um SVG em um drawable vetorial usando o Vector Asset Studio do Android Studio:
- Na janela Project, clique com o botão direito do mouse no diretório res e selecione New > Vector Asset.
- Selecione Local file (SVG, PSD).
Localize o arquivo que você quer importar e faça os ajustes necessários.
Talvez você note alguns erros na janela Asset Studio, indicando que os drawables vetoriais não são compatíveis com algumas propriedades do arquivo. Isso não impede a importação do arquivo, as propriedades sem suporte são ignoradas.
Clique em Next.
Na próxima tela, confirme o conjunto de origem em que você quer o arquivo no projeto e clique em Finish.
Como um drawable vetorial pode ser usado em todas as densidades de pixel, esse arquivo vai entrar no diretório de drawables padrão, conforme mostrado na hierarquia abaixo. Não é necessário usar diretórios específicos de densidade.
res/ drawable/ ic_android_launcher.xml
Para mais informações sobre como criar gráficos vetoriais, leia a documentação sobre drawables vetoriais.
Fornecer bitmaps alternativos
Para oferecer uma boa qualidade gráfica em dispositivos com densidades de pixel diferentes, forneça várias versões de cada bitmap no app, uma para cada bucket de densidade, com uma resolução correspondente. Caso contrário, o Android vai precisar dimensionar o bitmap para que ele ocupe o mesmo espaço visível em cada tela, resultando em artefatos de dimensionamento, como desfoque.
Há vários intervalos de densidade disponíveis para uso nos seus apps. A Tabela 1 descreve os diferentes qualificadores de configuração disponíveis e a quais tipos de tela eles se aplicam.
Qualificador de densidade | Descrição |
---|---|
ldpi |
Recursos para telas de baixa densidade (ldpi) (aproximadamente 120 dpi). |
mdpi |
Recursos para telas de média densidade (mdpi) (aproximadamente 160 dpi). Essa é a densidade de referência. |
hdpi |
Recursos para telas de alta densidade (hdpi) (aproximadamente 240 dpi). |
xhdpi |
Recursos para telas de densidade extra-alta (xhdpi) (aproximadamente 320 dpi). |
xxhdpi |
Recursos para telas de densidade extra-extra-alta (xxhdpi) (aproximadamente 480 dpi). |
xxxhdpi |
Recursos para usos de densidade extra-extra-extra-alta (xxxhdpi) (aproximadamente 640 dpi). |
nodpi |
Recursos para todas as densidades. Esses são recursos independentes de densidade. O sistema não dimensiona recursos marcados com esse qualificador, independente da densidade da tela atual. |
tvdpi |
Recursos para telas entre mdpi e hdpi, com aproximadamente
213 dpi. Não é considerado um grupo de densidade "principal".' É destinado principalmente
para televisões e a maioria dos apps não precisa dele. Fornecer recursos mdpi e hdpi
é suficiente para a maioria dos apps, e o sistema os dimensiona conforme
adequado. Se for necessário fornecer recursos tvdpi ,
dimensione-os a um fator de 1,33 * mdpi. Por exemplo, uma imagem de 100 x 100 pixels para
telas mdpi é de 133 x 133 pixels para tvdpi. |
Para criar drawables de bitmap alternativos para diferentes densidades, siga a proporção de dimensionamento 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 vão ser:
- 36 x 36 (0,75 x) para baixa densidade (ldpi)
- 48 x 48 (1,0x linha de base) 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)
Coloque 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ê referenciar @drawable/awesomeimage
,
o sistema vai selecionar o bitmap apropriado com base no DPI da tela. Se você
não fornecer um recurso específico de densidade para essa densidade, o sistema vai localizar
a melhor correspondência e dimensioná-la para caber na tela.
Dica:se você tem recursos drawable
que não quer que o sistema dimensione, como ao executar
alguns ajustes na imagem durante a execução, coloque-os em um
diretório com o qualificador de configuração nodpi
.
Recursos com esse qualificador são considerados independentes de densidade e
o sistema não os dimensiona.
Para saber mais sobre outros qualificadores de configuração e como o Android seleciona os recursos adequados para a configuração de tela atual, consulte 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 de densidade do ícone do app. No entanto, algumas telas de início exibem o ícone do app em até 25% maior do que o exigido pelo bucket de densidade do dispositivo.
Por exemplo, se o bucket de densidade de um dispositivo for xxhdpi e o maior ícone de app que você
fornecer estiver em drawable-xxhdpi
, a tela de início vai dimensionar esse ícone,
fazendo com que ele pareça menos nítido.
Para evitar isso, coloque todos
os ícones do app nos diretórios mipmap
em vez de drawable
. Ao contrário
dos diretórios drawable
, todos os diretórios mipmap
são mantidos no APK, mesmo
que você crie APKs específicos para densidades. Isso permite que os apps da tela de início escolham o melhor
ícone de resolução para exibir 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 um
ícone na tela de início de densidade mais alta no diretório mipmap-xxxhdpi
.
Para orientações sobre criação de ícones, consulte Ícones do sistema.
Para receber ajuda com a criação de ícones, consulte Criar ícones com o Image Asset Studio.
Orientações para problemas de densidade incomuns
Esta seção descreve como o Android executa o dimensionamento para bitmaps em diferentes densidades de pixel e como você pode ter mais controle sobre como os bitmaps são renderizados em diferentes densidades. A menos que seu app manipule gráficos ou que você tenha 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 tempo de execução, você precisa saber como o sistema ajuda a garantir o dimensionamento adequado para bitmaps. Isso é feito das seguintes maneiras:
- Pré-dimensionamento de recursos, como drawables de bitmap
Com base na densidade da tela atual, o sistema usa qualquer recurso específico de densidade do seu app. Se os recursos não estiverem disponíveis na densidade correta, o sistema carregará os recursos padrão e os dimensionará conforme necessário. O sistema presume que os recursos padrão (os de um diretório sem qualificadores de configuração) foram projetados para a densidade de pixel de referência (mdpi) e redimensiona esses bitmaps para o tamanho apropriado da densidade de pixel atual.
Se você solicitar as dimensões de um recurso pré-dimensionado, o sistema retornará valores que representam as dimensões após o dimensionamento. Por exemplo, um bitmap projetado a 50 x 50 pixels para uma tela mdpi é dimensionado para 75 x 75 pixels em uma tela hdpi (se não houver um recurso alternativo para hdpi), e o sistema vai informar o tamanho dessa forma.
Há algumas situações em que você pode não querer que o Android pré-dimensione um recurso. A maneira mais fácil de evitar o pré-dimensionamento é colocar o recurso em um diretório de recursos com o qualificador de configuração
nodpi
. Por 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. - Escalonamento automático de dimensões e coordenadas de pixel
É possível desativar dimensões e imagens de pré-dimensionamento definindo
android:anyDensity
como"false"
no manifesto ou programaticamente para umBitmap
definindoinScaled
como"false"
. Nesse caso, o sistema faz o escalonamento automático de todas as coordenadas absolutas e dos valores de dimensão de pixel no tempo de desenho. Isso garante que os elementos da tela definidos em pixels ainda sejam mostrados no mesmo tamanho físico aproximado que eles aparecem na densidade de pixel básica (mdpi). O sistema processa esse dimensionamento de forma transparente e informa as dimensões ajustadas em pixels ao app em vez das dimensões físicas em pixels.Por exemplo, suponha que um dispositivo tenha uma tela WVGA de alta densidade, que tenha 480 x 800 e aproximadamente o mesmo tamanho de uma tela HVGA tradicional, mas que esteja executando um app que tenha desativado o pré-dimensionamento. Nesse caso, o sistema "está" no app quando ele consulta as dimensões da tela e informa 320 x 533, que é a tradução em mdpi aproximada para a densidade de pixels.
Depois, quando o app 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, na verdade, invalida a região (15,15) para (150, 150). Essa discrepância pode causar um comportamento inesperado se o app manipular diretamente o bitmap dimensionado, mas isso é considerado uma troca razoável para garantir o melhor desempenho possível. Se você se deparar com essa situação, leia Converter unidades de dp em unidades de pixel.
Normalmente, você não desativa o pré-escalonamento. A melhor maneira de oferecer suporte a várias telas é seguir as técnicas básicas descritas nesta página.
Caso seu app manipule bitmaps ou interaja diretamente com pixels na tela de outra forma, pode ser necessário seguir outras etapas para oferecer suporte a diferentes densidades de pixel. Por exemplo, se você responder a gestos de toque contando o número de pixels que um dedo cruza, será necessário usar os valores de pixels de densidade independente adequados, em vez de pixels reais, mas você pode converter entre valores dp e px.
Testar em todas as densidades de pixel
Teste seu app em vários dispositivos com diferentes densidades de pixel para garantir que a interface seja dimensionada corretamente. Faça testes em um dispositivo físico quando possível. Use o Android Emulator se você não tiver acesso a dispositivos físicos para todas as densidades de pixel diferentes.
Se você quiser testar em dispositivos físicos, mas não quiser comprar dispositivos, use o Firebase Test Lab para acessar dispositivos em um data center do Google.