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 Это лучший доступный вариант, чтобы показать конец изображения.