Android: mettre à l'échelle une image dessinée ou d'arrière-plan?

134

Sur une mise en page, je souhaite redimensionner l'image d'arrière-plan (en conservant son rapport hauteur / largeur) à l'espace alloué lors de la création de la page. Quelqu'un a-t-il une idée de la façon de procéder?

J'utilise layout.setBackgroundDrawable()et un BitmapDrawable()pour définir gravitypour le découpage et le remplissage, mais je ne vois aucune option pour la mise à l'échelle.

Finnmglas
la source
J'ai eu ce problème et j'ai suivi la recommandation de Dweebo avec le changement suggéré par Anke: changé fitXY en fitCenter. A bien fonctionné.

Réponses:

90

Pour personnaliser la mise à l'échelle de l'image d'arrière-plan, créez une ressource comme celle-ci:

<?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" />

Ensuite, il sera centré dans la vue s'il est utilisé comme arrière-plan. Il existe également d'autres drapeaux: http://developer.android.com/guide/topics/resources/drawable-resource.html

Aleks N.
la source
6
J'ai essayé d'utiliser le bitmap mais l'image est simplement centrée, pas mise à l'échelle comme Sam le demande. Lorsque j'utilise ImageView, cela fonctionne à merveille, pas de problème. (nb: je ne défile pas non plus dans mon cas.) Avec quoi est-ce que j'augmenterais le bitmap pour redimensionner l'image?
Joe D'Andrea
7
Les valeurs possibles sont: "top" | "bas" | "gauche" | "droit" | "center_vertical" | "fill_vertical" | "center_horizontal" | "fill_horizontal" | "centre" | "remplir" | "clip_vertical" | "clip_horizontal"
Aleks N.
30
aucun de ces paramètres ne met à l'échelle le bitmap, en gardant son rapport d'aspect, donc la réponse est fausse.
Malachiasz le
7
Cher Malachiasz, «centre» respecte le rapport hauteur / largeur. Mais cela ne réduira pas l'image. Ce qui signifie que c'est une fonction quelque peu dangereuse à utiliser. En fonction de la résolution de la cible, vous ne savez jamais à quelle échelle la chose sera exactement mise à l'échelle. Une autre solution à moitié folle de Google.
2
avez un moyen d'ajouter l'attribut scaleType?
ademar111190
57

Je n'ai pas essayé de faire exactement ce que vous voulez, mais vous pouvez mettre à l'échelle un ImageView à l'aide de android:scaleType="fitXY"
et il sera dimensionné pour s'adapter à la taille que vous donnez à ImageView.

Vous pouvez donc créer un FrameLayout pour votre mise en page, y placer ImageView, puis tout autre contenu dont vous avez besoin dans le FrameLayout avec un arrière-plan transparent.

<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
la source
1
Est-il possible de faire cela sur une surface. Sinon, puis-je afficher l'aperçu (pour une application d'enregistrement) à l'aide de FrameLayout?
Namratha
1
@dweebo: Avez-vous essayé ça? Cela ne semble pas fonctionner pour moi.
speedplane
3
Évité car l'utilisation de deux composants d'interface utilisateur au lieu d'un est une mauvaise idée, en particulier pour des composants aussi occupés que ListView. Vous aurez très probablement des problèmes lors du défilement. Solution appropriée: stackoverflow.com/a/9362168/145046
Aleks N.
J'ai essayé un scaleType de fitCenter et centerCrop (avec quelques variations du dessin / image pour différentes densités) et cela a fonctionné! Les deux types d'échelle avaient du sens pour moi - plus une préférence personnelle basée sur l'image, je suppose. Notez que j'ai utilisé la fusion au lieu de FrameLayout, et il n'y a pas de défilement dans mon cas particulier.
Joe D'Andrea
2
centerInside est correct, pas fitXY, car fitXY ne garde pas les proportions de l'image
Malachiasz
35

Il existe un moyen simple de le faire à partir du dessinable:

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>

Le seul inconvénient est que s'il n'y a pas assez d'espace, votre image ne sera pas entièrement affichée, mais elle sera coupée, je n'ai pas trouvé de moyen de le faire directement à partir d'un dessinable. Mais d'après les tests que j'ai effectués, cela fonctionne plutôt bien et ne coupe pas beaucoup l'image. Vous pourriez jouer plus avec les options de gravité.

Une autre façon sera de simplement créer une mise en page, où vous utiliserez un ImageViewet définissez le scaleTypesur fitCenter.

Ionut Negru
la source
2
Cette solution est définitivement ce que je cherchais depuis longtemps pour remplacer la propriété d'arrière-plan de mon thème et éviter d'échelle d'image différente lorsque la vue a des onglets ou non. Merci d'avoir partagé ça.
Bibu
1
Il montre une erreur:error: <item> must have a 'name' attribute.
Dmitry
thx @lonutNegru, +1 pour avoir sauvé ma journée :)
Ravi Vaniya
18

Pour conserver le rapport hauteur / largeur, vous devez utiliser android:scaleType=fitCenterou fitStartetc. L'utilisation fitXYne conservera pas le rapport hauteur / largeur original de l'image!

Notez que cela ne fonctionne que pour les images avec un srcattribut, pas pour l'image d'arrière-plan.

Anke
la source
18

Utilisez l'image comme arrière-plan dimensionné pour la mise en page:

<?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
la source
Merci! Fonctionne aussi android:scaleType="centerCrop".
CoolMind le
5

Lorsque vous définissez le Drawable d'un ImageView à l'aide de la setBackgroundDrawableméthode, l'image sera toujours mise à l'échelle. Les paramètres tels que adjustViewBoundsou différents ScaleTypesseront simplement ignorés. La seule solution pour conserver le rapport hauteur / largeur que j'ai trouvé est de redimensionner le ImageViewfichier après le chargement de votre dessin. Voici l'extrait de code que j'ai utilisé:

// 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);

Dans l'extrait de code ci-dessus se trouve bmp l'objet Bitmap qui doit être affiché et imageView est l' ImageViewobjet

Une chose importante à noter est le changement des paramètres de mise en page . Ceci est nécessaire car setMaxHeightet setMaxWidthne fera une différence que si la largeur et la hauteur sont définies pour envelopper le contenu, et non pour remplir le parent. Par contre, Fill parent est le paramètre souhaité au début, car sinon, containerWidthet containerHeightles deux auront des valeurs égales à 0. Ainsi, dans votre fichier de mise en page, vous aurez quelque chose comme ceci pour votre ImageView:

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

Ce n'est pas la solution la plus performante, mais comme quelqu'un l'a suggéré à la place de l'arrière-plan, vous pouvez créer FrameLayout ou RelativeLayout et utiliser ImageView comme pseudo arrière-plan - d'autres éléments seront positionnés simplement au-dessus:

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

Le problème avec ImageView est que seuls les scaleTypes disponibles sont: 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 )

et pour "redimensionner l'image d'arrière-plan (en conservant son rapport hauteur / largeur)" dans certains cas, lorsque vous voulez qu'une image remplisse tout l'écran (par exemple l'image d'arrière-plan) et que le rapport hauteur / largeur de l'écran est différent de celui de l'image, le scaleType nécessaire est gentil de TOP_CROP, car:

CENTER_CROP centre l'image mise à l'échelle au lieu d'aligner le bord supérieur sur le bord supérieur de la vue d'image et FIT_START s'adapte à la hauteur de l'écran et ne remplit pas la largeur. Et comme l'utilisateur Anke l'a remarqué, FIT_XY ne conserve pas les proportions.

Heureusement, quelqu'un a étendu ImageView pour prendre en charge 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

Maintenant, IMHO serait parfait si quelqu'un écrivait Drawable personnalisé qui redimensionnait l'image comme ça. Ensuite, il pourrait être utilisé comme paramètre d'arrière-plan.


Reflog suggère de mettre à l'échelle le dessinable avant de l'utiliser. Voici les instructions pour le faire: Java (Android): Comment mettre à l'échelle un dessinable sans Bitmap? Bien que cela présente un inconvénient, ce dessin / bitmap mis à l'échelle utilisera plus de RAM, tandis que la mise à l'échelle à la volée utilisée par ImageView ne nécessite pas plus de mémoire. L'avantage pourrait être une charge de processeur moindre.

Malachiasz
la source
Joli! Fait exactement ce que je cherchais: une image de fond pour remplir tout l'écran en augmentant la largeur proportionnellement et en recadrant le bas de l'image.
CMash du
4

Le code ci-dessous rend le bitmap parfaitement avec la même taille de l'image vue. Obtenez la hauteur et la largeur de l'image bitmap, puis calculez la nouvelle hauteur et la largeur à l'aide des paramètres d'imageview. Cela vous donne l'image requise avec le meilleur rapport hauteur / largeur.

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
la source
2

Ce que Dweebo a proposé fonctionne. Mais à mon humble avis, ce n'est pas nécessaire. Un fond dessinable s'échelonne bien tout seul. La vue doit avoir une largeur et une hauteur fixes, comme dans l'exemple suivant:

 < 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
la source
2
Je crois comprendre qu'un arrière-plan s'agrandit pour remplir une vue, mais ne rétrécit pas.
Edward Falk
1

Une option à essayer consiste à placer l'image dans le drawable-nodpidossier et à définir l'arrière-plan d'une mise en page sur l'ID de ressource pouvant être dessiné.

Cela fonctionne certainement avec la réduction, mais je n'ai pas testé avec la mise à l'échelle.

Sababado
la source
0

Kotlin:

Si vous aviez besoin de dessiner un bitmap dans une vue, mis à l'échelle à FIT.

Vous pouvez effectuer les calculs appropriés pour définir bm la hauteur égale au conteneur et ajuster la largeur, dans le cas où le rapport largeur / hauteur bm est inférieur au rapport largeur / hauteur du conteneur, ou l'inverse dans le scénario opposé.

Images:

Exemple d'image 1

Exemple d'image 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
la source
-4

vous devrez pré-mettre à l'échelle ce dessinable avant de l'utiliser comme arrière-plan

reflog
la source
Est-ce vraiment la seule option? N'existe-t-il aucun type de conteneur pour placer l'ImageView dans lequel peut spécifier la taille d'affichage?
GrkEngineer
J'ai également des problèmes avec SurfaceView
AZ_
3
Il n'y a rien comme l'ajustement d'aspect, le remplissage d'aspect, le haut à gauche, le haut, etc. comme sur iOS? Ça craint ...
Maciej Swic
Comme l'a dit @Aleksej, une réponse fausse.
apprenant
-5

Vous pouvez utiliser l'un des éléments suivants:

android: gravité = "fill_horizontal | clip_vertical"

Ou

android: gravité = "fill_vertical | clip_horizontal"

Eli Baynesay
la source