J'essaie de définir une photo comme arrière-plan d'une Activity
. Je veux que l'arrière-plan soit une image en plein écran (sans bordure) }.
Comme je veux que l’image remplisse tout le fond de l’activité sans s’étirer ni se pincer (c’est-à-dire des redimensionnements non proportionnels pour X et Y) et que cela ne me dérange pas que la photo soit rognée, j’utilise une RelativeLayout
avec une ImageView
(avec Android:scaleType="centerCrop"
) et le reste de ma mise en page consistant en un ScrollView
et ses enfants.
<!-- Tried this with a FrameLayout as well... -->
<RelativeLayout>
<!-- Background -->
<ImageView
Android:layout_width="match_parent"
Android:layout_height="match_parent"
Android:scaleType="centerCrop"/>
<!-- Form -->
<LinearLayout
Android:layout_width="match_parent"
Android:layout_height="match_parent">
<ScrollView>
<LinearLayout>
...
<EditText/>
</LinearLayout>
</ScrollView>
</LinearLayout>
</RelativeLayout>
Le problème est que le reste de la mise en page comporte des vues EditText
et lorsque le clavier logiciel apparaît, ImageView est redimensionné. Je voudrais que le fond reste le même, peu importe si le clavier est visible ou non.
J'ai vu beaucoup de questions sur SO à propos de la redimensionnement d'ImageViews mais (imo) pas de réponses satisfaisantes. La plupart d'entre eux consistent simplement à définir le Android:windowSoftInputMode="adjustPan"
- ce qui n'est pas toujours pratique, surtout si vous souhaitez que l'utilisateur puisse faire défiler l'activité - ou à l'aide de getWindow().setBackgroundDrawable()
qui ne recadre pas l'image.
J'ai réussi à sous-classer ImageView et à remplacer son onMeasure()
(voir ma réponse ici: ImageView redimensionne lorsque le clavier est ouvert ) afin qu'il puisse forcer une hauteur et une largeur fixes - égales aux dimensions de l'écran du périphérique - selon à un drapeau, mais je me demande s’il existe un meilleur moyen d’obtenir le résultat souhaité.
Donc, pour résumer, ma question est la suivante: Comment puis-je définir le fond d’une activité pour qu’il s’agisse d’une photo en plein écran
avec scale type = "centerCrop", de sorte que la photo soit mise à l’échelle de manière uniforme (en conservant son rapport de format) et que les deux dimensions (largeur et hauteur) seront égales ou supérieures à la dimension correspondante de la vue;
cela ne sera pas redimensionné quand un clavier logiciel apparaît;
REPONSE:
J'ai fini par suivre les conseils de @ pskink et sous-classer BitmapDrawable
(voir sa réponse ci-dessous). J'ai dû faire quelques ajustements pour m'assurer que la BackgroundBitmapDrawable
est toujours réduite et recadrée de manière à remplir l'écran.
Voici mon dernier cours, adapté de sa réponse:
public class BackgroundBitmapDrawable extends BitmapDrawable {
private Matrix mMatrix = new Matrix();
private int moldHeight;
boolean simpleMapping = false;
public BackgroundBitmapDrawable(Resources res, Bitmap bitmap) {
super(res, bitmap);
}
@Override
protected void onBoundsChange(Rect bounds) {
if (bounds.height() > moldHeight) {
moldHeight = bounds.height();
Bitmap b = getBitmap();
RectF src = new RectF(0, 0, b.getWidth(), b.getHeight());
RectF dst;
if (simpleMapping) {
dst = new RectF(bounds);
mMatrix.setRectToRect(src, dst, ScaleToFit.CENTER);
} else {
// Full Screen Image -> Always scale and center-crop in order to fill the screen
float dwidth = src.width();
float dheight = src.height();
float vwidth = bounds.width();
float vheight = bounds.height();
float scale;
float dx = 0, dy = 0;
if (dwidth * vheight > vwidth * dheight) {
scale = (float) vheight / (float) dheight;
dx = (vwidth - dwidth * scale) * 0.5f;
} else {
scale = (float) vwidth / (float) dwidth;
dy = (vheight - dheight * scale) * 0.5f;
}
mMatrix.setScale(scale, scale);
mMatrix.postTranslate(dx, dy);
}
}
}
@Override
public void draw(Canvas canvas) {
canvas.drawColor(0xaa00ff00);
canvas.drawBitmap(getBitmap(), mMatrix, null);
}
}
Ensuite, il suffit de créer une BackgroundBitmapDrawable
et de la définir comme arrière-plan de la vue racine.
essayez ce LayerDrawable (res/drawable/backlayer.xml):
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:Android="http://schemas.Android.com/apk/res/Android"
>
<item>
<shape>
<solid Android:color="#f00" />
</shape>
</item>
<item>
<bitmap
Android:gravity="top" Android:src="@drawable/back1">
</bitmap>
</item>
</layer-list>
et réglez-le à votre disposition de niveau supérieur: Android: background = "@ drawable/backlayer"
UPDATE: essayez ce BitmapDrawadle, réglez-le sur la disposition de niveau supérieur (setBackgroundDrawable ()), si simpleMapping == true est suffisant, vous pouvez supprimer la branche "else":
class D extends BitmapDrawable {
private Matrix mMatrix = new Matrix();
private int moldHeight;
public D(Resources res, Bitmap bitmap) {
super(res, bitmap);
}
@Override
protected void onBoundsChange(Rect bounds) {
if (bounds.height() > moldHeight) {
moldHeight = bounds.height();
Bitmap b = getBitmap();
RectF src = new RectF(0, 0, b.getWidth(), b.getHeight());
RectF dst;
// if simpleMapping is good enough then remove "else" branch and
// declare "dst" as:
// RectF dst = new RectF(bounds);
boolean simpleMapping = true;
if (simpleMapping) {
dst = new RectF(bounds);
} else {
float x = bounds.exactCenterX();
dst = new RectF(x, 0, x, bounds.height());
float scale = bounds.height() / src.height();
float dx = scale * src.width() / 2;
dst.inset(-dx, 0);
}
mMatrix.setRectToRect(src, dst, ScaleToFit.CENTER);
}
}
@Override
public void draw(Canvas canvas) {
canvas.drawColor(0xaa00ff00);
canvas.drawBitmap(getBitmap(), mMatrix, null);
}
}
Après m'être gratté la tête pendant un moment, cela a été ma solution "bonne pour le moment".
Partage d'un exemple de XML pouvant être utile à quiconque.
<!-- RelativeLayout so that Views can overlap each other.
I believe a FrameLayout would work as well. -->
<RelativeLayout
Android:id="@+id/root"
Android:layout_width="match_parent"
Android:layout_height="match_parent" >
<!-- I've put the ImageView inside
a ScrollView with Android:fillViewport="true" and Android:isScrollContainer="false" -->
<ScrollView
Android:id="@+id/backgroundScrollView"
Android:layout_width="match_parent"
Android:layout_height="match_parent"
Android:fillViewport="true"
Android:isScrollContainer="false" >
<LinearLayout
Android:layout_width="match_parent"
Android:layout_height="match_parent"
Android:orientation="vertical" >
<!-- The FullScreen, Center-Cropped Image Background -->
<ImageView
Android:id="@+id/backgroundImage"
Android:layout_width="match_parent"
Android:layout_height="match_parent"
Android:scaleType="centerCrop"
Android:src="@drawable/background_image" />
</LinearLayout>
</ScrollView>
<!-- The rest of the Views, containing the data entry form... -->
<LinearLayout
Android:id="@+id/form"
Android:layout_width="match_parent"
Android:layout_height="match_parent"
Android:orientation="vertical" >
<ScrollView
Android:id="@+id/formScrollView"
Android:layout_width="match_parent"
Android:layout_height="wrap_content" >
<LinearLayout
Android:layout_width="match_parent"
Android:layout_height="0dp"
Android:layout_weight="1"
Android:orientation="vertical" >
<!-- The "Form", with EditTexts, Checkboxes and whatnot... -->
</LinearLayout>
</ScrollView>
</LinearLayout>
</RelativeLayout>
Avec cette mise en page, je réalise deux choses:
formScrollView
, qui contient le "formulaire" avec les EditTexts, etc., est redimensionné (comme je le souhaite) et défile lorsque le clavier logiciel est affiché.
backgroundScrollView
, qui contient ImageView, n'est pas redimensionné (à cause du Android:isScrollContainer="false"
) et remplit la fenêtre d'affichage entière (Android:fillViewport="true"
). Ainsi, l’arrière-plan reste le même, que le clavier soit visible ou non.
Ce n’est pas une solution parfaite, car la variable ImageView
souffre d’une très petite variation de sa taille, mais le résultat est suffisant pour le moment. Si quelqu'un connaît une meilleure solution, s'il vous plaît faites le moi savoir.
Mettez tout dans un FrameLayout, avec au premier enfant une imageview qui va correspondre à son parent (le frameLayout) et vous le configurez comme vous le souhaitez, et devant la vue, vous mettez ce que vous voulez (LinearLayout, je suppose)
Changer votre ScaleType
Android:scaleType="FitXY"
et dans votre manifeste
<activity
Android:name=".activity.MainActivity"
Android:windowSoftInputMode="adjustPan|stateHidden" />
La solution dans la réponse est la meilleure, pour l'utiliser:
Resources res = getActivity().getResources();
Bitmap bitmap = BitmapFactory.decodeResource(res, R.drawable.some_image);
BackgroundBitmapDrawable background = new BackgroundBitmapDrawable(res, bitmap);
view.setBackgroundDrawable(background);
ou
view.setBackground(background);
pour API 16 et plus.