我正在创建一个自定义ImageView,它会将我的图像裁剪成六边形并添加边框。我想知道我的方法是否正确,或者我的方法是否错误。有一堆自定义库已经可以做到这一点,但没有一个开箱即用的具有我正在寻找的形状。话虽如此,这是一个关于最佳实践的问题。
您可以在此gist中看到完整的课程,但主要问题是这是最好的方法。这对我来说感觉不对,部分是因为一些神奇的数字意味着它可能会在某些设备上混乱。
以下是代码的内容:
@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上有没有做类似的事情?
答案 0 :(得分:3)
我一直在寻找最好的方法。您的解决方案非常繁重,并且与动画效果不佳。 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使用这种方法即时圆角小工具。
答案 1 :(得分:1)
即使它可能有效,但在这个实现上还有一些错误:
你在onDraw
阶段分配了一些非常大的对象,这会导致糟糕的表现。最重要的是createBitmap
,但您应该在new
期间不惜一切代价避免任何onDraw
。在初始化期间预先分配所有必需的对象,并在onDraw
期间重新使用它们。
您应该在path
期间设置onSizeChanged
一次。避免在每个OnDraw
如果您使用BitmapDrawable
从互联网上加载图片,或者如果您想使用选择器,那么您依赖于Picasso
的使用,那么此代码将不起作用
您不需要分配第二个位图,而是使用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
}