Android: Dimensionar uma imagem do Drawable ou de fundo?


134

Em um layout, quero dimensionar a imagem de plano de fundo (mantendo sua proporção) para o espaço alocado quando a página é criada. Alguém tem uma idéia de como fazer isso?

Estou usando layout.setBackgroundDrawable()e um BitmapDrawable()para definir gravitypara recorte e preenchimento, mas não vejo nenhuma opção para dimensionamento.


Eu tive esse problema e segui a recomendação da Dweebo com a alteração sugerida pela Anke: mudou o fitXY para o fitCenter. Funcionou bem.

Respostas:


90

Para personalizar o dimensionamento da imagem de fundo, crie um recurso como este:

<?xml version="1.0" encoding="utf-8"?>
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
    android:gravity="center"
    android:src="@drawable/list_bkgnd" />

Em seguida, será centralizado na vista, se usado como plano de fundo. Também existem outros sinalizadores: http://developer.android.com/guide/topics/resources/drawable-resource.html


6
Tentei usar bitmap, mas a imagem é simplesmente centralizada, não dimensionada, como Sam está perguntando. Quando uso o ImageView, ele funciona lindamente, sem confusão. (nb: também não estou rolando no meu caso.) Com o que eu aumentaria o bitmap para dimensionar a imagem?
Joe D'Andrea

7
Os valores possíveis são: "top" | "fundo" | "esquerda" | "certo" | "center_vertical" | "fill_vertical" | "center_horizontal" | "fill_horizontal" | "centro" | "preencher" | "clip_vertical" | "clip_horizontal"
Aleks N.

30
nenhum desses parâmetros dimensiona o bitmap, mantendo sua proporção, então a resposta está errada.
Malachiasz 31/01

7
Caro Malachiasz, 'centro' respeita a proporção. Mas não reduzirá a imagem. O que significa que é um recurso um pouco perigoso de usar. Dependendo da resolução do alvo, você nunca sabe como a coisa será dimensionada exatamente. Outra solução meia-boca do Google.

2
tem uma maneira de adicionar o atributo scaleType?
ademar111190

57

Você ainda não tentou fazer exatamente o que deseja, mas pode dimensionar um ImageView usando android:scaleType="fitXY"
e ele será dimensionado para caber no tamanho que você der ao ImageView.

Assim, você pode criar um FrameLayout para o seu layout, colocar o ImageView dentro dele e, em seguida, qualquer outro conteúdo necessário no FrameLayout, além de um fundo transparente.

<FrameLayout  
android:layout_width="fill_parent" android:layout_height="fill_parent">

  <ImageView 
android:layout_width="fill_parent" android:layout_height="fill_parent"
android:src="@drawable/back" android:scaleType="fitXY" />

  <LinearLayout>your views</LinearLayout>
</FrameLayout>

1
É possível fazer isso em uma superfície. Caso contrário, posso mostrar a visualização (para um aplicativo de gravador) usando o FrameLayout?
Namratha

1
@weweebo: Você já tentou isso? Não parece estar funcionando para mim.
speedplane

3
A votação foi reduzida, pois o uso de dois componentes da interface do usuário em vez de um é uma má idéia, especialmente para componentes ocupados como o ListView. Provavelmente você terá problemas ao rolar a tela. Solução adequada: stackoverflow.com/a/9362168/145046
Aleks N.

Eu tentei um scaleType de fitCenter e centerCrop (junto com algumas variações da imagem / desenho para diferentes densidades) e funcionou! Qualquer tipo de escala fazia sentido para mim - mais uma preferência pessoal baseada na imagem, eu suspeito. Observe que eu usei mesclagem em vez de FrameLayout e não há rolagem no meu caso em particular.
Joe D'Andrea

2
centerInside é adequado, não fitXY, pois o fitXY não mantém a proporção da imagem #
Malachiasz

35

Existe uma maneira fácil de fazer isso no drawable:

your_drawable.xml

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >

    <item android:drawable="@color/bg_color"/>
    <item>
        <bitmap
            android:gravity="center|bottom|clip_vertical"
            android:src="@drawable/your_image" />
    </item>

</layer-list>

A única desvantagem é que, se não houver espaço suficiente, sua imagem não será totalmente exibida, mas será cortada, não encontrei uma maneira de fazer isso diretamente de um drawable. Mas a partir dos testes que fiz, ele funciona muito bem e não captura muito da imagem. Você pode jogar mais com as opções de gravidade.

Outra maneira será apenas criar um layout, onde você usará um ImageViewe definirá scaleTypecomo fitCenter.


2
Esta solução é definitivamente o que eu procurava há muito tempo para substituir a propriedade de segundo plano do meu tema e evitar a escala da imagem de maneira diferente quando a exibição tem guias ou não. Obrigado por compartilhar isso.
bibu

1
Ele mostra o erro:error: <item> must have a 'name' attribute.
Dmitry

thx @lonutNegru, +1 para salvar o meu dia :)
Ravi Vaniya 27/07/07

18

Para manter a proporção, você deve usar android:scaleType=fitCenterou fitStartetc. O uso fitXYnão mantém a proporção original da imagem!

Observe que isso funciona apenas para imagens com um srcatributo, não para a imagem de fundo.


18

Use a imagem como tamanho de fundo para o layout:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" >

    <ImageView
        android:id="@+id/imgPlaylistItemBg"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:adjustViewBounds="true"
        android:maxHeight="0dp"
        android:scaleType="fitXY"
        android:src="@drawable/img_dsh" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical" >


    </LinearLayout>

</FrameLayout>

Obrigado! Também android:scaleType="centerCrop"funciona.
CoolMind

5

Quando você define o Drawable de um ImageView usando o setBackgroundDrawablemétodo, a imagem sempre será dimensionada. Parâmetros como adjustViewBoundsou diferentes ScaleTypesserão apenas ignorados. A única solução para manter a proporção que eu encontrei é redimensionar o ImageViewdepois de carregar o seu drawable. Aqui está o trecho de código que eu usei:

// bmp is your Bitmap object
int imgHeight = bmp.getHeight();
int imgWidth = bmp.getWidth();
int containerHeight = imageView.getHeight();
int containerWidth = imageView.getWidth();
boolean ch2cw = containerHeight > containerWidth;
float h2w = (float) imgHeight / (float) imgWidth;
float newContainerHeight, newContainerWidth;

if (h2w > 1) {
    // height is greater than width
    if (ch2cw) {
        newContainerWidth = (float) containerWidth;
        newContainerHeight = newContainerWidth * h2w;
    } else {
        newContainerHeight = (float) containerHeight;
        newContainerWidth = newContainerHeight / h2w;
    }
} else {
    // width is greater than height
    if (ch2cw) {
        newContainerWidth = (float) containerWidth;
        newContainerHeight = newContainerWidth / h2w; 
    } else {
        newContainerWidth = (float) containerHeight;
        newContainerHeight = newContainerWidth * h2w;       
    }
}
Bitmap copy = Bitmap.createScaledBitmap(bmp, (int) newContainerWidth, (int) newContainerHeight, false);
imageView.setBackgroundDrawable(new BitmapDrawable(copy));
LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
imageView.setLayoutParams(params);
imageView.setMaxHeight((int) newContainerHeight);
imageView.setMaxWidth((int) newContainerWidth);

No trecho de código acima, bmp é o objeto Bitmap a ser mostrado e imageView é o ImageViewobjeto.

Uma coisa importante a ser observada é a alteração dos parâmetros de layout . Isso é necessário porque setMaxHeighte setMaxWidthsó fará diferença se a largura e a altura estiverem definidas para quebrar o conteúdo e não para preencher o pai. Fill pai, por outro lado é a configuração desejada no início, porque de outra maneira containerWidthe containerHeightambos têm valores iguais a 0. Assim, no seu arquivo de layout você terá algo parecido com isso para o seu ImageView:

...
<ImageView android:id="@+id/my_image_view"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"/>
...

4

Essa não é a solução com melhor desempenho, mas como sugerido por alguém em vez de segundo plano, você pode criar FrameLayout ou RelativeLayout e usar o ImageView como pseudo segundo plano - outros elementos estarão posicionados simplesmente acima:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_height="match_parent"
    android:layout_width="match_parent">

    <ImageView
        android:id="@+id/ivBackground"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:scaleType="fitStart"
        android:src="@drawable/menu_icon_exit" />

    <Button
        android:id="@+id/bSomeButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:layout_marginLeft="61dp"
        android:layout_marginTop="122dp"
        android:text="Button" />

</RelativeLayout>

O problema do ImageView é que apenas os scaleTypes disponíveis são: CENTER, CENTER_CROP, CENTER_INSIDE, FIT_CENTER, FIT_END, FIT_START, FIT_XY, MATRIX ( http://etcodehome.blogspot.de/2011/05/android-imageview-scaletype-samples.html )

e "redimensionar a imagem de fundo (mantendo sua proporção)" em alguns casos, quando você deseja que uma imagem preencha a tela inteira (por exemplo, imagem de fundo) e a proporção da tela seja diferente da imagem, o scaleType necessário é do tipo de TOP_CROP, porque:

CENTER_CROP centraliza a imagem em escala em vez de alinhar a borda superior com a borda superior da visualização da imagem e FIT_START se ajusta à altura da tela e não preenche a largura. E como o usuário Anke notou, o FIT_XY não mantém a proporção.

Felizmente, alguém estendeu o ImageView para oferecer suporte a TOP_CROP

public class ImageViewScaleTypeTopCrop extends ImageView {
    public ImageViewScaleTypeTopCrop(Context context) {
        super(context);
        setup();
    }

    public ImageViewScaleTypeTopCrop(Context context, AttributeSet attrs) {
        super(context, attrs);
        setup();
    }

    public ImageViewScaleTypeTopCrop(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        setup();
    }

    private void setup() {
        setScaleType(ScaleType.MATRIX);
    }

    @Override
    protected boolean setFrame(int frameLeft, int frameTop, int frameRight, int frameBottom) {

        float frameWidth = frameRight - frameLeft;
        float frameHeight = frameBottom - frameTop;

        if (getDrawable() != null) {

            Matrix matrix = getImageMatrix();
            float scaleFactor, scaleFactorWidth, scaleFactorHeight;

            scaleFactorWidth = (float) frameWidth / (float) getDrawable().getIntrinsicWidth();
            scaleFactorHeight = (float) frameHeight / (float) getDrawable().getIntrinsicHeight();

            if (scaleFactorHeight > scaleFactorWidth) {
                scaleFactor = scaleFactorHeight;
            } else {
                scaleFactor = scaleFactorWidth;
            }

            matrix.setScale(scaleFactor, scaleFactor, 0, 0);
            setImageMatrix(matrix);
        }

        return super.setFrame(frameLeft, frameTop, frameRight, frameBottom);
    }

}

https://stackoverflow.com/a/14815588/2075875

Agora, o IMHO seria perfeito se alguém escrevesse o Drawable personalizado, que dimensiona uma imagem assim. Então poderia ser usado como parâmetro de fundo.


O novo registro sugere pré-escala do drawable antes de usá-lo. Aqui estão as instruções de como fazê-lo: Java (Android): Como dimensionar um drawable sem Bitmap? Embora tenha desvantagem, esse drawable / bitmap com escala superior usará mais RAM, enquanto o dimensionamento instantâneo usado pelo ImageView não requer mais memória. A vantagem poderia ser menos carga do processador.


Agradável! Faz exatamente o que eu estava procurando: imagem de fundo para preencher a tela inteira, aumentando a largura proporcionalmente e cortando a parte inferior da imagem.
CMash

4

O código Abaixo cria o bitmap perfeitamente com o mesmo tamanho da visualização de imagem. Obtenha a altura e a largura da imagem de bitmap e calcule a nova altura e largura com a ajuda dos parâmetros do imageview. Isso fornece a imagem necessária com a melhor proporção.

int bwidth=bitMap1.getWidth();
int bheight=bitMap1.getHeight();
int swidth=imageView_location.getWidth();
int sheight=imageView_location.getHeight();
new_width=swidth;
new_height = (int) Math.floor((double) bheight *( (double) new_width / (double) bwidth));
Bitmap newbitMap = Bitmap.createScaledBitmap(bitMap1,new_width,new_height, true);
imageView_location.setImageBitmap(newbitMap)

2

O que Dweebo propôs funciona. Mas, na minha humilde opinião, é desnecessário. Um drawable de fundo é bem dimensionado por si só. A vista deve ter largura e altura fixas, como no exemplo a seguir:

 < RelativeLayout 
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="@android:color/black">

    <LinearLayout
        android:layout_width="500dip"
        android:layout_height="450dip"
        android:layout_centerInParent="true"
        android:background="@drawable/my_drawable"
        android:orientation="vertical"
        android:padding="30dip"
        >
        ...
     </LinearLayout>
 < / RelativeLayout>

2
Meu entendimento é que um plano de fundo será expandido para preencher uma visualização, mas não diminuirá.
Edward Falk

1

Uma opção para tentar é colocar a imagem na drawable-nodpipasta e definir o plano de fundo de um layout para o ID do recurso desenhável.

Definitivamente, isso funciona com a redução, mas ainda não testei com a redução.


0

Kotlin:

Se você precisava desenhar um bitmap em uma Visualização, dimensionado para FIT.

Você pode fazer os cálculos adequados para definir bm a altura igual ao contêiner e ajustar a largura; no caso, a proporção largura / altura bm é menor que a proporção largura / altura do contêiner ou o inverso no cenário oposto.

Imagens:

Exemplo de imagem 1

Imagem de exemplo 2

// binding.fragPhotoEditDrawCont is the RelativeLayout where is your view
// bm is the Bitmap
val ch = binding.fragPhotoEditDrawCont.height
val cw = binding.fragPhotoEditDrawCont.width
val bh = bm.height
val bw = bm.width
val rc = cw.toFloat() / ch.toFloat()
val rb = bw.toFloat() / bh.toFloat()

if (rb < rc) {
    // Bitmap Width to Height ratio is less than Container ratio
    // Means, bitmap should pin top and bottom, and have some space on sides.
    //              _____          ___
    // container = |_____|   bm = |___| 
    val bmHeight = ch - 4 //4 for container border
    val bmWidth = rb * bmHeight //new width is bm_ratio * bm_height
    binding.fragPhotoEditDraw.layoutParams = RelativeLayout.LayoutParams(bmWidth.toInt(), bmHeight)
}
else {
    val bmWidth = cw - 4 //4 for container border
    val bmHeight = 1f/rb * cw
    binding.fragPhotoEditDraw.layoutParams = RelativeLayout.LayoutParams(bmWidth, bmHeight.toInt())
}

-4

você precisará pré-dimensionar esse drawable antes de usá-lo como plano de fundo


Esta é realmente a única opção? Não há nenhum tipo de contêiner para colocar o ImageView no qual possa especificar o tamanho da exibição?
GrkEngineer

Também estou tendo problemas com o SurfaceView
AZ_

3
Não há nada como ajuste de aspecto, preenchimento de aspecto, parte superior esquerda, parte superior etc, como no iOS? Isso é péssimo ..
Maciej Swic

Como @Aleksej disse, uma resposta falsa.
aluno

-5

Você pode usar um dos seguintes:

android: gravity = "fill_horizontal | clip_vertical"

Ou

android: gravity = "fill_vertical | clip_horizontal"

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.