在Android中,ImageView默认是一个矩形。如何使它成为一个圆角矩形(剪辑我的位图的所有4个角为圆角矩形)在ImageView?


请注意,从2021年起,只需使用ShapeableImageView


当前回答

如果你不希望边框影响图像,使用这个类。不幸的是,我没有找到任何方法来绘制画布上的透明区域来到onDraw()。这里创建了一个新的位图它是画在一个真实的画布上的。

如果您想要创建一个消失的边界,该视图非常有用。如果你将borderWidth设置为0,边框将会消失,图像仍然保持圆角,就像边界一样。也就是说,它看起来就像边界完全由图像边缘绘制。

import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.PorterDuff
import android.graphics.PorterDuffXfermode
import android.graphics.RectF
import android.util.AttributeSet
import androidx.appcompat.widget.AppCompatImageView


class RoundedImageViewWithBorder @JvmOverloads constructor(
        context: Context,
        attrs: AttributeSet? = null,
        defStyleAttr: Int = 0) : AppCompatImageView(context, attrs, defStyleAttr) {

    var borderColor: Int = 0
        set(value) {
            invalidate()
            field = value
        }
    var borderWidth: Int = 0
        set(value) {
            invalidate()
            field = value
        }
    var cornerRadius: Float = 0f
        set(value) {
            invalidate()
            field = value
        }

    private var bitmapForDraw: Bitmap? = null
    private var canvasForDraw: Canvas? = null
    private val transparentPaint = Paint().apply {
        isAntiAlias = true
        color = Color.TRANSPARENT
        style = Paint.Style.STROKE
        xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC)
    }

    private val borderPaint = Paint().apply {
        isAntiAlias = true
        style = Paint.Style.STROKE
    }

    private val transparentAreaRect = RectF()
    private val borderRect = RectF()

    init {
        val typedArray = context.obtainStyledAttributes(attrs, R.styleable.RoundedImageViewWithBorder)

        try {
            borderWidth = typedArray.getDimensionPixelSize(R.styleable.RoundedImageViewWithBorder_border_width, 0)
            borderColor = typedArray.getColor(R.styleable.RoundedImageViewWithBorder_border_color, 0)
            cornerRadius = typedArray.getDimensionPixelSize(R.styleable.RoundedImageViewWithBorder_corner_radius, 0).toFloat()

        } finally {
            typedArray.recycle()
        }
    }

    @SuppressLint("CanvasSize", "DrawAllocation")
    override fun onDraw(canvas: Canvas) {
        if (canvas.height <=0 || canvas.width <=0) {
            return
        }

        if (canvasForDraw?.height != canvas.height || canvasForDraw?.width != canvas.width) {
            val newBitmap = Bitmap.createBitmap(canvas.width, canvas.height, Bitmap.Config.ARGB_8888)
            bitmapForDraw = newBitmap
            canvasForDraw = Canvas(newBitmap)
        }
        
        bitmapForDraw?.eraseColor(Color.TRANSPARENT)

        // Draw existing content
        super.onDraw(canvasForDraw)

        if (borderWidth > 0) {
            canvasForDraw?.let { drawWithBorder(it) }
        } else {
            canvasForDraw?.let { drawWithoutBorder(it) }
        }

        // Draw everything on real canvas
        bitmapForDraw?.let { canvas.drawBitmap(it, 0f, 0f, null) }
    }

    private fun drawWithBorder(canvas: Canvas) {
        // Draw transparent area
        transparentPaint.strokeWidth = borderWidth.toFloat() * 4
        transparentAreaRect.apply {
            left = -borderWidth.toFloat() * 1.5f
            top = -borderWidth.toFloat() * 1.5f
            right = canvas.width.toFloat() + borderWidth.toFloat() * 1.5f
            bottom = canvas.height.toFloat() + borderWidth.toFloat() * 1.5f
        }
        canvasForDraw?.drawRoundRect(transparentAreaRect, borderWidth.toFloat() * 2 + cornerRadius, borderWidth.toFloat() * 2 + cornerRadius, transparentPaint)

        // Draw border
        borderPaint.color = borderColor
        borderPaint.strokeWidth = borderWidth.toFloat()
        borderRect.apply {
            left = borderWidth.toFloat() / 2
            top = borderWidth.toFloat() / 2
            right = canvas.width.toFloat() - borderWidth.toFloat() / 2
            bottom = canvas.height.toFloat() - borderWidth.toFloat() / 2
        }
        canvas.drawRoundRect(borderRect, cornerRadius - borderWidth.toFloat() / 2, cornerRadius - borderWidth.toFloat() / 2, borderPaint)
    }

    private fun drawWithoutBorder(canvas: Canvas) {
        // Draw transparent area
        transparentPaint.strokeWidth = cornerRadius * 4
        transparentAreaRect.apply {
            left = -cornerRadius * 2
            top = -cornerRadius * 2
            right = canvas.width.toFloat() + cornerRadius * 2
            bottom = canvas.height.toFloat() + cornerRadius * 2
        }
        canvasForDraw?.drawRoundRect(transparentAreaRect, cornerRadius * 3, cornerRadius * 3, transparentPaint)
    }

}

值:

<declare-styleable name="RoundedImageViewWithBorder">
    <attr name="corner_radius" format="dimension|string" />
    <attr name="border_width" format="dimension|reference" />
    <attr name="border_color" format="color|reference" />
</declare-styleable>

其他回答

这里要回答的问题是: 如何在Android中创建一个圆形ImageView ?

public static Bitmap getRoundBitmap(Bitmap bitmap) {

    int min = Math.min(bitmap.getWidth(), bitmap.getHeight());

    Bitmap bitmapRounded = Bitmap.createBitmap(min, min, bitmap.getConfig());

    Canvas canvas = new Canvas(bitmapRounded);
    Paint paint = new Paint();
    paint.setAntiAlias(true);
    paint.setShader(new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP));
    canvas.drawRoundRect((new RectF(0.0f, 0.0f, min, min)), min/2, min/2, paint);

    return bitmapRounded;
}

您应该扩展ImageView并绘制自己的圆角矩形。

如果你想在图像周围添加一个框架,你也可以在布局中将圆形框架叠加在图像视图的顶部。

例如,通过使用FrameLayout将帧叠加到原始图像上。FrameLayout的第一个元素将是你想要显示的图像。然后添加另一个ImageView与框架。第二个ImageView将显示在原始ImageView的顶部,因此Android将在原始ImageView上方绘制它的内容。

    /**
 * Background Async task to load user profile picture from url
 */
private class LoadProfileImage extends AsyncTask<String, Void, RoundedBitmapDrawable> {
    ImageView profileImageView;

    public LoadProfileImage(ImageView profileImageView) {
        this.profileImageView = profileImageView;
    }

    protected RoundedBitmapDrawable doInBackground(String... urls) {
        String photoUrl = urls[0];
        RoundedBitmapDrawable profileRoundedDrawable = null;
        try {
            InputStream inputStream = new java.net.URL(photoUrl).openStream();
            Resources res = getResources();

            profileRoundedDrawable = RoundedBitmapDrawableFactory.create(res, inputStream);
            profileRoundedDrawable.setCircular(true);
        } catch (Exception e) {
            Log.e("Error", e.getMessage());
            e.printStackTrace();
        }
        return profileRoundedDrawable;
    }

    protected void onPostExecute(RoundedBitmapDrawable result) {
        profileImageView.setImageDrawable(result);
    }
}

应用一个形状到你的imageView如下所示:

<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle" >
    <solid android:color="#faf5e6" />
    <stroke
        android:width="1dp"
        android:color="#808080" />
    <corners android:radius="15dp" />
    <padding
        android:bottom="5dp"
        android:left="5dp"
        android:right="5dp"
        android:top="5dp" />
</shape>

这可能对你的朋友有帮助。

多亏了melanke,你可以使用一个自定义类并创建一个自定义循环ImageView。

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;

public class MLRoundedImageView extends android.support.v7.widget.AppCompatImageView {

    public MLRoundedImageView(Context context) {
        super(context);
    }

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

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

    @Override
    protected void onDraw(Canvas canvas) {

        Drawable drawable = getDrawable();

        if (drawable == null) {
            return;
        }

        if (getWidth() == 0 || getHeight() == 0) {
            return;
        }
        Bitmap b = ((BitmapDrawable) drawable).getBitmap();
        Bitmap bitmap = b.copy(Bitmap.Config.ARGB_8888, true);

        int w = getWidth(), h = getHeight();

        Bitmap roundBitmap = getCroppedBitmap(bitmap, w);
        canvas.drawBitmap(roundBitmap, 0, 0, null);

    }

    public static Bitmap getCroppedBitmap(Bitmap bmp, int radius) {
        Bitmap sbmp;

        if (bmp.getWidth() != radius || bmp.getHeight() != radius) {
            float smallest = Math.min(bmp.getWidth(), bmp.getHeight());
            float factor = smallest / radius;
            sbmp = Bitmap.createScaledBitmap(bmp, (int)(bmp.getWidth() / factor), (int)(bmp.getHeight() / factor), false);
        } else {
            sbmp = bmp;
        }

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

        final int color = 0xffa19774;
        final Paint paint = new Paint();
        final Rect rect = new Rect(0, 0, radius, radius);

        paint.setAntiAlias(true);
        paint.setFilterBitmap(true);
        paint.setDither(true);
        canvas.drawARGB(0, 0, 0, 0);
        paint.setColor(Color.parseColor("#BAB399"));
        canvas.drawCircle(radius / 2 + 0.7f,
                radius / 2 + 0.7f, radius / 2 + 0.1f, paint);
        paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
        canvas.drawBitmap(sbmp, rect, rect, paint);

        return output;
    }

}

然后在XML中使用它:

<your.package.name.MLRoundedImageView
..
/>