Android - ImageView bottomCrop вместо centerCrop

Я пытаюсь расположить ImageView так, чтобы нижняя часть изображения всегда была прикреплена к нижней части представления, независимо от того, насколько мала высота ImageView. Однако ни один из типов шкалы не подходит для того, что я пытаюсь сделать. CenterCrop близок, но я не хочу, чтобы изображение было сосредоточено. Подобно тому, как CSS будет обрабатывать абсолютное позиционирование.

Причина в том, что мне нужно оживить высоту ImageView, но похоже, что это "раскрытие" верхней части изображения. Я предполагаю, что выяснение этого метода обрезки изображения и анимация высоты ImageView - это самый простой способ сделать это, но если кто-то знает о лучшем способе, мне бы хотелось, чтобы его указывали в правильном направлении.

Любая помощь была оценена.

Ответ 1

В итоге я подклассифицировал ImageView и создал способ включения масштабирования изображения типа BottomCrop.

Я присвоил изображение RectF правильного размера, вычислив масштаб и ожидаемую высоту изображения в зависимости от высоты представления.

public class BottomCropImage extends ImageView {

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

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

public BottomCropImage(Context context, AttributeSet attrs,
        int defStyle) {
    super(context, attrs, defStyle);
    setup();
}

private void setup() {
    setScaleType(ScaleType.MATRIX);
}

@Override
protected boolean setFrame(int l, int t, int r, int b) {
    Matrix matrix = getImageMatrix();

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

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

    //Define the rect to take image portion from
    RectF drawableRect = new RectF(0, drawableHeight - (viewHeight / scale), drawableWidth, drawableHeight);
    RectF viewRect = new RectF(0, 0, viewWidth, viewHeight);
    matrix.setRectToRect(drawableRect, viewRect, Matrix.ScaleToFit.FILL);


    setImageMatrix(matrix);

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

}

Ответ 2

Ответ Jpoliachik был достаточно крутым, чтобы я хотел обобщить его, чтобы он поддерживал верхнюю/нижнюю и левую/правую переменную величину. :) Теперь, чтобы setCropOffset(0,0) сверху, просто вызовите setCropOffset(0,0), setCropOffset(0,1) снизу setCropOffset(0,1), setCropOffset(0,0) слева также setCropOffset(0,0) и setCropOffset(1,0) правого setCropOffset(1,0). Если вы хотите сместить область просмотра на некоторую часть изображения в одном измерении, вы можете вызвать, например, setCropOffset(0, 0.25f) чтобы сместить его на 25% невидимого пространства, в то время как 0,5f центрирует его. Ура!

/**
 * {@link android.widget.ImageView} that supports directional cropping in both vertical and
 * horizontal directions instead of being restricted to center-crop. Automatically sets {@link
 * android.widget.ImageView.ScaleType} to MATRIX and defaults to center-crop.
 */
public class CropImageView extends android.support.v7.widget.AppCompatImageView {
    private static final float DEFAULT_HORIZONTAL_OFFSET = 0.5f;
    private static final float DEFAULT_VERTICAL_OFFSET = 0.5f;

    private float mHorizontalOffsetPercent = DEFAULT_HORIZONTAL_OFFSET;
    private float mVerticalOffsetPercent = DEFAULT_VERTICAL_OFFSET;

    public CropImageView(Context context) {
        this(context, null);
    }

    public CropImageView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public CropImageView(Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        setScaleType(ScaleType.MATRIX);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        applyCropOffset();
    }

    /**
     * Sets the crop box offset by the specified percentage values. For example, a center-crop would
     * be (0.5, 0.5), a top-left crop would be (0, 0), and a bottom-center crop would be (0.5, 1)
     */
    public void setCropOffset(float horizontalOffsetPercent, float verticalOffsetPercent) {
        if (mHorizontalOffsetPercent < 0
                || mVerticalOffsetPercent < 0
                || mHorizontalOffsetPercent > 1
                || mVerticalOffsetPercent > 1) {
            throw new IllegalArgumentException("Offset values must be a float between 0.0 and 1.0");
        }

        mHorizontalOffsetPercent = horizontalOffsetPercent;
        mVerticalOffsetPercent = verticalOffsetPercent;
        applyCropOffset();
    }

    private void applyCropOffset() {
        Matrix matrix = getImageMatrix();

        float scale;
        int viewWidth = getWidth() - getPaddingLeft() - getPaddingRight();
        int viewHeight = getHeight() - getPaddingTop() - getPaddingBottom();
        int drawableWidth = 0, drawableHeight = 0;
        // Allow for setting the drawable later in code by guarding ourselves here.
        if (getDrawable() != null) {
            drawableWidth = getDrawable().getIntrinsicWidth();
            drawableHeight = getDrawable().getIntrinsicHeight();
        }

        // Get the scale.
        if (drawableWidth * viewHeight > drawableHeight * viewWidth) {
            // Drawable is flatter than view. Scale it to fill the view height.
            // A Top/Bottom crop here should be identical in this case.
            scale = (float) viewHeight / (float) drawableHeight;
        } else {
            // Drawable is taller than view. Scale it to fill the view width.
            // Left/Right crop here should be identical in this case.
            scale = (float) viewWidth / (float) drawableWidth;
        }

        float viewToDrawableWidth = viewWidth / scale;
        float viewToDrawableHeight = viewHeight / scale;
        float xOffset = mHorizontalOffsetPercent * (drawableWidth - viewToDrawableWidth);
        float yOffset = mVerticalOffsetPercent * (drawableHeight - viewToDrawableHeight);

        // Define the rect from which to take the image portion.
        RectF drawableRect =
                new RectF(
                        xOffset,
                        yOffset,
                        xOffset + viewToDrawableWidth,
                        yOffset + viewToDrawableHeight);
        RectF viewRect = new RectF(0, 0, viewWidth, viewHeight);
        matrix.setRectToRect(drawableRect, viewRect, Matrix.ScaleToFit.FILL);

        setImageMatrix(matrix);
    }
}

Ответ 3

Я использовал код @Jpoliachik, и он работал хорошо, я сделал пару настроек, потому что иногда возвращались getWidth и getHeight 0 - getMeasuredWidth и getMeasuredHeight.

@Override
protected boolean setFrame(int l, int t, int r, int b) {
   if (getDrawable() == null)
       return super.setFrame(l, t, r, b);

   Matrix matrix = getImageMatrix();

   float scale;
   int viewWidth = getMeasuredWidth() - getPaddingLeft() - getPaddingRight();
   int viewHeight = getMeasuredHeight() - getPaddingTop() - getPaddingBottom();
   int drawableWidth = getDrawable().getIntrinsicWidth();
   int drawableHeight = getDrawable().getIntrinsicHeight();
   //Get the scale
   if (drawableWidth * viewHeight > drawableHeight * viewWidth) {
       scale = (float) viewHeight / (float) drawableHeight;
   } else {
       scale = (float) viewWidth / (float) drawableWidth;
   }

   //Define the rect to take image portion from
   RectF drawableRect = new RectF(0, drawableHeight - (viewHeight / scale), drawableWidth, drawableHeight);
   RectF viewRect = new RectF(0, 0, viewWidth, viewHeight);
   matrix.setRectToRect(drawableRect, viewRect, Matrix.ScaleToFit.FILL);

   setImageMatrix(matrix);

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

Ответ 4

Это решение отлично работает. Небольшое улучшение сделало бы CustomView настраиваемым из .xml в topCrop или bottomCrop. Вот полное решение на gitHub: ScalableImageView

val drawableRect = when (matrixType) {
    FIT_BOTTOM -> RectF(0f, drawableHeight - offset, drawableWidth, drawableHeight)
    FIT_TOP -> RectF(0f, 0f, drawableWidth, offset)
}

Ответ 5

Вы пробовали Imageview Scaletype FIT_END Это лучший доступный вариант, чтобы показать конец изображения.