问题
I have the following piece of code for rendering an imageView with rounded corners.
public class RoundedCornerImageView extends ImageView {
private int rounded;
public RoundedCornerImageView(Context context) {
super(context);
}
public RoundedCornerImageView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public RoundedCornerImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public int getRounded() {
return rounded;
}
public void setRounded(int rounded) {
this.rounded = rounded;
}
@Override
public void onDraw(Canvas canvas)
{
Drawable drawable = getDrawable();
int w = drawable.getIntrinsicHeight(),
h = drawable.getIntrinsicWidth();
Bitmap rounder = Bitmap.createBitmap(w,h,Bitmap.Config.ARGB_8888);
Canvas tmpCanvas = new Canvas(rounder);
// We're going to apply this paint eventually using a porter-duff xfer mode.
// This will allow us to only overwrite certain pixels. RED is arbitrary. This
// could be any color that was fully opaque (alpha = 255)
Paint xferPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
xferPaint.setColor(Color.WHITE);
// We're just reusing xferPaint to paint a normal looking rounded box, the 20.f
// is the amount we're rounding by.
tmpCanvas.drawRoundRect(new RectF(0,0,w,h), 10.0f, 10.0f, xferPaint);
// Now we apply the 'magic sauce' to the paint
xferPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
drawable.draw(canvas);
canvas.drawBitmap(rounder, 0, 0, xferPaint);
}
}
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background='#a3deef'
>
<com.example.scheduling_android.view.RoundedCornerImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/eventImageView"
android:adjustViewBounds="false"/>
</LinearLayout>
It works in that it is indeed cropping off the corners of the image. However, the problem arises when I try to render this inside a linearLayout that has a background color #a3deef. The resulting display is a background color of #a3deef with each image displayed with rounded corners where the 4 cropped corner are all in black.
What should I do to make the cropped corners transparent rather than black? Also, it would be great if someone could explain to me why it would black, and not any other colors too!
Thanks in advance.
回答1:
The approach you are taking doesn't really work well if the source isn't already a Bitmap
, primarily because it's better to draw the content into the Canvas
using transfer modes outside the drawing callback (so it only happens once instead of on every draw refresh) and calling Drawable.draw()
anywhere else won't produce the correct results since the bounds won't be set as you expect.
A significantly more efficient method of doing this is not to modify the source data and just apply a rounded clip to the drawing Canvas
. For really large radii, this can create a little aliasing, but at 10px it won't be noticeable. The only other drawback to this approach is that clipPath()
is not currently supported by hardware acceleration, so you will have to set the layer type for this view to software in order for the rendering to work on Android 3.0+
public class RoundedCornerImageView extends ImageView {
private Path roundedPath;
private int rounded;
public RoundedCornerImageView(Context context) {
super(context);
init();
}
public RoundedCornerImageView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public RoundedCornerImageView(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init() {
// If the application is hardware accelerated,
// must disable it for this view.
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
// Set a default radius
setRounded(10);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
if (w != oldw || h != oldh) {
roundedPath = new Path();
roundedPath.addRoundRect(new RectF(0, 0, w, h),
rounded, rounded, Path.Direction.CW);
}
}
public int getRounded() {
return rounded;
}
public void setRounded(int rounded) {
this.rounded = rounded;
roundedPath = new Path();
roundedPath.addRoundRect(new RectF(0, 0, getWidth(), getHeight()),
rounded, rounded, Path.Direction.CW);
}
@Override
protected void onDraw(Canvas canvas) {
//Apply the clip
canvas.clipPath(roundedPath);
//Let the view draw as normal
super.onDraw(canvas);
}
}
In the modified version, you simply update the clipping path each time either the view or the radius size changes, and apply that Path
as a clip to the Canvas
before drawing.
HTH
回答2:
Add this line to make the canvas transparent:
canvas.saveLayerAlpha(0, 0, canvas.getWidth(), canvas.getHeight(), 255, Canvas.HAS_ALPHA_LAYER_SAVE_FLAG);
Also, I use a reverse-filled rounded rectangle path to mask the bitmap so I don't need an intermediary masking bitmap like you do.
public class RoundedImageView extends ImageView {
private Path mMaskPath;
private Paint mMaskPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private int mCornerRadius = 10;
public RoundedImageView(Context context) {
super(context);
init();
}
public RoundedImageView(Context context, AttributeSet attributeSet) {
super(context, attributeSet);
init();
}
public RoundedImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init() {
ViewCompat.setLayerType(this, ViewCompat.LAYER_TYPE_SOFTWARE, null);
mMaskPaint.setXfermode(new PorterDuffXfermode(Mode.CLEAR));
}
/**
* Set the corner radius to use for the RoundedRectangle.
*
* @param Primitive int - The corner radius of the rounded rectangle.
*/
public void setCornerRadius(int cornerRadius) {
mCornerRadius = cornerRadius;
generateMaskPath(getWidth(), getHeight());
invalidate();
}
@Override
protected void onSizeChanged(int w, int h, int oldW, int oldH) {
super.onSizeChanged(w, h, oldW, oldH);
if (w != oldW || h != oldH) {
generateMaskPath(w, h);
}
}
private void generateMaskPath(int w, int h) {
mMaskPath = new Path();
mMaskPath.addRoundRect(new RectF(0,0,w,h), mCornerRadius, mCornerRadius, Direction.CW);
mMaskPath.setFillType(FillType.INVERSE_WINDING);
}
@Override
protected void onDraw(Canvas canvas) {
if(canvas.isOpaque()) { // If canvas is opaque, make it transparent
canvas.saveLayerAlpha(0, 0, canvas.getWidth(), canvas.getHeight(), 255, Canvas.HAS_ALPHA_LAYER_SAVE_FLAG);
}
super.onDraw(canvas);
if(mMaskPath != null) {
canvas.drawPath(mMaskPath, mMaskPaint);
}
}
}
回答3:
This is because your Canvas
that you are working with is not transparent. A non-transparent Canvas
has a black background.
This answer sets a SurfaceView
's Canvas
to be transparent. For you, it may be as simple as setting the background of the Bitmap
to be transparent.
Bitmap rounder = Bitmap.createBitmap(w,h,Bitmap.Config.ARGB_8888);
rounder.eraseColor(Color.TRANSPARENT);
You can also try this in your ImageView
, to set its background to 100% transparent:
setBackgroundColor(0);
来源:https://stackoverflow.com/questions/12358350/rendering-rounded-corners-for-imageview-on-android