Como centralizar o drawable vetorial na lista de camadas sem escalar

102

Estou tentando usar a VectorDrawableem a LayerListsem dimensionar o vetor. Por exemplo:

<layer-list>
    <item android:drawable="@color/grid_item_activated"/>
    <item android:gravity="center" android:drawable="@drawable/ic_check_white_48dp"/>
</layer-list>

O ic_check_white_48dpid de drawable definido como:

<vector xmlns:android="http://schemas.android.com/apk/res/android"
        android:width="48dp"
        android:height="48dp"
        android:viewportWidth="24.0"
        android:viewportHeight="24.0">
    <path
        android:fillColor="#FFFFFFFF"
        android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z"/>
</vector>

O efeito desejado é que o ícone de verificação seja centralizado no drawable da camada, sem escala. O problema é que a lista de camadas acima faz com que o ícone de verificação seja dimensionado para caber no tamanho da camada.

I pode produzir o efeito desejado se eu substituir o vector amovível com PNGs para cada densidade e modificar a lista de camada tais como:

<layer-list>
    <item android:drawable="@color/grid_item_activated"/>
    <item>
        <bitmap android:gravity="center" android:src="@drawable/ic_check_white_48dp"/>
    </item>
</layer-list>

Existe alguma maneira de fazer isso usando um VectorDrawable?

Ashughes
fonte
11
Parece que isso pode ser simplesmente um bug na API 21/22. Acabei de testar em um dispositivo API 23 e o drawable vetorial foi centralizado corretamente.
ashughes de
1
Você deve responder à sua própria pergunta e aceitá-la. Assim fica mais visível e a pergunta não aparece como sem resposta.
Marcin Koziński
1
Não respondi minha pergunta porque ainda não tenho uma resposta sobre o que fazer na API 21/22. Minha solução temporária foi usar PNGs em vez de vetores. Esperando ainda encontrar uma maneira de fazer os vetores funcionarem.
ashughes de
1
Abri um problema aqui: code.google.com/p/android/issues/detail?id=219600
toobsco42
Isso já foi corrigido na API 23, então suponho que seu relatório de bug será apenas marcado como corrigido.
ashughes

Respostas:

29

Eu tive o mesmo problema ao tentar centralizar drawables de vetores em uma lista em camadas.

Eu tenho uma solução alternativa, não é exatamente o mesmo, mas funciona, você precisa definir um tamanho para todo o drawable e adicionar preenchimento ao item de vetor:

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item>
        <shape>
            <size android:height="120dp" android:width="120dp"/>
            <solid android:color="@color/grid_item_activated"/>
        </shape>
    </item>
    <item android:top="24dp"
          android:bottom="24dp"
          android:left="24dp"
          android:right="24dp"
          android:drawable="@drawable/ic_check_white_48dp"/>
</layer-list>

O tamanho da forma acima define o tamanho de todo o drawable, 120 dp neste exemplo, o preenchimento no segundo item, 24 dp neste exemplo, centraliza a imagem vetorial.

Não é o mesmo que usar o, gravity="center"mas é a maneira de trabalhar de usar vetores na API 21 e 22.

Nicolas Tyler
fonte
2
Isso parece exigir que você saiba o tamanho exato do drawable (a fim de definir o tamanho da forma), em vez de permitir que ele preencha todo o espaço definido com o drawable de cheque centralizado nele.
ashughes
1
é possível não saber o tamanho, mas você não pode alterar o preenchimento com este método, você pode colocar os drawables em cima uns dos outros em visualizações de imagens. Como afirmei acima, não há solução perfeita para um gravity="center"em API 21 e 22.
Nicolas Tyler
Não funciona quando usado com windowBackground. Os tamanhos explícitos não fazem nada neste caso. E também não funciona se eu colocar a lista de camadas dentro de outra lista de camadas.
Vsevolod Ganin
19

Tenho lutado com o mesmo problema. A única maneira que encontrei de corrigir isso e evitar que o drawable aumentasse a escala foi definir o tamanho do drawable e usar a gravidade para alinhá-lo:

<layer-list>
    <item android:drawable="@color/grid_item_activated"/>
     <item
        android:gravity="center"
        android:width="48dp"
        android:height="48dp"
        android:drawable="@drawable/ic_check_white_48dp"/>
</layer-list>

Espero que ajude!

Felipe duarte
fonte
34
Isso não ajuda na API 21-22, porque o widthatributo não está disponível nesses níveis de API. Na API 23, eles não são necessários, porque o bug foi corrigido e o drawable não é mais alongado.
WonderCsabo
3
Isso não funciona corretamente para API 27, a imagem é esticada para tela
inteira
8

Solução editada que fará com que seu SplashScreen tenha uma ótima aparência em todas as APIs, incluindo API21 a API23

Antes de mais nada leia este artigo e siga a BOA maneira de fazer uma tela inicial.

Se o seu logotipo estiver distorcido ou não couber e você está direcionando apenas para APIs24 +, você pode simplesmente reduzir seu drawable vetorial diretamente em seu arquivo xml assim:

<vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt"
android:viewportWidth="640"
android:viewportHeight="640"
android:width="240dp"
android:height="240dp">
<path
    android:pathData="M320.96 55.9L477.14 345L161.67 345L320.96 55.9Z"
    android:strokeColor="#292929"
    android:strokeWidth="24" />
</vector>

no código acima, estou redimensionando um drawable que desenhei em uma tela de 640x640 para 240x240. em seguida, coloco-o no drawable da tela inicial e funciona muito bem:

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" android:opacity="opaque"
android:paddingBottom="20dp" android:paddingRight="20dp" android:paddingLeft="20dp" android:paddingTop="20dp">

<!-- The background color, preferably the same as your normal theme -->
<item>
    <shape>
        <size android:height="120dp" android:width="120dp"/>
        <solid android:color="@android:color/white"/>
    </shape>
</item>

<!-- Your product logo - 144dp color version of your app icon -->
<item
    android:drawable="@drawable/logo_vect"
    android:gravity="center">

</item>
</layer-list>

meu código está, na verdade, apenas desenhando o triângulo na imagem na parte inferior, mas aqui você vê o que pode fazer com isso. A resolução finalmente é ótima, ao contrário das bordas pixeladas que recebia ao usar bitmap. portanto, use um drawable vetorial por todos os meios (há um site chamado vectr que usei para criar o meu sem o incômodo de baixar um software especializado).

EDITAR para que funcione também na API21-22-23

Embora a solução acima funcione para dispositivos que executam API24 +, fiquei muito desapontado depois de instalar meu aplicativo em um dispositivo que executam API22. Notei que a tela inicial estava novamente tentando preencher a visão inteira e parecendo uma merda. Depois de arrancar minhas sobrancelhas por meio dia, finalmente forcei brutalmente uma solução por pura força de vontade.

você precisa criar um segundo arquivo nomeado exatamente como o xml splashscreen (digamos splash_screen.xml) e colocá-lo em 2 pastas chamadas drawable-v22 e drawable-v21 que você criará na pasta res / (para vê-los você tem que mudar sua visão do projeto de Android para Projeto). Isso serve para instruir seu telefone a redirecionar para arquivos colocados nessas pastas sempre que o dispositivo relevante executar uma API correspondente ao sufixo -vXX na pasta drawable, consulte este link . coloque o seguinte código na lista de camadas do arquivo splash_screen.xml que você cria nestas pastas:

<item>
<shape>
    <size android:height="120dp" android:width="120dp"/>
    <solid android:color="@android:color/white"/>
</shape>
</item>

<!-- Your product logo - 144dp color version of your app icon -->
<item android:gravity="center">
    <bitmap android:gravity="center"
        android:src="logo_vect"/>

</item>

esta é a aparência das pastas

Por alguma razão para essas APIs, você precisa envolver seu drawable em um bitmap para fazê-lo funcionar e fazer com que o resultado final pareça o mesmo. O problema é que você deve usar a abordagem com as pastas adicionais de drawable, pois a segunda versão do arquivo splash_screen.xml fará com que sua tela inicial não seja exibida em dispositivos que executam APIs superiores a 23. Você também pode ter que colocar a primeira versão do splash_screen.xml em drawable-v24 como padrão do Android para a pasta drawable-vXX mais próxima que ele pode encontrar para recursos.

minha tela inicial

quealegriamasalegre
fonte
4

Atualmente, estou trabalhando em uma tela inicial com um drawable vetorial centralizado horizontal e verticalmente. Aqui está o que eu obtive no nível 25 da API:

<layer-list xmlns:android="http://schemas.android.com/apk/res/android" android:opacity="opaque">

<!-- background color, same as theme -->
<item android:drawable="@android:color/white"/>

<!-- app logo -->

<item
    android:drawable="@drawable/my_app_logo"
    android:gravity="center_horizontal|center_vertical"
    android:width="200dp"
    android:height="200dp"
    />
</layer-list>

Se você quiser ajustar a dimensão do drawable vetorial, modifique android:widtheandroid:height atributos acima, não necessariamente modifique o drawable original.

Além disso, minha experiência é NÃO coloque o drawable vetorial em uma tag de bitmap, o que nunca funciona para mim. Bitmap é para arquivos de formato .png, .jpg e .gif, não para drawable vetorial.

Referência

https://developer.android.com/guide/topics/resources/drawable-resource#LayerList

https://developer.android.com/guide/topics/resources/more-resources.html#Dimension

Wenhe Qi
fonte
1
Uma boa ideia sobre drawables vetoriais que precisam estar dentro de uma tag <item> em vez de uma tag <bitmap>.
FrostRocket