85 votes

ImageView de mise à l'échelle TOP_CROP

J'ai un ImageView qui est l'affichage d'une image png qui a un plus grand format que celui de l'appareil (verticalement parlant - dans le sens de la longueur). Je veux afficher ce, tout en conservant l'aspect ratio, correspondant à la largeur de la mère, et l'épinglage de l'imageview en haut de l'écran.

Le problème que j'ai avec l'aide d' CENTER_CROP comme le type d'échelle est qu'il (comprendre) le centre de l'image à l'échelle au lieu d'aligner le bord supérieur vers le haut, bord supérieur en f l'affichage de l'image.

Le problème avec FIT_START , c'est que l'image s'adapte à la hauteur de l'écran et de ne pas remplir la largeur.

J'ai résolu ce problème en utilisant une mesure ImageView et primordial, onDraw(Canvas) et handeling manuellement à l'aide de la toile; le problème avec cette approche est que 1) je suis inquiet il y a peut être une solution plus simple, de 2) je suis une VM mem exception lors de l'appel d' super(AttributeSet) dans le constructeur lorsque vous tentez de définir une img src de 330kb lorsque le tas est de 3 mo d'espace libre (avec une taille de segment de 6 mo) et je ne peux savoir pourquoi.

Toutes les idées / suggestions / solutions sont les bienvenus :)

Merci

p.s. je pensais qu'une solution peut être d'utiliser une matrice de type d'échelle et de le faire moi-même, mais qui semble être la même ou plus de travail que ma solution actuelle!

84voto

Dori Points 4011

Ok, j'ai une solution qui fonctionne. L'invite de Darko m'a fait regarder de nouveau l'ImageView classe (merci) et nous avons appliqué la transformation à l'aide d'une Matrice (que j'ai d'abord soupçonné, mais n'a pas de succès lors de ma première tentative!). Dans mon custom imageView classe j'appelle setScaleType(ScaleType.MATRIX) après super() dans le constructeur, et d'avoir la méthode suivante.

    @Override
    protected boolean setFrame(int l, int t, int r, int b)
    {
        Matrix matrix = getImageMatrix(); 
        float scaleFactor = getWidth()/(float)getDrawable().getIntrinsicWidth();    
        matrix.setScale(scaleFactor, scaleFactor, 0, 0);
        setImageMatrix(matrix);
        return super.setFrame(l, t, r, b);
    }

J'ai placé un int dans l' setFrame() méthode que dans ImageView l'appel à configureBounds() est à l'intérieur de cette méthode, qui est celle de la mise à l'échelle et de la matrice des trucs a lieu, de sorte que semble logique pour moi (dire si vous êtes en désaccord)

Ci-dessous est super.setFrame() la méthode de PSBA

 @Override
    protected boolean setFrame(int l, int t, int r, int b) {
        boolean changed = super.setFrame(l, t, r, b);
        mHaveFrame = true;
        configureBounds();
        return changed;
    }

Trouver toute la classe src ici

41voto

MajorBreakfast Points 191

voici mon code pour le centrage du bas. Btw. dans Dori Code est un petit bug: Depuis l' super.frame() est appelé à la fin, l' getWidth() méthode peut retourner la valeur faux. Si vous voulez centrer en haut tout simplement supprimer la postTranslate ligne et vous avez terminé. La bonne chose est que, avec ce code, vous pouvez le déplacer n'importe où vous voulez. (à droite, au centre => pas de problème ;)

public class CenterBottomImageView extends ImageView {

    public CenterBottomImageView(Context context) {
        super(context);
        setup();
    }

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

    public CenterBottomImageView(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;

        float originalImageWidth = (float)getDrawable().getIntrinsicWidth();
        float originalImageHeight = (float)getDrawable().getIntrinsicHeight();

        float usedScaleFactor = 1;

        if((frameWidth > originalImageWidth) || (frameHeight > originalImageHeight)) {
            // If frame is bigger than image
            // => Crop it, keep aspect ratio and position it at the bottom and center horizontally

            float fitHorizontallyScaleFactor = frameWidth/originalImageWidth;
            float fitVerticallyScaleFactor = frameHeight/originalImageHeight;

            usedScaleFactor = Math.max(fitHorizontallyScaleFactor, fitVerticallyScaleFactor);
        }

        float newImageWidth = originalImageWidth * usedScaleFactor;
        float newImageHeight = originalImageHeight * usedScaleFactor;

        Matrix matrix = getImageMatrix();
        matrix.setScale(usedScaleFactor, usedScaleFactor, 0, 0); // Replaces the old matrix completly
        matrix.postTranslate((frameWidth - newImageWidth) /2, frameHeight - newImageHeight);
        setImageMatrix(matrix);
        return super.setFrame(frameLeft, frameTop, frameRight, frameBottom);
    }

}

26voto

Jacek Marchwicki Points 408

Cet exemple fonctionne avec des images qui est chargé après la création de l'objet + certains d'optimisation. J'ai ajouté quelques commentaires dans le code pour expliquer ce qu'il se passe.

N'oubliez pas d'appeler:

imageView.setScaleType(ImageView.ScaleType.MATRIX);

ou

android:scaleType="matrix"

Source Java:

import com.appunite.imageview.OverlayImageView;

public class TopAlignedImageView extends ImageView {
    private Matrix mMatrix;
    private boolean mHasFrame;

    @SuppressWarnings("UnusedDeclaration")
    public TopAlignedImageView(Context context) {
        this(context, null, 0);
    }

    @SuppressWarnings("UnusedDeclaration")
    public TopAlignedImageView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    @SuppressWarnings("UnusedDeclaration")
    public TopAlignedImageView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        mHasFrame = false;
        mMatrix = new Matrix();
        // we have to use own matrix because:
        // ImageView.setImageMatrix(Matrix matrix) will not call
        // configureBounds(); invalidate(); because we will operate on ImageView object
    }

    @Override
    protected boolean setFrame(int l, int t, int r, int b)
    {
        boolean changed = super.setFrame(l, t, r, b);
        if (changed) {
            mHasFrame = true;
            // we do not want to call this method if nothing changed
            setupScaleMatrix(r-l, b-t);
        }
        return changed;
    }

    private void setupScaleMatrix(int width, int height) {
        if (!mHasFrame) {
            // we have to ensure that we already have frame
            // called and have width and height
            return;
        }
        final Drawable drawable = getDrawable();
        if (drawable == null) {
            // we have to check if drawable is null because
            // when not initialized at startup drawable we can
            // rise NullPointerException
            return;
        }
        Matrix matrix = mMatrix;
        final int intrinsicWidth = drawable.getIntrinsicWidth();
        final int intrinsicHeight = drawable.getIntrinsicHeight();

        float factorWidth = width/(float) intrinsicWidth;
        float factorHeight = height/(float) intrinsicHeight;
        float factor = Math.max(factorHeight, factorWidth);

        // there magic happen and can be adjusted to current
        // needs
        matrix.setTranslate(-intrinsicWidth/2.0f, 0);
        matrix.postScale(factor, factor, 0, 0);
        matrix.postTranslate(width/2.0f, 0);
        setImageMatrix(matrix);
    }

    @Override
    public void setImageDrawable(Drawable drawable) {
        super.setImageDrawable(drawable);
        // We have to recalculate image after chaning image
        setupScaleMatrix(getWidth(), getHeight());
    }

    @Override
    public void setImageResource(int resId) {
        super.setImageResource(resId);
        // We have to recalculate image after chaning image
        setupScaleMatrix(getWidth(), getHeight());
    }

    @Override
    public void setImageURI(Uri uri) {
        super.setImageURI(uri);
        // We have to recalculate image after chaning image
        setupScaleMatrix(getWidth(), getHeight());
    }

    // We do not have to overide setImageBitmap because it calls 
    // setImageDrawable method

}

13voto

Matt Points 863

Basé sur Dori je suis en utilisant une solution qui soit à l'échelle de l'image en fonction de la largeur ou de la hauteur de l'image à toujours remplir les environs de conteneurs. Cela permet la mise à l'échelle d'une image afin de remplir tout l'espace disponible à l'aide du point supérieur gauche de l'image plutôt que le centre d'origine (CENTER_CROP):

@Override
protected boolean setFrame(int l, int t, int r, int b)
{

    Matrix matrix = getImageMatrix(); 
    float scaleFactor, scaleFactorWidth, scaleFactorHeight;
    scaleFactorWidth = (float)width/(float)getDrawable().getIntrinsicWidth();
    scaleFactorHeight = (float)height/(float)getDrawable().getIntrinsicHeight();    

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

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

    return super.setFrame(l, t, r, b);
}

J'espère que cette aide - fonctionne comme une friandise dans mon projet.

5voto

DArkO Points 5974

Peut-être aller dans le code source pour l'affichage des images sur android, et voir comment il dessine le centre de culture etc.. et peut-être de copier certains de ce code dans vos méthodes. je ne sais pas vraiment une meilleure solution que de faire cela. j'ai de l'expérience manuellement le redimensionnement et le rognage de l'image bitmap (recherche de bitmap transformations), ce qui réduit sa taille réelle, mais il crée tout de même un peu de frais généraux dans le processus.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X