Saya memiliki ImageView
yang menampilkan png yang memiliki rasio aspek lebih besar daripada perangkat (secara vertikal - artinya lebih panjang). Saya ingin menampilkan ini sambil mempertahankan rasio aspek, mencocokkan lebar induknya, dan menyematkan tampilan gambar ke bagian atas layar.
Masalah yang saya miliki dengan menggunakan CENTER_CROP
sebagai tipe skala adalah bahwa itu akan (dapat dimengerti) memusatkan gambar yang diskalakan daripada menyelaraskan tepi atas ke tepi atas untuk tampilan gambar.
Masalahnya FIT_START
adalah bahwa gambar akan sesuai dengan tinggi layar dan tidak memenuhi lebarnya.
Saya telah memecahkan masalah ini dengan menggunakan ImageView kustom dan menimpa onDraw(Canvas)
serta menangani ini secara manual menggunakan kanvas; Masalah dengan pendekatan ini adalah 1) Saya khawatir mungkin ada solusi yang lebih sederhana, 2) Saya mendapatkan pengecualian mem VM saat memanggil super(AttributeSet)
konstruktor saat mencoba menyetel src img 330kb saat heap memiliki 3 mb gratis (dengan ukuran tumpukan 6 mb) dan tidak tahu mengapa.
Setiap ide / saran / solusi sangat diterima :)
Terima kasih
ps i berpikir bahwa solusinya mungkin dengan menggunakan jenis skala matriks dan melakukannya sendiri, tetapi tampaknya bekerja sama atau lebih daripada solusi saya saat ini!
Jawaban:
Oke, saya punya solusi yang berfungsi. Prompt dari Darko membuat saya melihat kembali kelas ImageView (terima kasih) dan telah menerapkan transformasi menggunakan Matrix (seperti yang awalnya saya duga tetapi tidak berhasil pada upaya pertama saya!). Di kelas imageView kustom saya, saya memanggil
setScaleType(ScaleType.MATRIX)
setelahsuper()
di konstruktor, dan memiliki metode berikut.@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); }
Saya telah menempatkan int dalam
setFrame()
metode seperti dalam ImageView, panggilan keconfigureBounds()
dalam metode ini, di mana semua penskalaan dan matriks terjadi, jadi menurut saya logis (katakanlah jika Anda tidak setuju)Di bawah ini adalah metode super.setFrame () dari AOSP
@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; }
Temukan src kelas lengkap di sini
sumber
di sini adalah kode saya untuk memusatkannya di bagian bawah. Btw. dalam Dori's Code ada sedikit bug: Karena
super.frame()
dipanggil paling akhir,getWidth()
metode ini mungkin mengembalikan nilai yang salah. Jika Anda ingin memusatkannya di atas cukup hapus baris postTranslate dan selesai. Hal yang menyenangkan adalah dengan kode ini Anda dapat memindahkannya ke mana pun Anda mau. (kanan, tengah => tidak masalah;)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); } }
sumber
(r-l)
saja?if((frameWidth > originalImageWidth) || (frameHeight > originalImageHeight))
harus dibalik? Dengan kata lain, bukankah sebaiknya Anda menguji apakah gambar lebih besar dari bingkai? Saya sarankan untuk menggantinya denganif((originalImageWidth > frameWidth ) || (originalImageHeight > frameHeight ))
((View) getParent()).getWidth()
sejakImageView
hasMATCH_PARENT
Anda tidak perlu menulis Tampilan Gambar Kustom untuk mendapatkan
TOP_CROP
fungsionalitas tersebut. Anda hanya perlu memodifikasimatrix
fileImageView
.Setel
scaleType
kematrix
untukImageView
:<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"/>
Tetapkan matriks kustom untuk
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);
Melakukan ini akan memberi Anda
TOP_CROP
fungsionalitas.sumber
scaleType
kecenterCrop
jika tidak, saya akan melihat ruang kosong di tepinya.Contoh ini bekerja dengan gambar yang dimuat setelah pembuatan objek + beberapa pengoptimalan. Saya menambahkan beberapa komentar dalam kode yang menjelaskan apa yang sedang terjadi.
Ingatlah untuk menelepon:
imageView.setScaleType(ImageView.ScaleType.MATRIX);
atau
android:scaleType="matrix"
Sumber 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 }
sumber
Berdasarkan Dori, saya menggunakan solusi yang mengubah skala gambar berdasarkan lebar atau tinggi gambar untuk selalu mengisi wadah di sekitarnya. Ini memungkinkan penskalaan gambar untuk mengisi seluruh ruang yang tersedia menggunakan titik kiri atas gambar, bukan di tengah sebagai titik asal (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); }
Saya harap ini membantu - bekerja seperti suguhan dalam proyek saya.
sumber
Tak satu pun dari solusi ini berhasil untuk saya, karena saya menginginkan kelas yang mendukung pemangkasan sewenang-wenang baik dari arah horizontal maupun vertikal, dan saya ingin itu memungkinkan saya mengubah pemangkasan secara dinamis. Saya juga membutuhkan kompatibilitas Picasso , dan Picasso menyetel gambar yang dapat digambar dengan malas.
Implementasi saya diadaptasi langsung dari ImageView.java di AOSP. Untuk menggunakannya, nyatakan seperti itu dalam XML:
<com.yourapp.PercentageCropImageView android:id="@+id/view" android:layout_width="match_parent" android:layout_height="match_parent" android:scaleType="matrix"/>
Dari sumber, jika Anda ingin memiliki hasil panen terbaik, hubungi:
imageView.setCropYCenterOffsetPct(0f);
Jika Anda ingin memiliki potongan bawah, hubungi:
imageView.setCropYCenterOffsetPct(1.0f);
Jika Anda ingin mendapatkan 1/3 hasil panen, panggil:
imageView.setCropYCenterOffsetPct(0.33f);
Selain itu, jika Anda memilih untuk menggunakan metode pemotongan lain, seperti fit_center, Anda dapat melakukannya dan tidak ada logika khusus ini yang akan dipicu. (Penerapan lain HANYA memungkinkan Anda menggunakan metode pemangkasannya).
Terakhir, saya menambahkan metode, redraw (), jadi jika Anda memilih untuk mengubah metode krop / scaleType secara dinamis dalam kode, Anda dapat memaksa tampilan untuk menggambar ulang. Sebagai contoh:
fullsizeImageView.setScaleType(ScaleType.FIT_CENTER); fullsizeImageView.redraw();
Untuk kembali ke pemangkasan sepertiga tengah atas kustom Anda, hubungi:
fullsizeImageView.setScaleType(ScaleType.MATRIX); fullsizeImageView.redraw();
Ini kelasnya:
/* * 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); } } }
sumber
Mungkin masuk ke kode sumber untuk tampilan gambar di android dan lihat bagaimana itu menggambar tanaman pusat dll .. dan mungkin menyalin beberapa kode itu ke dalam metode Anda. saya tidak tahu untuk solusi yang lebih baik daripada melakukan ini. saya memiliki pengalaman secara manual mengubah ukuran dan memotong bitmap (mencari transformasi bitmap) yang mengurangi ukuran sebenarnya tetapi masih menciptakan sedikit overhead dalam prosesnya.
sumber
public class ImageViewTopCrop extends ImageView { public ImageViewTopCrop(Context context) { super(context); setScaleType(ScaleType.MATRIX); } public ImageViewTopCrop(Context context, AttributeSet attrs) { super(context, attrs); setScaleType(ScaleType.MATRIX); } public ImageViewTopCrop(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); setScaleType(ScaleType.MATRIX); } @Override protected boolean setFrame(int l, int t, int r, int b) { computMatrix(); return super.setFrame(l, t, r, b); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); computMatrix(); } private void computMatrix() { Matrix matrix = getImageMatrix(); float scaleFactor = getWidth() / (float) getDrawable().getIntrinsicWidth(); matrix.setScale(scaleFactor, scaleFactor, 0, 0); setImageMatrix(matrix); }
}
sumber
onLayout
menyelamatkan saya banyak! Terima kasih! Saya mengalami masalah di mana ia menghitung matriks tetapi tidak segera menampilkan gambar dan menambahkanonLayout
kode memecahkan masalah saya.Jika Anda menggunakan Fresco (SimpleDraweeView), Anda dapat dengan mudah melakukannya dengan:
PointF focusPoint = new PointF(0.5f, 0f); imageDraweeView.getHierarchy().setActualImageFocusPoint(focusPoint);
Yang ini akan menjadi hasil panen terbaik.
Info lebih lanjut di Reference Link
sumber
Ada 2 masalah dengan solusinya di sini:
Modifikasi kecil ini memperbaiki masalah (tempatkan kode di onDraw, dan periksa faktor skala lebar dan tinggi):
@Override protected void onDraw(Canvas canvas) { Matrix matrix = getImageMatrix(); float scaleFactorWidth = getWidth() / (float) getDrawable().getIntrinsicWidth(); float scaleFactorHeight = getHeight() / (float) getDrawable().getIntrinsicHeight(); float scaleFactor = (scaleFactorWidth > scaleFactorHeight) ? scaleFactorWidth : scaleFactorHeight; matrix.setScale(scaleFactor, scaleFactor, 0, 0); setImageMatrix(matrix); super.onDraw(canvas); }
sumber
Solusi Paling Sederhana: Klip gambar
@Override public void draw(Canvas canvas) { if(getWidth() > 0){ int clipHeight = 250; canvas.clipRect(0,clipHeight,getWidth(),getHeight()); } super.draw(canvas); }
sumber