在Android上为imageView渲染圆角

3
我有以下的代码来渲染一个带圆角的imageView。
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>

它确实可以裁剪图像的角落,但是问题出现在我尝试在具有背景颜色#a3deef的linearLayout内进行渲染时。结果显示的是#a3deef背景色,其中每个图像都显示为带有圆角的四个裁剪角都是黑色。
我该怎么做才能使裁剪的角变成透明而不是黑色呢?另外,如果有人能解释一下为什么会是黑色而不是其他颜色,那就太好了!
提前致谢。
3个回答

6
你采用的方法并不适用于源数据不是Bitmap的情况,主要是因为最好使用转移模式将内容绘制到Canvas中,而不是在每次绘制刷新时调用Drawable.draw()(这样只会发生一次),在其他任何地方调用它都不会产生正确的结果,因为边界不会按照你的预期设置。
一个更有效的方法是不修改源数据,而是将圆角剪辑应用于绘图Canvas。对于非常大的半径,这可能会产生一些锯齿,但在10px时不会被注意到。这种方法的唯一缺点是clipPath()目前不支持硬件加速,因此您必须将此视图的层类型设置为软件,以便在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);
    }
}

在修改版本中,您只需在视图或半径大小更改时更新剪辑路径,并将该Path应用为剪辑到绘制前的Canvas即可。
希望这有所帮助。

1
谢谢,我在这个页面(http://tech.chitgoks.com/2012/08/28/how-to-add-round-corners-on-imageview-xml-in-android/)找到了你的解决方案。不过它没有说明需要禁用硬件加速以使视图正常工作。 - Display name
顺便说一下,剪辑不支持抗锯齿。 - Pedro Oliveira

4

添加此行以使画布透明:

canvas.saveLayerAlpha(0, 0, canvas.getWidth(), canvas.getHeight(), 255, Canvas.HAS_ALPHA_LAYER_SAVE_FLAG);

此外,我使用一个反向填充的圆角矩形路径来屏蔽位图,因此不需要像您那样使用中介遮罩位图。
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);
        }
    }
}

1
这是因为您正在使用的Canvas不透明。非透明的Canvas具有黑色背景。 此答案SurfaceViewCanvas设置为透明。对于您来说,可能只需将Bitmap的背景设置为透明即可。
Bitmap rounder = Bitmap.createBitmap(w,h,Bitmap.Config.ARGB_8888);
rounder.eraseColor(Color.TRANSPARENT);

您也可以尝试在 ImageView 中将其背景设置为 100% 透明:

setBackgroundColor(0);

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