問題描述
注意:我知道有很多關于此的問題和存儲庫,但似乎沒有一個適合我嘗試實現的目標.
Note: I know there are a lot of questions and repositories about this, but none seems to fit what I try to achieve.
給定任何縱橫比的位圖,我希望將其設置為 ImageView 的內容(僅使用可繪制對象,不擴展 ImageView),以便內容將被中心裁剪,但形狀一個圓圈.
Given a bitmap of any aspect-ratio, I wish to set it as the content of an ImageView (using a drawable only, without extending the ImageView), so that the content will be center-cropped, and yet in the shape of a circle.
所有這些都使用最少的內存,因為有時圖像可能非常大.我不想為此創建一個全新的位圖.內容已經存在...
All of this, with minimal memory usage, because the images could be quite large sometimes. I do not want to create a whole new Bitmap just for this. The content is already there...
我發現的所有解決方案都缺少我所寫的內容之一:有些沒有居中裁剪,有些假設圖像是方形的,有些從給定的位圖創建一個新的位圖...
All solutions I've found lack one of the things I've written: some do not center-crop, some assume the image is square-shaped, some create a new bitmap from the given bitmap...
除了嘗試各種存儲庫之外,我還嘗試了 本教程,我嘗試針對非正方形縱橫比的情況進行修復,但失敗了.
Other than trying various repositories, I've tried this tutorial, and I tried to fix it for the case of non-square aspect ratios, but I've failed.
這是它的代碼,以防網站關閉:
Here's its code, in case the website will get closed:
public class RoundImage extends Drawable {
private final Bitmap mBitmap;
private final Paint mPaint;
private final RectF mRectF;
private final int mBitmapWidth;
private final int mBitmapHeight;
public RoundImage(Bitmap bitmap) {
mBitmap = bitmap;
mRectF = new RectF();
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setDither(true);
final BitmapShader shader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
mPaint.setShader(shader);
mBitmapWidth = mBitmap.getWidth();
mBitmapHeight = mBitmap.getHeight();
}
@Override
public void draw(Canvas canvas) {
canvas.drawOval(mRectF, mPaint);
}
@Override
protected void onBoundsChange(Rect bounds) {
super.onBoundsChange(bounds);
mRectF.set(bounds);
}
@Override
public void setAlpha(int alpha) {
if (mPaint.getAlpha() != alpha) {
mPaint.setAlpha(alpha);
invalidateSelf();
}
}
@Override
public void setColorFilter(ColorFilter cf) {
mPaint.setColorFilter(cf);
}
@Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
@Override
public int getIntrinsicWidth() {
return mBitmapWidth;
}
@Override
public int getIntrinsicHeight() {
return mBitmapHeight;
}
public void setAntiAlias(boolean aa) {
mPaint.setAntiAlias(aa);
invalidateSelf();
}
@Override
public void setFilterBitmap(boolean filter) {
mPaint.setFilterBitmap(filter);
invalidateSelf();
}
@Override
public void setDither(boolean dither) {
mPaint.setDither(dither);
invalidateSelf();
}
public Bitmap getBitmap() {
return mBitmap;
}
}
我找到了一個非常好的解決方案(這里) 完全符合我的需要,除了它在 ImageView 本身中使用它,而不是創建一個可繪制對象.這意味著我不能將它設置為例如視圖的背景.
A very good solution I've found (here) does exactly what I need, except it uses it all in the ImageView itself, instead of creating a drawable. This means that I can't set it, for example, as the background of a view.
我怎樣才能做到這一點?
How can I achieve this?
這是當前代碼,因為我想添加邊框,它也有這個代碼:
this is the current code, and as I wanted to add border, it also has this code for it:
public class SimpleRoundedDrawable extends BitmapDrawable {
private final Path p = new Path();
private final Paint mBorderPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
public SimpleRoundedDrawable(final Resources res, final Bitmap bitmap) {
super(res, bitmap);
mBorderPaint.setStyle(Paint.Style.STROKE);
}
public SimpleRoundedDrawable setBorder(float borderWidth, @ColorInt int borderColor) {
mBorderPaint.setStrokeWidth(borderWidth);
mBorderPaint.setColor(borderColor);
invalidateSelf();
return this;
}
@Override
protected void onBoundsChange(Rect bounds) {
super.onBoundsChange(bounds);
p.rewind();
p.addCircle(bounds.width() / 2,
bounds.height() / 2,
Math.min(bounds.width(), bounds.height()) / 2,
Path.Direction.CW);
}
@Override
public void draw(Canvas canvas) {
canvas.clipPath(p);
super.draw(canvas);
final float width = getBounds().width(), height = getBounds().height();
canvas.drawCircle(width / 2, height / 2, Math.min(width, height) / 2, mBorderPaint);
}
}
我希望事情應該是這樣運作的.
I hope this is how things should really work.
似乎該解決方案僅適用于特定的 Android 版本,因為它不適用于 Android 4.2.2.相反,它顯示了一個正方形的圖像.
It seems that the solution works only from specific Android version, as it doesn't work on Android 4.2.2. Instead, it shows a squared image.
似乎上述解決方案也比使用 BitmapShader 效率低得多(鏈接 這里).知道如何在可繪制對象中而不是在自定義的 ImageView 中使用它真的很棒
it seems that the above solution is also much less efficient than using BitmapShader (Link here). It would be really great to know how to use it within a drawable instead of within a customized ImageView
--這是以下解決方案的當前修改版本.我希望它對某些人有用:
-- Here's the current modified version of the below solutions. I hope it will be handy for some people:
public class SimpleRoundedDrawable extends Drawable {
final Paint mMaskPaint = new Paint(Paint.ANTI_ALIAS_FLAG), mBorderPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
Bitmap mBitmap;
int mSide;
float mRadius;
public SimpleRoundedDrawable() {
this(null);
}
public SimpleRoundedDrawable(Bitmap bitmap) {
this(bitmap, 0, 0);
}
public SimpleRoundedDrawable(Bitmap bitmap, float width, @ColorInt int color) {
mBorderPaint.setStyle(Paint.Style.STROKE);
mBitmap = bitmap;
mSide = mBitmap == null ? 0 : Math.min(bitmap.getWidth(), bitmap.getHeight());
mBorderPaint.setStrokeWidth(width);
mBorderPaint.setColor(color);
}
public SimpleRoundedDrawable setBitmap(final Bitmap bitmap) {
mBitmap = bitmap;
mSide = Math.min(bitmap.getWidth(), bitmap.getHeight());
invalidateSelf();
return this;
}
public SimpleRoundedDrawable setBorder(float width, @ColorInt int color) {
mBorderPaint.setStrokeWidth(width);
mBorderPaint.setColor(color);
invalidateSelf();
return this;
}
@Override
protected void onBoundsChange(Rect bounds) {
if (mBitmap == null)
return;
Matrix matrix = new Matrix();
RectF src = new RectF(0, 0, mSide, mSide);
src.offset((mBitmap.getWidth() - mSide) / 2f, (mBitmap.getHeight() - mSide) / 2f);
RectF dst = new RectF(bounds);
final float strokeWidth = mBorderPaint.getStrokeWidth();
if (strokeWidth > 0)
dst.inset(strokeWidth, strokeWidth);
matrix.setRectToRect(src, dst, Matrix.ScaleToFit.CENTER);
Shader shader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
shader.setLocalMatrix(matrix);
mMaskPaint.setShader(shader);
matrix.mapRect(src);
mRadius = src.width() / 2f;
}
@Override
public void draw(Canvas canvas) {
Rect b = getBounds();
if (mBitmap != null)
canvas.drawCircle(b.exactCenterX(), b.exactCenterY(), mRadius, mMaskPaint);
final float strokeWidth = mBorderPaint.getStrokeWidth();
if (strokeWidth > 0)
canvas.drawCircle(b.exactCenterX(), b.exactCenterY(), mRadius + strokeWidth / 2, mBorderPaint);
}
@Override
public void setAlpha(int alpha) {
mMaskPaint.setAlpha(alpha);
invalidateSelf();
}
@Override
public void setColorFilter(ColorFilter cf) {
mMaskPaint.setColorFilter(cf);
invalidateSelf();
}
@Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
}
推薦答案
試試這個極簡的自定義 Drawable
并修改它以滿足您的需求:
try this minimalist custom Drawable
and modify it to meet your needs:
class D extends Drawable {
Bitmap bitmap;
Paint maskPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
Paint borderPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
int side;
float radius;
public D(Bitmap wrappedBitmap) {
bitmap = wrappedBitmap;
borderPaint.setStyle(Paint.Style.STROKE);
borderPaint.setStrokeWidth(16);
borderPaint.setColor(0xcc220088);
side = Math.min(bitmap.getWidth(), bitmap.getHeight());
}
@Override
protected void onBoundsChange(Rect bounds) {
Matrix matrix = new Matrix();
RectF src = new RectF(0, 0, side, side);
src.offset((bitmap.getWidth() - side) / 2f, (bitmap.getHeight() - side) / 2f);
RectF dst = new RectF(bounds);
dst.inset(borderPaint.getStrokeWidth(), borderPaint.getStrokeWidth());
matrix.setRectToRect(src, dst, Matrix.ScaleToFit.CENTER);
Shader shader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
shader.setLocalMatrix(matrix);
maskPaint.setShader(shader);
matrix.mapRect(src);
radius = src.width() / 2f;
}
@Override
public void draw(Canvas canvas) {
Rect b = getBounds();
canvas.drawCircle(b.exactCenterX(), b.exactCenterY(), radius, maskPaint);
canvas.drawCircle(b.exactCenterX(), b.exactCenterY(), radius + borderPaint.getStrokeWidth() / 2, borderPaint);
}
@Override public void setAlpha(int alpha) {}
@Override public void setColorFilter(ColorFilter cf) {}
@Override public int getOpacity() {return PixelFormat.TRANSLUCENT;}
}
這篇關于如何在不創建新位圖的情況下擁有圓形、中心裁剪的 imageView?的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持html5模板網!