How do you scale an ImageView like centerCrop, but from top?
Asked Answered
R

4

12

Let's say I'm getting some image from an API. I have no idea what the image's dimensions will be ahead of time, but I know that I want that image to become the src of an ImageView I have set to some specific size. I want whatever image I get from the API to fill the entire ImageView, I want to preserve aspect ratio, and I don't care if one dimension (width or height) becomes too big for the set size of the view—so I use centerCrop.

<ImageView
  android:layout_width="400px"
  android:layout_height="300px"
  android:scaleType="centerCrop" />

If the image that comes back from the API is this:

Uncropped, unscaled sample image

When it gets set as the ImageView's src, the result will be something akin to this (shaded parts are cropped off):

Sample image scaled and cropped with centerCrop

However, I get a request that we should always show the top of the image and crop from the bottom up. So the desired result is something like this:

Sample image scaled and cropped from the top

I'm sure this is possible, but I'm a web guy operating out of his league here. Will it be better to extend ImageView somehow, or to try scaleType="matrix" and setImageMatrix(), or some third, unmentioned option?

Roscoeroscommon answered 7/7, 2016 at 2:37 Comment(2)
Possibly duplicate https://mcmap.net/q/161220/-imageview-scaling-top_cropGunstock
Possible duplicate of How set imageview scaletype to topCropConifer
A
10

Use this TopCropImageView gist

import android.content.Context;
import android.graphics.Matrix;
import android.widget.ImageView;

/**
* ImageView to display top-crop scale of an image view.
*
* @author Chris Arriola
*/
public class TopCropImageView extends ImageView {

    public TopCropImageView(Context context) {
        super(context);
        setScaleType(ScaleType.MATRIX);
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        recomputeImgMatrix();
    }

    @Override
    protected boolean setFrame(int l, int t, int r, int b) {
        recomputeImgMatrix();
        return super.setFrame(l, t, r, b);
    }      

    private void recomputeImgMatrix() {
        final Matrix matrix = getImageMatrix();

        float scale;
        final int viewWidth = getWidth() - getPaddingLeft() - getPaddingRight();
        final int viewHeight = getHeight() - getPaddingTop() - getPaddingBottom();
        final int drawableWidth = getDrawable().getIntrinsicWidth();
        final int drawableHeight = getDrawable().getIntrinsicHeight();

        if (drawableWidth * viewHeight > drawableHeight * viewWidth) {
            scale = (float) viewHeight / (float) drawableHeight;
        } else {
            scale = (float) viewWidth / (float) drawableWidth;
        }

        matrix.setScale(scale, scale);
        setImageMatrix(matrix);
    }
}
Autostability answered 7/7, 2016 at 2:44 Comment(1)
I also added a Kotlin version of this class in your gist: gist.github.com/arriolac/3843346#gistcomment-3604153Chessboard
D
7

If you are using Glide, you can use a custom transformation. Feel free to use mine (which is an almost exact copy of the CenterCrop Class by Glide), which takes percentages:

https://gist.github.com/bjornson/3ff8888c09908d5c6cc345d0a8e1f6a7

Use it just like any other bitmap transformation:

Glide.with(getContext())
    .load(imagePath)
    .transform(new PositionedCropTransformation(getContext(), 1, 0))
    .into(imageView);

Commonly needed values: top-left: 0, 0 top-right: 0, 1 bottom-left: 1, 0 bottom-right: 1, 1

Use 0.5f for center

Disrespectable answered 28/9, 2016 at 8:40 Comment(1)
This doesn't work on Glide 4.x. All values I plugged in crop the rightCantabrigian
M
2

PositionedCropTransformation converted code to support for Glide v4.x https://gist.github.com/bjornson/3ff8888c09908d5c6cc345d0a8e1f6a7

Mitrewort answered 4/10, 2020 at 12:4 Comment(0)
M
0

If you are using Glide you can use optionalTransform by this way:

Glide.with(context).load("http:///...")
            .override(300, 300)
            .optionalTransform(
                  new Transformation<Bitmap>() {
                     @NonNull
                     @Override
                     public Resource<Bitmap> transform(@NonNull Context context, 
                    @NonNull Resource<Bitmap> resource, int outWidth, int outHeight) {
                                       resource.get().setHeight(YOUR_CUSTOM_HIGHT);
                                       return resource;
                                   }

                    @Override 
                    public void updateDiskCacheKey(@NonNull MessageDigest messageDigest) {

                                   }
                               }

            )
           into(binding.img);
Moramorabito answered 24/6, 2020 at 9:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.