How to have a circular, center-cropped imageView, without creating a new bitmap?(如何在不创建新位图的情况下拥有圆形、中心裁剪的 imageView?)
问题描述
注意:我知道有很多关于此的问题和存储库,但似乎没有一个适合我尝试实现的目标.
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?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!
本文标题为:如何在不创建新位图的情况下拥有圆形、中心裁
基础教程推荐
- 如何在没有IB的情况下将2个按钮添加到右侧的UINavigationbar? 2022-01-01
- 如何在 UIImageView 中异步加载图像? 2022-01-01
- 在 gmail 中为 ios 应用程序检索朋友的朋友 2022-01-01
- Android:对话框关闭而不调用关闭 2022-01-01
- 当从同一个组件调用时,两个 IBAction 触发的顺序是什么? 2022-01-01
- 如何在 iPhone 上显示来自 API 的 HTML 文本? 2022-01-01
- UIWebView 委托方法 shouldStartLoadWithRequest:在 WKWebView 中等效? 2022-01-01
- 如何让对象对 Cocos2D 中的触摸做出反应? 2022-01-01
- android 应用程序已发布,但在 google play 中找不到 2022-01-01
- Kivy Buildozer 无法构建 apk,命令失败:./distribute.sh -m “kivy"d 2022-01-01