Java 我应该如何在 Android 中给图像圆角?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1705239/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 21:44:49  来源:igfitidea点击:

How should I give images rounded corners in Android?

javaandroidimage

提问by iamkoa

I would like to change an image I loaded to have round corners.

我想将我加载的图像更改为具有圆角。

Any hints, tutorials, best practices you know of?

您知道的任何提示、教程、最佳实践?

采纳答案by Ralphleon

For a more controlled method draw a rounded rectangle and mask it onto your image using the porter-duff Xfer mode of the paint.

对于更可控的方法,绘制一个圆角矩形并使用油漆的 porter-duff Xfer 模式将其遮罩到您的图像上。

First setup the Xfer paint and the rounded bitmap:

首先设置 Xfer 绘制和圆形位图:

Bitmap myCoolBitmap = ... ; // <-- Your bitmap you want rounded    
int w = myCoolBitmap.getWidth(), h = myCoolBitmap.getHeight();

// We have to make sure our rounded corners have an alpha channel in most cases
Bitmap rounder = Bitmap.createBitmap(w,h,Bitmap.Config.ARGB_8888);
Canvas canvas = 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.RED);

// We're just reusing xferPaint to paint a normal looking rounded box, the 20.f
// is the amount we're rounding by.
canvas.drawRoundRect(new RectF(0,0,w,h), 20.0f, 20.0f, xferPaint);     

// Now we apply the 'magic sauce' to the paint  
xferPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));

Now apply this bitmap ontop of your image:

现在将此位图应用到您的图像上:

Bitmap result = Bitmap.createBitmap(myCoolBitmap.getWidth(), myCoolBitmap.getHeight() ,Bitmap.Config.ARGB_8888);
Canvas resultCanvas = new Canvas(result)
resultCanvas.drawBitmap(myCoolBitmap, 0, 0, null);
resultCanvas.drawBitmap(rounder, 0, 0, xferPaint);

Bitmap with rounded corners now resides in result.

带圆角的位图现在位于结果中。

回答by Navin

How about creating a NinePatchDrawableimage that has just rounded corners and has a transparent body. Overlay your image with an appropriately re-sized version of your NinePatchDrawable.

如何创建一个只有圆角和透明主体的NinePatchDrawable图像。使用适当调整大小的 NinePatchDrawable 版本覆盖您的图像。

回答by Jerry

Why not use clipPath?

为什么不使用clipPath?

protected void onDraw(Canvas canvas) {
    Path clipPath = new Path();
    float radius = 10.0f;
    float padding = radius / 2;
    int w = this.getWidth();
    int h = this.getHeight();
    clipPath.addRoundRect(new RectF(padding, padding, w - padding, h - padding), radius, radius, Path.Direction.CW);
    canvas.clipPath(clipPath);
    super.onDraw(canvas);
}

回答by Swati

package com.pkg;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Bitmap.Config;
import android.graphics.PorterDuff.Mode;
import android.os.Bundle;
import android.os.Environment;
import android.widget.ImageView;

public class RoundedImage extends Activity {
    /** Called when the activity is first created. */
    ImageView imag;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        imag=(ImageView)findViewById(R.id.image);

        //ImageView img1=(ImageView)findViewById(R.id.imageView1);
        BitmapFactory.Options bitopt=new BitmapFactory.Options();
        bitopt.inSampleSize=1;
        // String img=Environment.getExternalStorageDirectory().toString();
        // String filepath =Environment.getExternalStorageDirectory().toString();
        String filepath ="/mnt/sdcard/LOST.DIR";
        File imagefile = new File(filepath + "/logo.jpg");
        FileInputStream fis = null;
        try 
        {
        fis = new FileInputStream(imagefile);
        }  
        catch (FileNotFoundException e1)
        {
        // TODO Auto-generated catch block
        e1.printStackTrace();
        }
        Bitmap bi = BitmapFactory.decodeStream(fis);
        if(bi!=null){
            imag.setImageBitmap(getRoundedCornerBitmap(bi));
        }

    }

    public static Bitmap getRoundedCornerBitmap(Bitmap bitmap) {
    Bitmap output = Bitmap.createBitmap(bitmap.getWidth(),
         bitmap.getHeight(), 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 = 12;

    paint.setAntiAlias(true);
    canvas.drawARGB(0, 0, 0, 0);
    paint.setColor(color);

    canvas.drawRoundRect(rectF, roundPx, roundPx, paint);

    paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
    canvas.drawBitmap(bitmap, rect, rect, paint);

    return output;
    }
}

回答by Dheeraj Vepakomma

Romain Guy himself writes about this in his blog:

Romain Guy 本人在他的博客中写道:

To generate the rounded images I simply wrote a custom Drawable that draws a rounded rectangle using Canvas.drawRoundRect(). The trick is to use a Paint with a BitmapShader to fill the rounded rectangle with a texture instead of a simple color. Here is what the code looks like:

为了生成圆角图像,我简单地编写了一个自定义 Drawable,它使用 Canvas.drawRoundRect() 绘制一个圆角矩形。诀窍是使用带有 BitmapShader 的 Paint 用纹理而不是简单的颜色填充圆角矩形。下面是代码的样子:

BitmapShader shader;
shader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);

Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setShader(shader);

RectF rect = new RectF(0.0f, 0.0f, width, height);

// rect contains the bounds of the shape
// radius is the radius in pixels of the rounded corners
// paint contains the shader that will texture the shape
canvas.drawRoundRect(rect, radius, radius, paint);

The sample application goes a little further and fakes a vignette effect by combining the BitmapShader with a RadialGradient.

示例应用程序更进一步,通过将 BitmapShader 与 RadialGradient 组合来伪造晕影效果。

回答by Learn OpenGL ES

Here's a way I discovered to do it with an ImageView. I tried other methods, including the answers here and on similar questions, but I found that they didn't work well for me, as I needed the corners to be applied to the image view and not directly to the bitmap. Applying directly to the bitmap won't work if you're scaling/cropping/panning that bitmap, since the corners will also be scaled/cropped/panned.

这是我发现的一种使用 ImageView 实现的方法。我尝试了其他方法,包括此处和类似问题的答案,但我发现它们对我来说效果不佳,因为我需要将角应用于图像视图而不是直接应用于位图。如果您正在缩放/裁剪/平移该位图,则直接应用于位图将不起作用,因为角也将被缩放/裁剪/平移。

public class RoundedCornersImageView extends ImageView {
    private final Paint restorePaint = new Paint();
    private final Paint maskXferPaint = new Paint();
    private final Paint canvasPaint = new Paint();

    private final Rect bounds = new Rect();
    private final RectF boundsf = new RectF();

    public RoundedCornersImageView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init();
    }

    public RoundedCornersImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public RoundedCornersImageView(Context context) {
        super(context);
        init();
    }

    private void init() {
        canvasPaint.setAntiAlias(true);
        canvasPaint.setColor(Color.argb(255, 255, 255, 255));
        restorePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP));
        maskXferPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY));
    }

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.getClipBounds(bounds);
        boundsf.set(bounds);

        canvas.saveLayer(boundsf, restorePaint, Canvas.ALL_SAVE_FLAG);
        super.onDraw(canvas);

        canvas.saveLayer(boundsf, maskXferPaint, Canvas.ALL_SAVE_FLAG);
        canvas.drawARGB(0, 0, 0, 0);
        canvas.drawRoundRect(boundsf, 75, 75, canvasPaint);

        canvas.restore();
        canvas.restore();
    }
}

Here's an alternative that uses hardware layers for the final layer composite:

这是将硬件层用于最终层复合的替代方案:

public class RoundedCornersImageView extends ImageView {
    private final Paint restorePaint = new Paint();
    private final Paint maskXferPaint = new Paint();
    private final Paint canvasPaint = new Paint();

    private final Rect bounds = new Rect();
    private final RectF boundsf = new RectF();

    public RoundedCornersImageView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init();
    }

    public RoundedCornersImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public RoundedCornersImageView(Context context) {
        super(context);
        init();
    }

    private void init() {
        canvasPaint.setAntiAlias(true);
        canvasPaint.setColor(Color.argb(255, 255, 255, 255));
        restorePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP));
        maskXferPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY));

        setLayerType(View.LAYER_TYPE_HARDWARE, restorePaint);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.getClipBounds(bounds);
        boundsf.set(bounds);

        super.onDraw(canvas);

        canvas.saveLayer(boundsf, maskXferPaint, Canvas.ALL_SAVE_FLAG);
        canvas.drawARGB(0, 0, 0, 0);
        canvas.drawRoundRect(boundsf, 75, 75, canvasPaint);

        canvas.restore();
    }
}

At first I wasn't able to get it to work with this method because my corners were becoming black; I later realized what the problem was after reading this question: Android how to apply mask on ImageView?. It turns out that modifying the alpha in the canvas is actually "scratching it out" directly on the screen, and punching a hole to the underlying window which is black. That's why two layers are needed: one to apply the mask, and another to apply the composited image to the screen.

起初我无法用这种方法让它工作,因为我的角落变黑了;后来我在阅读这个问题后意识到问题出在哪里:Android how to apply mask on ImageView? . 事实证明,修改画布中的 alpha 实际上是直接在屏幕上“刮掉”它,并在底层的黑色窗口上打一个洞。这就是为什么需要两层:一层用于应用蒙版,另一层用于将合成图像应用到屏幕上。

回答by Nikolai

Here's another rounded ImageViewimplementation using Path. The performance is great, but in certain conditions some bugs may appear on emulators because of the hardware drawing.

这是ImageView使用Path. 性能很好,但在某些情况下,由于硬件绘图,模拟器上可能会出现一些错误。

public class RoundImageView extends ImageView {

    private Path mPath;
    private RectF mRect;
    private Paint mPaint;

    private int mCornerRadius;
    private float mImageAlpha;
    private boolean mIsCircular;

    public RoundImageView(Context context) {
        this(context, null);
    }

    public RoundImageView(Context context, AttributeSet attrs) {
        this(context, attrs, R.attr.roundImageViewStyle);
    }

    public RoundImageView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);

        TypedArray a = context.obtainStyledAttributes(
                attrs, R.styleable.RoundImageView, defStyle, 0);

        mCornerRadius = a.getDimensionPixelSize(R.styleable.RoundImageView_cornerRadius, 0);
        mIsCircular = a.getBoolean(R.styleable.RoundImageView_isCircular, false);
        mImageAlpha = a.getFloat(R.styleable.RoundImageView_imageAlpha, 1);

        a.recycle();

        setAlpha((int) (mImageAlpha * 255));

        // Avoid expensive off-screen drawing
        setLayerType(LAYER_TYPE_HARDWARE, null);

        mPath = new Path();

        mRect = new RectF();

        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        mPath.reset();

        if (mIsCircular) {
            float halfWidth = canvas.getWidth() / 2;
            float halfHeight = canvas.getHeight() / 2;
            float radius = Math.max(halfWidth, halfHeight);

            mPath.addCircle(halfWidth, halfHeight, radius, Path.Direction.CW);
        } else {
            mRect.right = canvas.getWidth();
            mRect.bottom = canvas.getHeight();

            mPath.addRoundRect(mRect, mCornerRadius, mCornerRadius, Path.Direction.CW);
        }

        canvas.drawPath(mPath, mPaint);
    }
}

P.S. Learn OpenGL ESprovided the best solution. It's very smooth and works on emulators too.

PS Learn OpenGL ES提供了最好的解决方案。它非常流畅,也适用于模拟器。

回答by Yogesh Alai

best solution I found ->

我找到的最佳解决方案->

1) create rounded corner drawable. And set to imageview as background.

1)创建圆角drawable。并设置为 imageview 作为背景。

<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<corners
    android:radius="10dp" /></shape>

2) Then set image view object property of setClipToOutline(true) in java code.

2) 然后在java代码中设置setClipToOutline(true)的图像视图对象属性。

imageview.setClipToOutline(true);

It works like charm

它的作用就像魅力