如何有一个圆形,中心裁剪的ImageView,而无需创建一个新的位图? [英] How to have a circular, center-cropped imageView, without creating a new bitmap?

查看:481
本文介绍了如何有一个圆形,中心裁剪的ImageView,而无需创建一个新的位图?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

注意:我知道有很多关于这个问题和资料库,但没有似乎符合我尽量做到

背景

任给纵横比的位图,祝(仅使用可拉伸,不延长的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);
    }
}

我希望这是怎么会事应该真正发挥作用。


编辑:看来,解决办法只能从特定的Andr​​oid版本,因为它不会在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屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆