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.

finnmglas
fonte
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

Aleks N.
fonte
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>
dweebo
fonte
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.

Ionut Negru
fonte
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.

Anke
fonte
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>
Konstantin Konopko
fonte
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"/>
...
maddob
fonte
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.

Malachiasz
fonte
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)
Naresh Sharma
fonte
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>
Yar
fonte
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.

Sababado
fonte
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())
}
danitinez
fonte
-4

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

reflog
fonte
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"

Eli Baynesay
fonte