【问题标题】:Android: Circular DrawableAndroid:圆形可绘制
【发布时间】:2016-03-08 00:55:09
【问题描述】:

我制作了这个自定义可绘制对象,它应该将任何可绘制对象剪辑成圆形。但是在我的实现中,传递的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。

【问题讨论】:

  • 参考this
  • @pskink 但这是其他实现。我想知道我在 Porter/Duff 实现中的错误是什么。
  • 如果是正方形,则表示您正在绘制整个"DST"区域
  • 不,我尝试了这两种方法。你试试这整个代码。我都试过了。我们用paint绘制的是“Source”,绘制的地方是“Destination”,也就是说Image是source,画布上的圆圈是destination。
  • 好的,到目前为止你做了什么?你试过运行 APiDemos 吗?它在那里有一个 porter-duff 示例。您是否尝试关闭硬件加速?

标签: android drawable porter-duff


【解决方案1】:

在使用 Porter/Duff xfermode 模式时,画布默认没有 alpha 通道(根据我在 android 中的经验)。这就是整个图像(src)作为输出的原因。 Porter/Duff xfermode 行为基于 Alpha 通道。

在 Circular Drawable 2 Canvas 的许多实现中,其中一个被赋予 RGB 图像位图,而另一个仅被赋予带有圆形掩码的 Alpha 通道位图。应用 Porter/Duff xfermode 并将结果绘制在主画布上。

我的错误是我没有指定绘制函数的画布,在将画布上的图层用于 xfermode 时要考虑 alpha 通道。

draw函数会变成

@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。最后将其恢复到其保存计数。

【讨论】:

    【解决方案2】:

    所以基本上我都是用这个方法来获取圆角图片

    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 和其他类型,我所做的是首先压缩我的图像高度与我的图像视图相同的高度和宽度,以便整个图像适合图像视图并且不会发生缩放,或者我只是根据高度使用此函数计算要舍入多少角高度

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

    所以我使用如下代码:new Bitmap(getRoundedCornerBitmap(bmp, calculatePercentage(5, bmp.getHeight()))); where 5 is 5 of image height 这为所有图像提供了相同的曲线,即使某些图像比其他图像大 `

    【讨论】:

    • 未询问另一种解决方法。您对 scaletype 所做的假设是错误的。默认比例类型为 fitCenter。
    • @Paritosh Tonk 你可以接受我的帮助,或者我的工作是否完美
    • 感谢您的帮助。思考与工作无关。它关于概念以及为什么会发生这种情况。
    • 在编程时,我会避免使用会让我感到沮丧的概念,除非您是谷歌开发人员,否则您不想花一周时间试图弄清楚为什么在有其他方法时某些东西不起作用工作,您正在做的事情称为re-inventing the wheel 那里有很多教程概述了如何获得圆形图像
    • 我正在尝试重新发明轮子,因为圆角位图不是我想要的。我正在裁剪不同的东西并制作动画。 Rounded Bitmap 和rounded Drawable 的代码是可用的,但为了动画这个绘图的目的,我不想通过在每一帧中创建位图来给内存带来沉重的负担。如果事情是关于编程,那么没有所谓的概念就没有什么是可能的。人们不会雇用和付钱给你,因为你会编程。我希望你能理解我在这里想说的话。
    【解决方案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;
        }
    }
    

    【讨论】:

      【解决方案4】:

      您已经在支持库 v4 中优化了圆形可绘制对象的实现:

      Google reference

       /**
      * 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 ....
      

      【讨论】:

        【解决方案5】:
        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;
            }
        
        }
        

        【讨论】:

          【解决方案6】:

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

          在整个活动的清单中:

          <activity android:hardwareAccelerated="false" />
          

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

          myView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2016-09-21
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多