如何有一个圆形,中心裁剪的ImageView,而无需创建一个新的位图? [英] How to have a circular, center-cropped imageView, without creating a new bitmap?
问题描述
注意:我知道有很多关于这个问题和资料库,但没有似乎符合我尽量做到
背景
任给纵横比的位图,祝(仅使用可拉伸,不延长的ImageView)的形状,以将其设置为一个ImageView的内容,所以该内容将是中心裁剪,但一圈。
所有这一切,以最小的内存使用情况,因为图像可能会相当大,有时。我不希望创建一个全新的位图只是为了这一点。内容已经在那里...
问题
我发现所有的解决方案缺乏对我写的一件事:一些不居中的作物,有的认为该图像是方形,有的创建从给定的位图新位图...
我已经试过
除了尝试各种信息库,我已经试过的 本教程 ,我试图解决它的非方形纵横比的情况下,但我失败了。
下面是它的code,万一网站将得到关闭的:
公共类RoundImage扩展可绘制{
私人最终位图mBitmap;
私人最终涂料mPaint;
私人最终RectF mRectF;
私人最终诠释mBitmapWidth;
私人最终诠释mBitmapHeight; 公共RoundImage(位图位图){
mBitmap =位图;
mRectF =新RectF();
mPaint =新的油漆();
mPaint.setAntiAlias(真);
mPaint.setDither(真);
最后BitmapShader着色器=新BitmapShader(位图,Shader.TileMode.CLAMP,Shader.TileMode.CLAMP);
mPaint.setShader(着色器); mBitmapWidth = mBitmap.getWidth();
mBitmapHeight = mBitmap.getHeight();
} @覆盖
公共无效画(油画画布){
canvas.drawOval(mRectF,mPaint);
} @覆盖
保护无效onBoundsChange(矩形边界){
super.onBoundsChange(边界);
mRectF.set(边界);
} @覆盖
公共无效setAlpha(INT阿尔法){
如果(mPaint.getAlpha()!=阿尔法){
mPaint.setAlpha(阿尔法);
invalidateSelf();
}
} @覆盖
公共无效setColorFilter(ColorFilter CF){
mPaint.setColorFilter(CF);
} @覆盖
公众诠释getOpacity(){
返回PixelFormat.TRANSLUCENT;
} @覆盖
公众诠释getIntrinsicWidth(){
返回mBitmapWidth;
} @覆盖
公众诠释getIntrinsicHeight(){
返回mBitmapHeight;
} 公共无效setAntiAlias(布尔AA){
mPaint.setAntiAlias(AA);
invalidateSelf();
} @覆盖
公共无效setFilterBitmap(布尔过滤器){
mPaint.setFilterBitmap(过滤器);
invalidateSelf();
} @覆盖
公共无效setDither(布尔抖动){
mPaint.setDither(抖动);
invalidateSelf();
} 公共位图getBitmap(){
返回mBitmap;
}}
一个很好的解决方案,我发现( 这里 )不正是我需要的,只是它使用它都在ImageView的本身,而不是创建一个可绘制。这意味着,我无法设置它,例如,作为视图的背景。
问题
我怎样才能做到这一点?
编辑:这是当前code,和我想添加边框,它也有它这个code:
公共类SimpleRoundedDrawable扩展BitmapDrawable {
私人最终路径P =新路径();
私人最终油漆mBorderPaint =新的油漆(Paint.ANTI_ALIAS_FLAG); 公共SimpleRoundedDrawable(最终资源资源,最终的位图位图){
超(RES,位图);
mBorderPaint.setStyle(Paint.Style.STROKE);
} 公共SimpleRoundedDrawable setBorder(浮动边框宽度,@ColorInt INT BORDERCOLOR){
mBorderPaint.setStrokeWidth(边框宽度);
mBorderPaint.setColor(BORDERCOLOR);
invalidateSelf();
返回此;
} @覆盖
保护无效onBoundsChange(矩形边界){
super.onBoundsChange(边界);
p.rewind();
p.addCircle(bounds.width()/ 2,
bounds.height()/ 2,
Math.min(bounds.width(),bounds.height())/ 2,
Path.Direction.CW);
} @覆盖
公共无效画(油画画布){
canvas.clipPath(P);
super.draw(画布);
最终浮动宽度=的getBounds()宽(),身高=的getBounds()高度()。
canvas.drawCircle(宽度/ 2,高度/ 2,Math.min(宽度,高度)/ 2,mBorderPaint);
}
}
我希望这是怎么会事应该真正发挥作用。
编辑:看来,解决办法只能从特定的Android版本,因为它不会在Android 4.2.2工作。相反,它显示了一个方形的图像。
编辑:看来,上述方案也比使用BitmapShader(链接的 这里 )。这将是真正伟大知道如何在绘制中,而不是定制的ImageView
内使用 -
下面是下面的解决方案目前修改后的版本。我希望这将是得心应手一些人:
公共类SimpleRoundedDrawable扩展可绘制{
最终油漆mMaskPaint =新的油漆(Paint.ANTI_ALIAS_FLAG),mBorderPaint =新的油漆(Paint.ANTI_ALIAS_FLAG);
位图mBitmap;
INT mSide;
浮mRadius; 公共SimpleRoundedDrawable(){
这个(NULL);
} 公共SimpleRoundedDrawable(位图位图){
这(位图,0,0);
} 公共SimpleRoundedDrawable(位图位图,浮动幅度,@ColorInt INT颜色){
mBorderPaint.setStyle(Paint.Style.STROKE);
mBitmap =位图;
mSide = mBitmap == NULL? 0:Math.min(bitmap.getWidth(),bitmap.getHeight());
mBorderPaint.setStrokeWidth(宽);
mBorderPaint.setColor(颜色);
} 公共SimpleRoundedDrawable setBitmap(最终位图位图){
mBitmap =位图;
mSide = Math.min(bitmap.getWidth(),bitmap.getHeight());
invalidateSelf();
返回此;
} 公共SimpleRoundedDrawable setBorder(浮动幅度,@ColorInt INT颜色){
mBorderPaint.setStrokeWidth(宽);
mBorderPaint.setColor(颜色);
invalidateSelf();
返回此;
} @覆盖
保护无效onBoundsChange(矩形边界){
如果(mBitmap == NULL)
返回;
字模=新的Matrix();
RectF SRC =新RectF(0,0,mSide,mSide);
src.offset((mBitmap.getWidth() - mSide)/ 2F,(mBitmap.getHeight() - mSide)/ 2F);
RectF DST =新RectF(边界);
最终浮动strokeWidth = mBorderPaint.getStrokeWidth();
如果(strokeWidth大于0)
dst.inset(strokeWidth,strokeWidth);
matrix.setRectToRect(SRC,DST,Matrix.ScaleToFit.CENTER);
着色的着色器=新BitmapShader(mBitmap,Shader.TileMode.CLAMP,Shader.TileMode.CLAMP);
shader.setLocalMatrix(矩阵);
mMaskPaint.setShader(着色器);
matrix.ma preCT(SRC);
mRadius = src.width()/ 2F;
} @覆盖
公共无效画(油画画布){
矩形B =的getBounds();
如果(mBitmap!= NULL)
canvas.drawCircle(b.exactCenterX(),b.exactCenterY(),mRadius,mMaskPaint);
最终浮动strokeWidth = mBorderPaint.getStrokeWidth();
如果(strokeWidth大于0)
canvas.drawCircle(b.exactCenterX(),b.exactCenterY(),mRadius + strokeWidth / 2,mBorderPaint);
} @覆盖
公共无效setAlpha(INT阿尔法){
mMaskPaint.setAlpha(阿尔法);
invalidateSelf();
} @覆盖
公共无效setColorFilter(ColorFilter CF){
mMaskPaint.setColorFilter(CF);
invalidateSelf();
} @覆盖
公众诠释getOpacity(){
返回PixelFormat.TRANSLUCENT;
}
}
尝试这种极简的定制可绘制
并修改它以满足您的需要:
D类扩展可绘制{
位图位图;
涂料maskPaint =新的油漆(Paint.ANTI_ALIAS_FLAG);
涂料borderPaint =新的油漆(Paint.ANTI_ALIAS_FLAG);
INT端;
漂浮半径; 公共D(位图wrappedBitmap){
位= wrappedBitmap;
borderPaint.setStyle(Paint.Style.STROKE);
borderPaint.setStrokeWidth(16);
borderPaint.setColor(0xcc220088);
侧= Math.min(bitmap.getWidth(),bitmap.getHeight());
} @覆盖
保护无效onBoundsChange(矩形边界){
字模=新的Matrix();
RectF SRC =新RectF(0,0,侧面侧);
src.offset((bitmap.getWidth() - 侧)/ 2F,(bitmap.getHeight() - 侧)/ 2F);
RectF DST =新RectF(边界);
dst.inset(borderPaint.getStrokeWidth(),borderPaint.getStrokeWidth());
matrix.setRectToRect(SRC,DST,Matrix.ScaleToFit.CENTER); 着色的着色器=新BitmapShader(位图,Shader.TileMode.CLAMP,Shader.TileMode.CLAMP);
shader.setLocalMatrix(矩阵);
maskPaint.setShader(着色器);
matrix.ma preCT(SRC);
半径= src.width()/ 2F;
} @覆盖
公共无效画(油画画布){
矩形B =的getBounds();
canvas.drawCircle(b.exactCenterX(),b.exactCenterY(),半径,maskPaint);
canvas.drawCircle(b.exactCenterX(),b.exactCenterY(),半径+ borderPaint.getStrokeWidth()/ 2,borderPaint);
} @覆盖公共无效setAlpha(INT阿尔法){}
@覆盖公共无效setColorFilter(ColorFilter CF){}
@覆盖公众诠释getOpacity(){返回PixelFormat.TRANSLUCENT;}
}
Note: I know there are a lot of questions and repositories about this, but none seems to fit what I try to achieve.
Background
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...
The problem
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...
What I've tried
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;
}
}
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.
The question
How can I achieve this?
EDIT: 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.
EDIT: 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.
EDIT: 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;
}
}
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,而无需创建一个新的位图?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!