安卓:圆形Drawable

7
我做了一个自定义的Drawable,应该可以将任何Drawable切成圆形。但是用我的实现方式,传递的drawable输出的形式是原始形式而不是圆形的形式。
public class CircularDrawable extends Drawable {
    Paint mPaint,xfermodePaint;
    Drawable mDrawable;
    int[] vinylCenter = new int[2];
    int radius;
    Bitmap src;
    PorterDuffXfermode xfermode;
    Rect rect;
    Canvas testCanvas;
    public CircularDrawable(Drawable drawable) {
        mDrawable = drawable;
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setColor(0xffffffff);
        mPaint.setStyle(Paint.Style.FILL);
        xfermodePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        xfermode=new PorterDuffXfermode(PorterDuff.Mode.SRC_IN);
        xfermodePaint.setXfermode(xfermode);
        testCanvas=new Canvas();
    }

    @Override
    public void setBounds(Rect bounds) {
        super.setBounds(bounds);
        mDrawable.setBounds(bounds);
    }

    @Override
    public void setBounds(int left, int top, int right, int bottom) {
        super.setBounds(left, top, right, bottom);
        mDrawable.setBounds(left, top, right, bottom);
    }

    @Override
    protected void onBoundsChange(Rect bounds) {
        super.onBoundsChange(bounds);
        vinylCenter[0] = bounds.width() / 2;
        vinylCenter[1] = bounds.height() / 2;
        radius = (bounds.right - bounds.left) / 2;
        src = Bitmap.createBitmap(bounds.width(), bounds.height(), Bitmap.Config.ARGB_8888);
        testCanvas.setBitmap(src);
    }


    @Override
    public void draw(Canvas canvas) {
        canvas.save();
        canvas.drawARGB(0, 0, 0, 0);
        canvas.drawCircle(vinylCenter[0],vinylCenter[1],radius,mPaint);
        mDrawable.draw(testCanvas);
        canvas.drawBitmap(src,0f,0f,xfermodePaint);
    }



    @Override
    public void setAlpha(int alpha) {/*ignored*/}

    @Override
    public void setColorFilter(ColorFilter colorFilter) {/*ignored*/}

    @Override
    public int getOpacity() {
        /*ignored*/
        return 0;
    }
}

我的错误在哪里?

另外,我正在使用SquareImageview来显示这个可绘制对象,它只是在onMeasure中使视图变成正方形。 这个问题不是针对Circular Imageview的。


2
请参考此链接查看示例代码:http://androidxref.com/6.0.0_r1/xref/frameworks/support/v4/donut/android/support/v4/graphics/drawable/RoundedBitmapDrawable.java - pskink
@pskink但那是另一种实现。我想知道在Porter/Duff实现中我的错误是什么。 - user3426273
如果它是正方形,那意味着你正在绘制整个“DST”区域。 - pskink
1
好的,那么你目前做了什么?你尝试运行过APiDemos吗?它有一个Porter-Duff示例。你尝试关闭硬件加速了吗? - pskink
你需要圆形的ImageView吗? - Chirag Savsani
显示剩余4条评论
6个回答

4

你已经在Support库v4中拥有了优化的圆形可绘制实现:

Google参考文献

 /**
* A Drawable that wraps a bitmap and can be drawn with rounded corners. You can create a
* RoundedBitmapDrawable from a file path, an input stream, or from a
* {@link android.graphics.Bitmap} object.
* <p>
* Also see the {@link android.graphics.Bitmap} class, which handles the management and
* transformation of raw bitmap graphics, and should be used when drawing to a
* {@link android.graphics.Canvas}.
* </p>
*/
public abstract class RoundedBitmapDrawable extends Drawable ....

3
默认情况下,使用Porter/Duff xfermode模式时,画布不会开启alpha通道(根据我的经验,在安卓上)。这就是整个图像(src)成为输出的原因。Porter/Duff xfermode基于Alpha通道进行操作。
在许多圆形Drawable 2 Canvas实现中,其中一个给出了RGB图像位图,另一个只给出了带有圆形蒙版的alpha通道位图。应用Porter/Duff xfermode,然后将结果绘制在主画布上。
我的错误在于我没有指定draw函数的画布,在画布上使用xfermode的层时要考虑alpha通道。
绘制函数将变为:
@Override
public void draw(Canvas canvas) {
    int sc = canvas.saveLayer(null, null,
                    Canvas.HAS_ALPHA_LAYER_SAVE_FLAG |
                    );
    canvas.drawCircle(vinylCenter[0],vinylCenter[1],radius,mPaint);
    mDrawable.draw(testCanvas);
    canvas.drawBitmap(src,0f,0f,xfermodePaint);
    canvas.restoreToCount(sc);
}

使用标志保存图层,以指示alpha通道,然后使用xfermode。最后将其恢复为保存计数。


3

基本上,我使用这种方法来获取圆角图片

public static Bitmap getRoundedCornerBitmap(Bitmap bitmap, int pixels) {
    Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(output);

    final int color = 0xff424242;
    final Paint paint = new Paint();
    final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
    final RectF rectF = new RectF(rect);
    final float roundPx = pixels;

    paint.setAntiAlias(true);
    canvas.drawARGB(0, 0, 0, 0);
    paint.setColor(color);
    canvas.drawRoundRect(rectF, roundPx, roundPx, paint);

    paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
    canvas.drawBitmap(bitmap, rect, rect, paint);

    return output;

但是像你指出的有时候它并不起作用,其实它是有效的,只是大多数情况下您的ImageView可能已经将其ScaleType设置为centerCrop和其他类型,我的做法是首先将我的图片高度压缩到与我的ImageView相同的高度和宽度,以便整个图片都适合在ImageView中,并且不会进行任何缩放,或者我只需根据高度使用此函数计算出要使角落高度成为圆形的量。

   private int calculatePercentage(int percentage, int target)
{
    int k = (int)(target*(percentage/100.0f));
    return k;

}

所以我使用了这段代码:new Bitmap(getRoundedCornerBitmap(bmp, calculatePercentage(5, bmp.getHeight()))); 其中的 5 是图像高度的 5%。这样可以使所有图片产生相同的曲线,无论图像大小如何。


另一个未被要求的解决方法。你对缩放类型的假设是错误的。默认的缩放类型是fitCenter。 - user3426273
@Paritosh Tonk,你可以选择接受我的帮助或不接受,但无论如何,我的方法都完美运作。 - life evader
我感激你的帮助。问题不在于工作,而在于概念和为什么会发生这种情况。 - user3426273
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - life evader
我正在尝试重新发明轮子,因为圆形位图不是我想要的。我正在裁剪不同的东西并进行动画处理。圆形位图和圆形Drawable的代码可用,但为了在绘制时进行动画处理,我不想通过在每个帧中创建位图来增加内存负载。如果涉及编程,没有所谓的概念就不可能实现任何事情。人们雇用和支付你不是因为你会编程。我希望你能理解我在这里试图表达的意思。 - user3426273

3
For rounded imageview you can use this code 

    public class RoundedImageView extends ImageView {

    public RoundedImageView(Context context) {
        super(context);
        // TODO Auto-generated constructor stub
    }

    public RoundedImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public RoundedImageView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    protected void onDraw(Canvas canvas) {

        Drawable drawable = getDrawable();

        if (drawable == null) {
            return;
        }

        if (getWidth() == 0 || getHeight() == 0) {
            return;
        }
        Bitmap b = ((BitmapDrawable) drawable).getBitmap();
        Bitmap bitmap = b.copy(Bitmap.Config.ARGB_8888, true);

        int w = getWidth();//, h = getHeight();

        Bitmap roundBitmap = getCroppedBitmap(bitmap, w);
        canvas.drawBitmap(roundBitmap, 0, 0, null);

    }

    public static Bitmap getCroppedBitmap(Bitmap bmp, int radius)
    {
        Bitmap sbmp;
        if (bmp.getWidth() != radius || bmp.getHeight() != radius)
            sbmp = Bitmap.createScaledBitmap(bmp, radius, radius, false);
        else
            sbmp = bmp;
        Bitmap output = Bitmap.createBitmap(sbmp.getWidth(), sbmp.getHeight(),Config.ARGB_8888);
        Canvas canvas = new Canvas(output);

//      final int color = 0xffa19774;
        final Paint paint = new Paint();
        final Rect rect = new Rect(0, 0, sbmp.getWidth(), sbmp.getHeight());

        paint.setAntiAlias(true);
        paint.setFilterBitmap(true);
        paint.setDither(true);
        canvas.drawARGB(0, 0, 0, 0);
//      paint.setColor(Color.parseColor("#BAB399"));
        paint.setColor(Color.parseColor("#FF0000"));
        canvas.drawCircle(sbmp.getWidth() / 2 + 0.7f, sbmp.getHeight() / 2 + 0.7f, sbmp.getWidth() / 2 + 0.1f, paint);
        paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
        canvas.drawBitmap(sbmp, rect, rect, paint);

        return output;
    }
}

3
For circular imageview use the below code

public class RoundedImageView extends ImageView {

    public RoundedImageView(Context context) {
        super(context);
    }

    public RoundedImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public RoundedImageView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        BitmapDrawable drawable = (BitmapDrawable) getDrawable();

        if (drawable == null) {
            return;
        }

        if (getWidth() == 0 || getHeight() == 0) {
            return;
        }

        Bitmap fullSizeBitmap = drawable.getBitmap();

        int scaledWidth = getMeasuredWidth();
        int scaledHeight = getMeasuredHeight();

        /*
         * scaledWidth = scaledWidth/2; scaledHeight = scaledHeight/2;
         */

        Bitmap mScaledBitmap;
        if (scaledWidth == fullSizeBitmap.getWidth()
                && scaledHeight == fullSizeBitmap.getHeight()) {
            mScaledBitmap = fullSizeBitmap;
        } else {
            mScaledBitmap = Bitmap.createScaledBitmap(fullSizeBitmap,
                    scaledWidth, scaledHeight, true /* filter */);
        }

        // Bitmap roundBitmap = getRoundedCornerBitmap(mScaledBitmap);

        // Bitmap roundBitmap = getRoundedCornerBitmap(getContext(),
        // mScaledBitmap, 10, scaledWidth, scaledHeight, false, false,
        // false, false);
        // canvas.drawBitmap(roundBitmap, 0, 0, null);

        Bitmap circleBitmap = getCircledBitmap(mScaledBitmap);

        canvas.drawBitmap(circleBitmap, 0, 0, null);

    }

    public Bitmap getRoundedCornerBitmap(Context context, Bitmap input,
            int pixels, int w, int h, boolean squareTL, boolean squareTR,
            boolean squareBL, boolean squareBR) {

        Bitmap output = Bitmap.createBitmap(w, h, Config.ARGB_8888);
        Canvas canvas = new Canvas(output);
        final float densityMultiplier = context.getResources()
                .getDisplayMetrics().density;

        final int color = 0xff424242;

        final Paint paint = new Paint();
        final Rect rect = new Rect(0, 0, w, h);
        final RectF rectF = new RectF(rect);

        // make sure that our rounded corner is scaled appropriately
        final float roundPx = pixels * densityMultiplier;

        paint.setAntiAlias(true);
        canvas.drawARGB(0, 0, 0, 0);
        paint.setColor(color);
        canvas.drawRoundRect(rectF, roundPx, roundPx, paint);

        // draw rectangles over the corners we want to be square
        if (squareTL) {
            canvas.drawRect(0, 0, w / 2, h / 2, paint);
        }
        if (squareTR) {
            canvas.drawRect(w / 2, 0, w, h / 2, paint);
        }
        if (squareBL) {
            canvas.drawRect(0, h / 2, w / 2, h, paint);
        }
        if (squareBR) {
            canvas.drawRect(w / 2, h / 2, w, h, paint);
        }

        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
        canvas.drawBitmap(input, 0, 0, paint);

        return output;
    }

    Bitmap getCircledBitmap(Bitmap bitmap) {

        Bitmap result = Bitmap.createBitmap(bitmap.getWidth(),
                bitmap.getHeight(), Bitmap.Config.ARGB_8888);

        Canvas canvas = new Canvas(result);

        int color = Color.BLUE;
        Paint paint = new Paint();
        Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());

        paint.setAntiAlias(true);
        canvas.drawARGB(0, 0, 0, 0);
        paint.setColor(color);
        // canvas.drawRoundRect(rectF, roundPx, roundPx, paint);
        canvas.drawCircle(bitmap.getWidth() / 2, bitmap.getHeight() / 2,
                bitmap.getHeight() / 2, paint);

        paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
        canvas.drawBitmap(bitmap, rect, rect, paint);

        return result;
    }

}

0

如果您正在使用PorterDuffXfermode,请尝试禁用硬件加速:

在整个活动的清单中:

<activity android:hardwareAccelerated="false" />

或者在你使用xfermode的特定视图上:

myView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接