用开源项目RoundedImageView来实现 圆形 / 圆角 / 椭圆的图片
该开源项目的地址:https://github.com/vinc3m1/RoundedImageView
我自己分流下载文件的:http://download.csdn.net/detail/shark0017/7656009
这个开源的项目是用来实现圆形、圆角、椭圆的项目,这是他上面的介绍。
There are many ways to create rounded corners in android, but this is the fastest and best one that I know of because it:
- does not create a copy of the original bitmap
- does not use a clipPath which is not hardware accelerated and not anti-aliased.
- does not use setXfermode to clip the bitmap and draw twice to the canvas.
接下来我们来用他的代码实现我们想要的效果。
首先,在attrs.xml文件中添加如下代码
<!-- corner_radius 边缘的弧度数 border_width 描边的宽度 border_color 描边的颜色 mutate_background 背景是否变化 oval 是否为圆形 android:scaleType 拉伸的方式 --> <declare-styleable name="RoundedImageView"> <attr name="corner_radius" format="dimension" /> <attr name="border_width" format="dimension" /> <attr name="border_color" format="color" /> <attr name="mutate_background" format="boolean" /> <attr name="oval" format="boolean" /> <attr name="android:scaleType" /> </declare-styleable>
corner_radius 边缘的弧度数
border_width 描边的宽度
border_color 描边的颜色
mutate_background 背景是否变化
oval 是否为圆形
android:scaleType 拉伸的方式
然后,将需要的类添加进自己的工程
RoundedImageView.java
package com.eva.views; import android.content.Context; import android.content.res.ColorStateList; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.drawable.Drawable; import android.graphics.drawable.LayerDrawable; import android.net.Uri; import android.util.AttributeSet; import android.util.Log; import android.widget.ImageView; public class RoundedImageView extends ImageView { public static final String TAG = "RoundedImageView"; public static final float DEFAULT_RADIUS = 0f; public static final float DEFAULT_BORDER_WIDTH = 0f; private static final ScaleType[] SCALE_TYPES = { ScaleType.MATRIX, ScaleType.FIT_XY, ScaleType.FIT_START, ScaleType.FIT_CENTER, ScaleType.FIT_END, ScaleType.CENTER, ScaleType.CENTER_CROP, ScaleType.CENTER_INSIDE }; private float cornerRadius = DEFAULT_RADIUS; private float borderWidth = DEFAULT_BORDER_WIDTH; private ColorStateList borderColor = ColorStateList.valueOf(RoundedDrawable.DEFAULT_BORDER_COLOR); private boolean isOval = false; private boolean mutateBackground = false; private int mResource; private Drawable mDrawable; private Drawable mBackgroundDrawable; private ScaleType mScaleType; public RoundedImageView(Context context) { super(context); } public RoundedImageView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public RoundedImageView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RoundedImageView, defStyle, 0); int index = a.getInt(R.styleable.RoundedImageView_android_scaleType, -1); if (index >= 0) { setScaleType(SCALE_TYPES[index]); } else { // default scaletype to FIT_CENTER setScaleType(ScaleType.FIT_CENTER); } cornerRadius = a.getDimensionPixelSize(R.styleable.RoundedImageView_corner_radius, -1); borderWidth = a.getDimensionPixelSize(R.styleable.RoundedImageView_border_width, -1); // don't allow negative values for radius and border if (cornerRadius < 0) { cornerRadius = DEFAULT_RADIUS; } if (borderWidth < 0) { borderWidth = DEFAULT_BORDER_WIDTH; } borderColor = a.getColorStateList(R.styleable.RoundedImageView_border_color); if (borderColor == null) { borderColor = ColorStateList.valueOf(RoundedDrawable.DEFAULT_BORDER_COLOR); } mutateBackground = a.getBoolean(R.styleable.RoundedImageView_mutate_background, false); isOval = a.getBoolean(R.styleable.RoundedImageView_oval, false); updateDrawableAttrs(); updateBackgroundDrawableAttrs(true); a.recycle(); } @Override protected void drawableStateChanged() { super.drawableStateChanged(); invalidate(); } /** * Return the current scale type in use by this ImageView. * * @attr ref android.R.styleable#ImageView_scaleType * @see android.widget.ImageView.ScaleType */ @Override public ScaleType getScaleType() { return mScaleType; } /** * Controls how the image should be resized or moved to match the size * of this ImageView. * * @param scaleType The desired scaling mode. * @attr ref android.R.styleable#ImageView_scaleType */ @Override public void setScaleType(ScaleType scaleType) { assert scaleType != null; if (mScaleType != scaleType) { mScaleType = scaleType; switch (scaleType) { case CENTER: case CENTER_CROP: case CENTER_INSIDE: case FIT_CENTER: case FIT_START: case FIT_END: case FIT_XY: super.setScaleType(ScaleType.FIT_XY); break; default: super.setScaleType(scaleType); break; } updateDrawableAttrs(); updateBackgroundDrawableAttrs(false); invalidate(); } } @Override public void setImageDrawable(Drawable drawable) { mResource = 0; mDrawable = RoundedDrawable.fromDrawable(drawable); updateDrawableAttrs(); super.setImageDrawable(mDrawable); } @Override public void setImageBitmap(Bitmap bm) { mResource = 0; mDrawable = RoundedDrawable.fromBitmap(bm); updateDrawableAttrs(); super.setImageDrawable(mDrawable); } @Override public void setImageResource(int resId) { if (mResource != resId) { mResource = resId; mDrawable = resolveResource(); updateDrawableAttrs(); super.setImageDrawable(mDrawable); } } @Override public void setImageURI(Uri uri) { super.setImageURI(uri); setImageDrawable(getDrawable()); } private Drawable resolveResource() { Resources rsrc = getResources(); if (rsrc == null) { return null; } Drawable d = null; if (mResource != 0) { try { d = rsrc.getDrawable(mResource); } catch (Exception e) { Log.w(TAG, "Unable to find resource: " + mResource, e); // Don't try again. mResource = 0; } } return RoundedDrawable.fromDrawable(d); } @Override public void setBackground(Drawable background) { setBackgroundDrawable(background); } private void updateDrawableAttrs() { updateAttrs(mDrawable); } private void updateBackgroundDrawableAttrs(boolean convert) { if (mutateBackground) { if (convert) { mBackgroundDrawable = RoundedDrawable.fromDrawable(mBackgroundDrawable); } updateAttrs(mBackgroundDrawable); } } private void updateAttrs(Drawable drawable) { if (drawable == null) { return; } if (drawable instanceof RoundedDrawable) { ((RoundedDrawable) drawable) .setScaleType(mScaleType) .setCornerRadius(cornerRadius) .setBorderWidth(borderWidth) .setBorderColor(borderColor) .setOval(isOval); } else if (drawable instanceof LayerDrawable) { // loop through layers to and set drawable attrs LayerDrawable ld = ((LayerDrawable) drawable); for (int i = 0, layers = ld.getNumberOfLayers(); i < layers; i++) { updateAttrs(ld.getDrawable(i)); } } } @Override @Deprecated public void setBackgroundDrawable(Drawable background) { mBackgroundDrawable = background; updateBackgroundDrawableAttrs(true); super.setBackgroundDrawable(mBackgroundDrawable); } public float getCornerRadius() { return cornerRadius; } public void setCornerRadius(int resId) { setCornerRadius(getResources().getDimension(resId)); } public void setCornerRadius(float radius) { if (cornerRadius == radius) { return; } cornerRadius = radius; updateDrawableAttrs(); updateBackgroundDrawableAttrs(false); } public float getBorderWidth() { return borderWidth; } public void setBorderWidth(int resId) { setBorderWidth(getResources().getDimension(resId)); } public void setBorderWidth(float width) { if (borderWidth == width) { return; } borderWidth = width; updateDrawableAttrs(); updateBackgroundDrawableAttrs(false); invalidate(); } public int getBorderColor() { return borderColor.getDefaultColor(); } public void setBorderColor(int color) { setBorderColor(ColorStateList.valueOf(color)); } public ColorStateList getBorderColors() { return borderColor; } public void setBorderColor(ColorStateList colors) { if (borderColor.equals(colors)) { return; } borderColor = (colors != null) ? colors : ColorStateList.valueOf(RoundedDrawable.DEFAULT_BORDER_COLOR); updateDrawableAttrs(); updateBackgroundDrawableAttrs(false); if (borderWidth > 0) { invalidate(); } } public boolean isOval() { return isOval; } public void setOval(boolean oval) { isOval = oval; updateDrawableAttrs(); updateBackgroundDrawableAttrs(false); invalidate(); } public boolean isMutateBackground() { return mutateBackground; } public void setMutateBackground(boolean mutate) { if (mutateBackground == mutate) { return; } mutateBackground = mutate; updateBackgroundDrawableAttrs(true); invalidate(); } }
RoundedDrawable.java
package com.eva.views; import android.content.res.ColorStateList; import android.graphics.Bitmap; import android.graphics.Bitmap.Config; import android.graphics.BitmapShader; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.ColorFilter; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Shader; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.LayerDrawable; import android.util.Log; import android.widget.ImageView.ScaleType; public class RoundedDrawable extends Drawable { public static final String TAG = "RoundedDrawable"; public static final int DEFAULT_BORDER_COLOR = Color.BLACK; private final RectF mBounds = new RectF(); private final RectF mDrawableRect = new RectF(); private final RectF mBitmapRect = new RectF(); private final BitmapShader mBitmapShader; private final Paint mBitmapPaint; private final int mBitmapWidth; private final int mBitmapHeight; private final RectF mBorderRect = new RectF(); private final Paint mBorderPaint; private final Matrix mShaderMatrix = new Matrix(); private float mCornerRadius = 0; private boolean mOval = false; private float mBorderWidth = 0; private ColorStateList mBorderColor = ColorStateList.valueOf(DEFAULT_BORDER_COLOR); private ScaleType mScaleType = ScaleType.FIT_CENTER; public RoundedDrawable(Bitmap bitmap) { mBitmapWidth = bitmap.getWidth(); mBitmapHeight = bitmap.getHeight(); mBitmapRect.set(0, 0, mBitmapWidth, mBitmapHeight); mBitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); mBitmapShader.setLocalMatrix(mShaderMatrix); mBitmapPaint = new Paint(); mBitmapPaint.setStyle(Paint.Style.FILL); mBitmapPaint.setAntiAlias(true); mBitmapPaint.setShader(mBitmapShader); mBorderPaint = new Paint(); mBorderPaint.setStyle(Paint.Style.STROKE); mBorderPaint.setAntiAlias(true); mBorderPaint.setColor(mBorderColor.getColorForState(getState(), DEFAULT_BORDER_COLOR)); mBorderPaint.setStrokeWidth(mBorderWidth); } public static RoundedDrawable fromBitmap(Bitmap bitmap) { if (bitmap != null) { return new RoundedDrawable(bitmap); } else { return null; } } public static Drawable fromDrawable(Drawable drawable) { if (drawable != null) { if (drawable instanceof RoundedDrawable) { // just return if it's already a RoundedDrawable return drawable; } else if (drawable instanceof LayerDrawable) { LayerDrawable ld = (LayerDrawable) drawable; int num = ld.getNumberOfLayers(); // loop through layers to and change to RoundedDrawables if possible for (int i = 0; i < num; i++) { Drawable d = ld.getDrawable(i); ld.setDrawableByLayerId(ld.getId(i), fromDrawable(d)); } return ld; } // try to get a bitmap from the drawable and Bitmap bm = drawableToBitmap(drawable); if (bm != null) { return new RoundedDrawable(bm); } else { Log.w(TAG, "Failed to create bitmap from drawable!"); } } return drawable; } public static Bitmap drawableToBitmap(Drawable drawable) { if (drawable instanceof BitmapDrawable) { return ((BitmapDrawable) drawable).getBitmap(); } Bitmap bitmap; int width = Math.max(drawable.getIntrinsicWidth(), 1); int height = Math.max(drawable.getIntrinsicHeight(), 1); try { bitmap = Bitmap.createBitmap(width, height, Config.ARGB_8888); Canvas canvas = new Canvas(bitmap); drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); drawable.draw(canvas); } catch (Exception e) { e.printStackTrace(); bitmap = null; } return bitmap; } @Override public boolean isStateful() { return mBorderColor.isStateful(); } @Override protected boolean onStateChange(int[] state) { int newColor = mBorderColor.getColorForState(state, 0); if (mBorderPaint.getColor() != newColor) { mBorderPaint.setColor(newColor); return true; } else { return super.onStateChange(state); } } private void updateShaderMatrix() { float scale; float dx; float dy; switch (mScaleType) { case CENTER: mBorderRect.set(mBounds); mBorderRect.inset((mBorderWidth) / 2, (mBorderWidth) / 2); mShaderMatrix.set(null); mShaderMatrix.setTranslate((int) ((mBorderRect.width() - mBitmapWidth) * 0.5f + 0.5f), (int) ((mBorderRect.height() - mBitmapHeight) * 0.5f + 0.5f)); break; case CENTER_CROP: mBorderRect.set(mBounds); mBorderRect.inset((mBorderWidth) / 2, (mBorderWidth) / 2); mShaderMatrix.set(null); dx = 0; dy = 0; if (mBitmapWidth * mBorderRect.height() > mBorderRect.width() * mBitmapHeight) { scale = mBorderRect.height() / (float) mBitmapHeight; dx = (mBorderRect.width() - mBitmapWidth * scale) * 0.5f; } else { scale = mBorderRect.width() / (float) mBitmapWidth; dy = (mBorderRect.height() - mBitmapHeight * scale) * 0.5f; } mShaderMatrix.setScale(scale, scale); mShaderMatrix.postTranslate((int) (dx + 0.5f) + mBorderWidth, (int) (dy + 0.5f) + mBorderWidth); break; case CENTER_INSIDE: mShaderMatrix.set(null); if (mBitmapWidth <= mBounds.width() && mBitmapHeight <= mBounds.height()) { scale = 1.0f; } else { scale = Math.min(mBounds.width() / (float) mBitmapWidth, mBounds.height() / (float) mBitmapHeight); } dx = (int) ((mBounds.width() - mBitmapWidth * scale) * 0.5f + 0.5f); dy = (int) ((mBounds.height() - mBitmapHeight * scale) * 0.5f + 0.5f); mShaderMatrix.setScale(scale, scale); mShaderMatrix.postTranslate(dx, dy); mBorderRect.set(mBitmapRect); mShaderMatrix.mapRect(mBorderRect); mBorderRect.inset((mBorderWidth) / 2, (mBorderWidth) / 2); mShaderMatrix.setRectToRect(mBitmapRect, mBorderRect, Matrix.ScaleToFit.FILL); break; default: case FIT_CENTER: mBorderRect.set(mBitmapRect); mShaderMatrix.setRectToRect(mBitmapRect, mBounds, Matrix.ScaleToFit.CENTER); mShaderMatrix.mapRect(mBorderRect); mBorderRect.inset((mBorderWidth) / 2, (mBorderWidth) / 2); mShaderMatrix.setRectToRect(mBitmapRect, mBorderRect, Matrix.ScaleToFit.FILL); break; case FIT_END: mBorderRect.set(mBitmapRect); mShaderMatrix.setRectToRect(mBitmapRect, mBounds, Matrix.ScaleToFit.END); mShaderMatrix.mapRect(mBorderRect); mBorderRect.inset((mBorderWidth) / 2, (mBorderWidth) / 2); mShaderMatrix.setRectToRect(mBitmapRect, mBorderRect, Matrix.ScaleToFit.FILL); break; case FIT_START: mBorderRect.set(mBitmapRect); mShaderMatrix.setRectToRect(mBitmapRect, mBounds, Matrix.ScaleToFit.START); mShaderMatrix.mapRect(mBorderRect); mBorderRect.inset((mBorderWidth) / 2, (mBorderWidth) / 2); mShaderMatrix.setRectToRect(mBitmapRect, mBorderRect, Matrix.ScaleToFit.FILL); break; case FIT_XY: mBorderRect.set(mBounds); mBorderRect.inset((mBorderWidth) / 2, (mBorderWidth) / 2); mShaderMatrix.set(null); mShaderMatrix.setRectToRect(mBitmapRect, mBorderRect, Matrix.ScaleToFit.FILL); break; } mDrawableRect.set(mBorderRect); mBitmapShader.setLocalMatrix(mShaderMatrix); } @Override protected void onBoundsChange(Rect bounds) { super.onBoundsChange(bounds); mBounds.set(bounds); updateShaderMatrix(); } @Override public void draw(Canvas canvas) { if (mOval) { if (mBorderWidth > 0) { canvas.drawOval(mDrawableRect, mBitmapPaint); canvas.drawOval(mBorderRect, mBorderPaint); } else { canvas.drawOval(mDrawableRect, mBitmapPaint); } } else { if (mBorderWidth > 0) { canvas.drawRoundRect(mDrawableRect, Math.max(mCornerRadius, 0), Math.max(mCornerRadius, 0), mBitmapPaint); canvas.drawRoundRect(mBorderRect, mCornerRadius, mCornerRadius, mBorderPaint); } else { canvas.drawRoundRect(mDrawableRect, mCornerRadius, mCornerRadius, mBitmapPaint); } } } @Override public int getOpacity() { return PixelFormat.TRANSLUCENT; } @Override public void setAlpha(int alpha) { mBitmapPaint.setAlpha(alpha); invalidateSelf(); } @Override public void setColorFilter(ColorFilter cf) { mBitmapPaint.setColorFilter(cf); invalidateSelf(); } @Override public void setDither(boolean dither) { mBitmapPaint.setDither(dither); invalidateSelf(); } @Override public void setFilterBitmap(boolean filter) { mBitmapPaint.setFilterBitmap(filter); invalidateSelf(); } @Override public int getIntrinsicWidth() { return mBitmapWidth; } @Override public int getIntrinsicHeight() { return mBitmapHeight; } public float getCornerRadius() { return mCornerRadius; } public RoundedDrawable setCornerRadius(float radius) { mCornerRadius = radius; return this; } public float getBorderWidth() { return mBorderWidth; } public RoundedDrawable setBorderWidth(float width) { mBorderWidth = width; mBorderPaint.setStrokeWidth(mBorderWidth); return this; } public int getBorderColor() { return mBorderColor.getDefaultColor(); } public RoundedDrawable setBorderColor(int color) { return setBorderColor(ColorStateList.valueOf(color)); } public ColorStateList getBorderColors() { return mBorderColor; } public RoundedDrawable setBorderColor(ColorStateList colors) { mBorderColor = colors != null ? colors : ColorStateList.valueOf(0); mBorderPaint.setColor(mBorderColor.getColorForState(getState(), DEFAULT_BORDER_COLOR)); return this; } public boolean isOval() { return mOval; } public RoundedDrawable setOval(boolean oval) { mOval = oval; return this; } public ScaleType getScaleType() { return mScaleType; } public RoundedDrawable setScaleType(ScaleType scaleType) { if (scaleType == null) { scaleType = ScaleType.FIT_CENTER; } if (mScaleType != scaleType) { mScaleType = scaleType; updateShaderMatrix(); } return this; } public Bitmap toBitmap() { return drawableToBitmap(this); } }
最后,在布局文件中使用并设置即可。注意要先添加命名空间
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:app="http://schemas.android.com/apk/res/com.eva.views" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#d6d6d6" android:orientation="vertical"> <!-- RoundedImageView iv = new RoundedImageView(context); iv.setScaleType(ScaleType.CENTER_CROP); iv.setCornerRadius(10); iv.setBorderWidth(2); iv.setBorderColor(Color.DKGRAY); iv.setRoundedBackground(true); iv.setImageDrawable(drawable); iv.setBackground(backgroundDrawable); iv.setOval(true); --> <com.eva.views.RoundedImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="16dp" android:layout_gravity="center_horizontal" android:src="@drawable/kale" android:scaleType="centerCrop" app:border_width="3dp" app:border_color="#333333" app:mutate_background="true" app:oval="true" /> <com.eva.views.RoundedImageView android:layout_width="200dp" android:layout_height="150dp" android:layout_marginTop="16dp" android:layout_gravity="center_horizontal" android:src="@drawable/kale" android:scaleType="centerCrop" app:corner_radius="40dip" app:border_width="6dp" app:border_color="#f8d67e" app:mutate_background="false" app:oval="true" /> <com.eva.views.RoundedImageView android:layout_width="200dp" android:layout_height="150dp" android:layout_marginTop="16dp" android:layout_gravity="center_horizontal" android:src="@drawable/kale" android:scaleType="fitXY" app:corner_radius="30dip" app:border_width="6dp" app:border_color="#50c0e9" app:mutate_background="true" app:oval="false" /> </LinearLayout>
当然,我们在代码中也可以进行设置。
RoundedImageView iv = new RoundedImageView(context); iv.setScaleType(ScaleType.CENTER_CROP); iv.setCornerRadius(10); iv.setBorderWidth(2); iv.setBorderColor(Color.DKGRAY); iv.setRoundedBackground(true); iv.setImageDrawable(drawable); iv.setBackground(backgroundDrawable); iv.setOval(true);
这样,我们就可以给图片加上圆角了。
源码下载:用RoundedImageView将图片裁剪为圆形、椭圆形
分类:
ImageView
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?