나는 ImageView
장치의 종횡비보다 더 큰 종횡비를 가진 png를 표시하고 있습니다 (수직으로 말하면-더 길다는 것을 의미합니다). 가로 세로 비율을 유지하고 부모의 너비를 일치시키고 이미지 뷰를 화면 상단에 고정하면서 이것을 표시하고 싶습니다.
CENTER_CROP
배율 유형 으로 사용하는 문제 는 이미지보기의 위쪽 가장자리를 위쪽 가장자리에 정렬하는 대신 배율 조정 된 이미지를 중앙에 배치한다는 것입니다 (이해할 수 있음).
문제 FIT_START
는 이미지가 화면 높이에 맞고 너비를 채우지 않는다는 것입니다.
사용자 지정 ImageView를 onDraw(Canvas)
사용하고 캔버스를 사용하여 수동으로 재정의 및 처리 하여이 문제를 해결했습니다 . 이 접근 방식의 문제는 1) 더 간단한 솔루션이 있을지 걱정됩니다. 2) super(AttributeSet)
힙에 3MB의 여유 공간이있을 때 src img를 330kb로 설정하려고 할 때 생성자에서 호출 할 때 VM mem 예외가 발생합니다. (힙 크기가 6MB) 이유를 알 수 없습니다.
모든 아이디어 / 제안 / 솔루션을 환영합니다 🙂
감사
추신 나는 솔루션이 매트릭스 스케일 유형을 사용하고 직접 수행하는 것이라고 생각했지만 현재 솔루션과 동일하거나 더 많은 작업 인 것 같습니다!
답변
좋아, 작동하는 솔루션이 있습니다. Darko의 프롬프트를 통해 ImageView 클래스를 다시 살펴보고 (감사합니다) Matrix를 사용하여 변환을 적용했습니다 (원래 예상했던 것처럼 첫 번째 시도에서 성공하지 못했습니다!). 내 사용자 지정 이미지 뷰 클래스에서 나는 전화 setScaleType(ScaleType.MATRIX)
후 super()
생성자에서 다음과 같은 방법이있다.
@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);
}
setFrame()
ImageView 에서와 같이 메서드 에 int를 배치했습니다.이 메서드에 대한 호출 configureBounds()
은 모든 크기 조정 및 매트릭스 작업이 발생하는 곳이므로 논리적으로 보입니다 (동의하지 않는 경우)
아래는 AOSP의 super.setFrame () 메서드입니다.
@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;
}
여기 에서 전체 클래스 src 찾기
답변
여기 중앙에 배치하는 코드가 있습니다. Btw. Dori의 코드에는 약간의 버그가 있습니다. super.frame()
맨 끝에가 호출되므로 getWidth()
메서드가 잘못된 값을 반환 할 수 있습니다. 상단 중앙에 배치하려면 postTranslate 줄을 제거하기 만하면됩니다. 좋은 점은이 코드를 사용하여 원하는 곳으로 이동할 수 있다는 것입니다. (오른쪽, 가운데 => 문제 없음;)
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) {
if (getDrawable() == null) {
return super.setFrame(frameLeft, frameTop, frameRight, 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
//comment matrix.postTranslate if you want crop from TOP
matrix.postTranslate((frameWidth - newImageWidth) /2, frameHeight - newImageHeight);
setImageMatrix(matrix);
return super.setFrame(frameLeft, frameTop, frameRight, frameBottom);
}
}
답변
TOP_CROP
기능 을 얻기 위해 사용자 정의 이미지보기를 작성할 필요가 없습니다 . 당신은 방금 수정해야 matrix
의를 ImageView
.
-
설정
scaleType
에matrix
에 대한ImageView
:<ImageView android:id="@+id/imageView" android:contentDescription="Image" android:layout_width="match_parent" android:layout_height="match_parent" android:src="@drawable/image" android:scaleType="matrix"/>
-
에 대한 사용자 지정 행렬을 설정합니다
ImageView
.final ImageView imageView = (ImageView) findViewById(R.id.imageView); final Matrix matrix = imageView.getImageMatrix(); final float imageWidth = imageView.getDrawable().getIntrinsicWidth(); final int screenWidth = getResources().getDisplayMetrics().widthPixels; final float scaleRatio = screenWidth / imageWidth; matrix.postScale(scaleRatio, scaleRatio); imageView.setImageMatrix(matrix);
이렇게하면 TOP_CROP
기능 이 제공 됩니다.
답변
이 예제는 객체 생성 + 일부 최적화 후로드 된 이미지와 함께 작동합니다. 무슨 일이 일어나고 있는지 설명하는 코드에 주석을 추가했습니다.
다음으로 전화하십시오.
imageView.setScaleType(ImageView.ScaleType.MATRIX);
또는
android:scaleType="matrix"
자바 소스 :
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
}
답변
Dori를 기반으로 항상 주변 컨테이너를 채우기 위해 이미지의 너비 또는 높이를 기준으로 이미지의 크기를 조정하는 솔루션을 사용하고 있습니다. 이렇게하면 중심 (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);
}
이것이 도움이 되었기를 바랍니다. 내 프로젝트에서 대접처럼 작동합니다.
답변
수평 또는 수직 방향에서 임의의 자르기를 지원하는 클래스를 원했기 때문에 이러한 솔루션 중 어느 것도 나에게 적합하지 않았으며 자르기를 동적으로 변경할 수 있기를 원했습니다. 또한 Picasso 호환성이 필요 했고 Picasso는 이미지 드로어 블을 느리게 설정했습니다.
내 구현은 AOSP의 ImageView.java 에서 직접 조정됩니다 . 이를 사용하려면 XML에서 다음과 같이 선언하십시오.
<com.yourapp.PercentageCropImageView
android:id="@+id/view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="matrix"/>
소스에서 최고 자르기를 원하면 다음으로 전화하십시오.
imageView.setCropYCenterOffsetPct(0f);
하단 자르기를 원하시면 다음으로 전화하십시오.
imageView.setCropYCenterOffsetPct(1.0f);
1/3 정도 자르려면 다음으로 전화하십시오.
imageView.setCropYCenterOffsetPct(0.33f);
또한 fit_center와 같은 다른 자르기 방법을 사용하기로 선택한 경우 그렇게 할 수 있으며이 사용자 지정 논리가 트리거되지 않습니다. (다른 구현에서는 자르기 방법 만 사용할 수 있습니다.)
마지막으로 redraw () 메서드를 추가 했으므로 코드에서 자르기 메서드 / scaleType을 동적으로 변경하도록 선택하면 뷰를 강제로 다시 그릴 수 있습니다. 예를 들면 :
fullsizeImageView.setScaleType(ScaleType.FIT_CENTER);
fullsizeImageView.redraw();
사용자 정의 상단 중앙 1/3 자르기로 돌아가려면 다음으로 전화하십시오.
fullsizeImageView.setScaleType(ScaleType.MATRIX);
fullsizeImageView.redraw();
수업은 다음과 같습니다.
/*
* Adapted from ImageView code at:
* http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.4.4_r1/android/widget/ImageView.java
*/
import android.content.Context;
import android.graphics.Matrix;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.widget.ImageView;
public class PercentageCropImageView extends ImageView{
private Float mCropYCenterOffsetPct;
private Float mCropXCenterOffsetPct;
public PercentageCropImageView(Context context) {
super(context);
}
public PercentageCropImageView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public PercentageCropImageView(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
}
public float getCropYCenterOffsetPct() {
return mCropYCenterOffsetPct;
}
public void setCropYCenterOffsetPct(float cropYCenterOffsetPct) {
if (cropYCenterOffsetPct > 1.0) {
throw new IllegalArgumentException("Value too large: Must be <= 1.0");
}
this.mCropYCenterOffsetPct = cropYCenterOffsetPct;
}
public float getCropXCenterOffsetPct() {
return mCropXCenterOffsetPct;
}
public void setCropXCenterOffsetPct(float cropXCenterOffsetPct) {
if (cropXCenterOffsetPct > 1.0) {
throw new IllegalArgumentException("Value too large: Must be <= 1.0");
}
this.mCropXCenterOffsetPct = cropXCenterOffsetPct;
}
private void myConfigureBounds() {
if (this.getScaleType() == ScaleType.MATRIX) {
/*
* Taken from Android's ImageView.java implementation:
*
* Excerpt from their source:
} else if (ScaleType.CENTER_CROP == mScaleType) {
mDrawMatrix = mMatrix;
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;
}
mDrawMatrix.setScale(scale, scale);
mDrawMatrix.postTranslate((int) (dx + 0.5f), (int) (dy + 0.5f));
}
*/
Drawable d = this.getDrawable();
if (d != null) {
int dwidth = d.getIntrinsicWidth();
int dheight = d.getIntrinsicHeight();
Matrix m = new Matrix();
int vwidth = getWidth() - this.getPaddingLeft() - this.getPaddingRight();
int vheight = getHeight() - this.getPaddingTop() - this.getPaddingBottom();
float scale;
float dx = 0, dy = 0;
if (dwidth * vheight > vwidth * dheight) {
float cropXCenterOffsetPct = mCropXCenterOffsetPct != null ?
mCropXCenterOffsetPct.floatValue() : 0.5f;
scale = (float) vheight / (float) dheight;
dx = (vwidth - dwidth * scale) * cropXCenterOffsetPct;
} else {
float cropYCenterOffsetPct = mCropYCenterOffsetPct != null ?
mCropYCenterOffsetPct.floatValue() : 0f;
scale = (float) vwidth / (float) dwidth;
dy = (vheight - dheight * scale) * cropYCenterOffsetPct;
}
m.setScale(scale, scale);
m.postTranslate((int) (dx + 0.5f), (int) (dy + 0.5f));
this.setImageMatrix(m);
}
}
}
// These 3 methods call configureBounds in ImageView.java class, which
// adjusts the matrix in a call to center_crop (android's built-in
// scaling and centering crop method). We also want to trigger
// in the same place, but using our own matrix, which is then set
// directly at line 588 of ImageView.java and then copied over
// as the draw matrix at line 942 of ImageVeiw.java
@Override
protected boolean setFrame(int l, int t, int r, int b) {
boolean changed = super.setFrame(l, t, r, b);
this.myConfigureBounds();
return changed;
}
@Override
public void setImageDrawable(Drawable d) {
super.setImageDrawable(d);
this.myConfigureBounds();
}
@Override
public void setImageResource(int resId) {
super.setImageResource(resId);
this.myConfigureBounds();
}
public void redraw() {
Drawable d = this.getDrawable();
if (d != null) {
// Force toggle to recalculate our bounds
this.setImageDrawable(null);
this.setImageDrawable(d);
}
}
}
답변
Android의 이미지보기에 대한 소스 코드로 이동하여 중앙 자르기 등을 그리는 방법을 확인하고 해당 코드 중 일부를 메서드에 복사 할 수 있습니다. 나는 이것을하는 것보다 더 나은 해결책을 정말로 모른다. 실제 크기를 줄이는 비트 맵 (비트 맵 변환 검색)의 크기를 수동으로 조정하고 자르는 경험이 있지만 여전히 프로세스에서 약간의 오버 헤드가 발생합니다.