Android自定义图像视图形状

我正在创建一个自定义的ImageView,它会将我的图像裁剪成六边形并添加边框。 我想知道我的方法是否正确,或者如果我以错误的方式做这件事。 有一堆自定义库已经做到这一点,但它们中没有一个具有我期待的形状。 话虽如此,这更多的是关于最佳实践的问题。

预期结果

你可以在这个要点中看到完整的课程,但主要问题是这是最好的方法。 这对我来说是错误的,部分原因是由于某些神奇的数字,这意味着它可能会在一些设备上搞砸。

这是代码的肉:

      @Override
      protected void onDraw(Canvas canvas) {
        Drawable drawable = getDrawable();
        if (drawable == null || getWidth() == 0 || getHeight() == 0) {
          return;
        }

        Bitmap b = ((BitmapDrawable) drawable).getBitmap();
        Bitmap bitmap = b.copy(Bitmap.Config.ARGB_8888, true);

        int dimensionPixelSize = getResources().getDimensionPixelSize(R.dimen.width); // (width and height of ImageView)
        Bitmap drawnBitmap = drawCanvas(bitmap, dimensionPixelSize);
        canvas.drawBitmap(drawnBitmap, 0, 0, null);
      }

      private Bitmap drawCanvas(Bitmap recycledBitmap, int width) {
        final Bitmap bitmap = verifyRecycledBitmap(recycledBitmap, width);

        final Bitmap output = Bitmap.createBitmap(width, width, Bitmap.Config.ARGB_8888);
        final Canvas canvas = new Canvas(output);

        final Rect rect = new Rect(0, 0, width, width);
        final int offset = (int) (width / (double) 2 * Math.tan(30 * Math.PI / (double) 180)); // (width / 2) * tan(30deg)
        final int length = width - (2 * offset);

        final Path path = new Path();
        path.moveTo(width / 2, 0); // top
        path.lineTo(0, offset); // left top
        path.lineTo(0, offset + length); // left bottom
        path.lineTo(width / 2, width); // bottom
        path.lineTo(width, offset + length); // right bottom
        path.lineTo(width, offset); // right top
        path.close(); //back to top

        Paint paint = new Paint();
        paint.setStrokeWidth(4);
        canvas.drawARGB(0, 0, 0, 0);
        canvas.drawPath(path, paint);
        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
        canvas.drawBitmap(bitmap, rect, rect, paint); // draws the bitmap for the image

        paint.setColor(Color.parseColor("white"));
        paint.setStrokeWidth(4);
        paint.setDither(true);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeJoin(Paint.Join.ROUND);
        paint.setStrokeCap(Paint.Cap.ROUND);
        paint.setPathEffect(new CornerPathEffect(10));
        paint.setAntiAlias(true); // draws the border

        canvas.drawPath(path, paint);

        return output;
      }

我正在查看一些iOS代码,他们能够将实际图像作为掩码来实现此结果。 无论如何,Android上有这样的事情吗?


我一直在寻找最好的方法。 您的解决方案非常繁重,并且不适合动画。 clipPath方法不使用反锯齿功能,并且在特定版本的Android(4.0和4.1?)上不支持硬件加速。 似乎最好的方法(动画友好,反锯齿,非常干净和硬件加速)是使用Canvas图层:

Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
private static PorterDuffXfermode pdMode = new PorterDuffXfermode(PorterDuff.Mode.CLEAR);

@Override
public void draw(Canvas canvas) {
        int saveCount = canvas.saveLayer(0, 0, getWidth(), getHeight(),
                                         null, Canvas.ALL_SAVE_FLAG);

        super.draw(canvas);

        paint.setXfermode(pdMode);
        canvas.drawBitmap(maskBitmap, 0, 0, paint);

        canvas.restoreToCount(saveCount);
        paint.setXfermode(null);
}

您可以使用任何种类的遮罩,包括自定义形状和位图。 Carbon使用这种方法来即时旋转小部件的角落。


尽管它可能有效,但是在这个实现上有一些糟糕的错误:

  • onDraw阶段,您会分配一些非常大的对象,并导致可怕的性能。 最重要的是createBitmap但是您应该在onDraw不惜一切代价避免任何new 。 在初始化期间预先分配所有必需的对象,并在onDraw期间重新使用它们。

  • onSizeChanged期间,您应该只设置一次path 。 避免每个OnDraw上的所有路径

  • 如果您使用Picasso从互联网加载图像或者如果您想使用选择器,则您依赖于BitmapDrawable使用,那么此代码将不起作用。

  • 您不需要分配第二个位图,而是使用canvas.clipPath来提高效率。

  • 说所有的图纸应该是一个更有效的伪代码:

    @Override
    protected void onDraw(Canvas canvas) {
       canvas.save(CLIP_SAVE_FLAG); // save the clipping
       canvas.clipPath(path, Region.Op./*have to test which one*/ ); // cut the canvas
       super.onDraw(canvas); // do the normal drawing
       canvas.restore(); // restore the saved clipping
       canvas.drawPath(path, paint); // draw the extra border
    }
    
    链接地址: http://www.djcxy.com/p/31455.html

    上一篇: Android custom image view shape

    下一篇: Android Matrix isn't displaying properly, how do I fix it?