O que é unidade de medida dp
dp significa density-independent pixel. É uma unidade de medida virtual usada principalmente no ecossistema Android para dimensionar interfaces de usuário, mas também aparece em outras ferramentas de design que precisam lidar com resoluções diferentes. O conceito básico é simples: um dp corresponde sempre ao mesmo tamanho físico independente da densidade de pixels do dispositivo. Isso resolve o problema histórico de telas com densidades variadas, onde 1px físico poderia ser enorme num telefone antigo e invisível num monitor de alta resolução. A conta por trás disso é baseada numa densidade de referência de 160dpi, chamada mdpi. Se o seu dispositivo tem 320dpi, cada dp vira 2px físicos. Se tem 480dpi, cada dp vira 3px. A fórmula é dp = px / (dpi / 160). Esse fator de escala é o que as equipes chamam de density scale factor, ou simplesmente density scaling.
Conversão prática de unidade de medida dp
Na prática você raramente faz essa conta na mão. O Android converte automaticamente quando você define um valor em dp no XML de layout. A classe Resources do sistema aplica o fator de escala baseado no display configuration atual. Mas existem situações em que a conversão manual é necessária, como quando você programa o tamanho de uma view por código ou trabalha com bibliotecas de terceiros que exigem px brutos. Em Kotlin, a conversão de dp para px fica assim:
val px = dp * resources.displayMetrics.density Para o caminho inverso, de px para dp:
val dp = px / resources.displayMetrics.density O campo density é um float, então fique atento a arredondamentos se estiver trabalhando com dimensões muito pequenas. Em layouts de 8dp de grid, o erro acumula de forma visível depois de beberapa telas.
Eu costumava usar uma extensão rápida em Kotlin que recebe um Int em dp e devolve um Int em px já arredondado: fun Int.dpToPx(context: Context): Int = (this * context.resources.displayMetrics.density + 0.5f).toInt()
O +0.5f antes do toInt() evita aquele viés de truncamento para baixo que causa bordas serrilhadas em divisores de 1dp em dispositivos mdpi puros.
Por que usar dp em vez de px direto
Usar px fixos em layouts Android é uma armadilha clássica. Um botão de 200px de largura ocupa metade da tela num Galaxy S4 antigo e menos de um quarto num Galaxy S23. Com dp, o botão mantém o mesmo tamanho percebido no mundo real, aproximadamente 3.5cm, independentemente do dispositivo. Isso não significa que o layout se comporte idêntico em todos os tamanhos de tela, mas pelo menos a densidade visual não distorce tudo. Existe uma confusão comum entre dp, sp e pt que vale esclarecer. sp é o sibling do dp e serve especificamente para tipografia. Ele herda o mesmo sistema de escala de densidade, mas ainda respeita a preferência de tamanho de fonte do usuário nas configurações do sistema. Se você usar dp para texto, seu app viola as diretrizes de acessibilidade e pode ser reprovado em revisões de qualidade ou simplesmente rejeitado em lojas com filtros de conformidade. Use sp para fontes, dp para tudo o mais.
Outra confusão frequente é pensar que dp é um submúltiplo de polegada. Na verdade, a relação é diferente. 1dp equivale a 1/160 de uma polegada física somente na densidade de referência de 160dpi. Em telas mais densas, 1dp ainda representa a mesma fração de polegada, mas isso não se traduz em menos pixels físicos, e sim em mais pixels empacotados numa área menor. A lógica é a oposta do que muitos assumem inicialmente.
Problemas reais que aparecem no dia a dia
Eu já perdi meio dia num projeto porque uma biblioteca de gráfico vetorial internamente multiplicava valores em dp por 1.0f, como se já estivessem em px. O resultado era uma tabela com elementos renderizados com o tamanho em pixels num dispositivo mdpi e completamente achatados num dispositivo xhdpi. A solução foi inspecionar o código-fonte da biblioteca, encontrar o ponto de conversão ausente e aplicar uma chamada explícita de dp para px antes de passar o parâmetro. Um problema mais sutil envolve a borda de 1dp em views com background Drawable personalizado. Em telas mdpi, 1dp é exatamente 1px físico, o que funciona bem. Em telas hdpi ou superior, 1dp vira 2px físicos, e dependendo de como o Ripple ou o StateListDrawable é desenhado, pode aparecer um efeito de borda duplicada ou um leve halo. A correção que eu adotei foi criar um ShapeDrawable com stroke de 0.5dp em telas de alta densidade usando resource qualifiers, mapeando o drawable para drawable-hdpi, drawable-xhdpi etc., cada um com o stroke ajustado. Não é bonito, mas evita o artefato visual.
👉 Clique no botão abaixo para saber mais sobre o assunto!
Tem ainda o caso dos insets de sistema. Em dispositivos com notch, barra de navegação gestual ou tela curvada, o SafeAreaCalculator aplica margens que nem sempre são múltiplos inteiros de dp. Se o seu grid baseia-se em incrementos de 4dp ou 8dp, você pode terminar com uma célula de 3dp ou 5dp após a aplicação do inset. A correção prática é arredondar os insets para o múltiplo mais próximo do seu grid antes de aplicar as restrições de layout.
Limitações e quando dp não resolve
dp não é bala de prata. Ele normaliza a densidade de pixels, mas não normaliza o tamanho da tela. Um tablet de 10 polegadas e um telefone de 6 polegadas compartilham a mesma escala dp, então os elementos ficam com o mesmo tamanho físico, o que pode deixar o tablet com aparência apertada ou o telefone com aparência enorme, dependendo do seu objetivo. Para isso existem os qualifiers de layout width, como layout-sw600dp, que permitem fornecer arquivos de layout diferentes para tablets e telefones. Outro ponto fraco é que dp ignora a distância de uso. Um aplicativo de leitura médica usado de perto precisa de elementos maiores do que um relógio de pulso, mesmo que ambos rodam em telas com a mesma densidade dp. Nesse cenário, alguns times adotam uma abordagem mista: dp para a maioria dos componentes e dimensionamento dinâmico baseado no tamanho da tela ou na classe do dispositivo via DisplayMetrics ou WindowMetricsCalculator. O Android moderno fornece o WindowMetricsCalculator API desde a API 30, que entrega métricas mais precisas do que o antigo DisplayMetrics em janelas com padding de sistema.
Existe ainda o problema de componentes de terceiros que exigem px brutos e não oferecem parâmetro em dp. Nesses casos, a conversão manual introduz dependência de contexto, e se você chamar a conversão antes do LayoutInflater terminar o attach, o displayMetrics pode ainda refletir a configuração anterior, resultando em tamanho errado por um frame. A mitigação é adiar a conversão para o método onWindowFocusChanged ou para o ViewTreeObserver.OnGlobalLayoutListener, onde as métricas já estão consolidadas.
Cenários onde dp funciona bem e onde falha
Para layouts de formulários, listas, cards e elementos de interface padrão, dp é a escolha certa. A cobertura do sistema é ampla, a conversão é automática e a previsibilidade é alta. Para animações de tamanho baseadas em fração da tela, dp é menos útil porque a fração da tela varia com o tamanho físico. Nesse caso, use ConstraintLayout com guidelines e constraints em percentual, ou ViewBinding com dimensions relativas. Para ícones de ação em toolbars, dp funciona, mas sp pode ser mais adequado se o ícone contém label textual. Botões de ação flutuantes seguem a recomendação oficial de 56dp de diâmetro, que é um valor fixo no Material Design, então use dp e não se preocupe com variação.
Em telas com overscan, como algumas TVs Android, dp pode ser enganoso porque a região visível é menor que a resolução declarada. O workaround mais comum é testar em hardware real ou usar um emulador com overlay de overscan e ajustar os margin tops e bottoms manualmente. Não existe configuração automática confiável para isso.
Boas práticas que economizam tempo
Defina dimensões padronizadas em dimens.xml usando dp. Evite valores fixos soltos no meio do layout. Grupos de 4dp ou 8dp funcionam bem para a maioria dos casos. Mantenha um arquivo de recursos por densidade apenas quando precisar de ajuste fino de stroke ou sombra, senão o overhead de manutenção cresce rápido. Teste em pelo menos três pontos de densidade: mdpi, hdpi e xhdpi. Se conseguir, adicione um dispositivo xxhdpi para verificar se algo quebrou. A diferença entre 2x e 3x é onde aparecem a maioria dos bugs de layout disfarçados de coisa pequena.
Se o projeto usa Jetpack Compose, a sintaxe muda. Em vez de dp como string no XML, você usa o valor direto com .dp no código Kotlin, e o compositor trata da conversão automaticamente. Ainda assim, a lógica por trás permanece a mesma, e os problemas de insets e classes de tela aparecem da mesma forma. A vantagem principal de dp é a previsibilidade em dispositivos com densidades conhecidas. A desvantagem é que ela não elimina a necessidade de testes em hardware diverso. Nenhum sistema de unidade substitui a verificação visual em dispositivos reais, especialmente quando se lida com bordas finas, sombras e componentes que mesclam dp com medidas absolutas vindas de bibliotecas externas.
Se você está começando um projeto novo, adotar dp como unidade padrão desde o início evita retrabalho. Migrar de px para dp depois que o layout já está espalhado por centenas de telas é uma das tarefas mais chatas que eu já fiz, e recomendo que ninguém passe por isso se puder evitar. O investimento inicial de revisar os XMLs cabe em algumas horas. A correção tardia custa dias.
Dicas de migração de px para dp
Se você já tem um projeto em px, o primeiro passo é identificar os valores fixos que mais causam inconsistência. Na minha experiência, 70% dos problemas aparecem em margens, paddings e alturas de linhas. Comece por aí. O resto, como animações e drawables, pode esperar. Use o Refactor do Android Studio para substituir atributos de dimensão de px para dp em lote. Há uma opção nativa nessa direção, mas ela só funciona bem quando os valores seguem padrões consistentes. Se o código mistura px hardcoded em strings, layout params no Kotlin e resources XML, a migração precisa ser manual mesmo. Organize primeiro os valores em um arquivo de dimensões centralizado, depois faça a troca.
Depois da migração, rode o teste de layout em telas virtuais com densidades mdpi, hdpi e xhdpi. Anote qualquer divergência. Se um botão parece maior num dispositivo específico, verifique se há um drawable com dimensão fixa ou se algum cálculo de layout usa px brutos que escaparam da conversão. Isso cobre o essencial sobre unidade de medida dp. Se precisar de detalhes mais específicos sobre um caso particular, como integração com bibliotecas de gráficos vetoriais ou ajuste de insets em telas com cutout irregular, o caminho é sempre o mesmo: inspecionar o código de conversão, testar em hardware real e ajustar conforme a densidade do dispositivo.