背景
图片展示需要圆角,原生并没有提供这样的api实现。 网上很多大神都分享过如何做圆角。 1:path和矩形裁切 2:bitmap和path裁切 3:应该还有其他的
遇到的坑
我记得在Android 10上面裁切的api和path直接有点问题,顺序问题。然后具体不记得了,后面可以去查下。 结果:456789正常10无法裁切
今天的主角
代码是从某大神哪里得到的,具体忘记了。 先分享代码给大家吧。 这个控件目前在公司项目使用,也在上家公司的2个项目中使用,没有出现问题。
public class RadiusImageView extends AppCompatImageView {
private static final int DEFAULT_BORDER_COLOR = Color.GRAY;
private static final Bitmap.Config BITMAP_CONFIG = Bitmap.Config.ARGB_8888;
private static final int COLOR_DRAWABLE_DIMEN = 2;
private boolean mIsSelected = false;
private boolean mIsOval = false;
private boolean mIsCircle = false;
private int mBorderWidth;
private int mBorderColor;
private int mSelectedBorderWidth;
private int mSelectedBorderColor;
private int mSelectedMaskColor;
private boolean mIsTouchSelectModeEnabled = true;
private int mCornerRadius;
private Paint mBitmapPaint;
private Paint mBorderPaint;
private ColorFilter mColorFilter;
private ColorFilter mSelectedColorFilter;
private BitmapShader mBitmapShader;
private boolean mNeedResetShader = false;
private RectF mRectF = new RectF();
private Bitmap mBitmap;
private Matrix mMatrix;
private int mWidth;
private int mHeight;
public RadiusImageView(Context context) {
this(context, null, R.attr.RadiusImageViewStyle);
}
public RadiusImageView(Context context, AttributeSet attrs) {
this(context, attrs, R.attr.RadiusImageViewStyle);
}
public RadiusImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initAttrs(context, attrs, defStyleAttr);
}
private void initAttrs(Context context, AttributeSet attrs, int defStyleAttr) {
mBorderPaint = new Paint();
mBorderPaint.setAntiAlias(true);
mBorderPaint.setStyle(Paint.Style.STROKE);
mMatrix = new Matrix();
setScaleType(ScaleType.CENTER_CROP);
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.RadiusImageView, defStyleAttr, 0);
mBorderWidth = array.getDimensionPixelSize(R.styleable.RadiusImageView_riv_border_width, 0);
mBorderColor = array.getColor(R.styleable.RadiusImageView_riv_border_color, DEFAULT_BORDER_COLOR);
mSelectedBorderWidth = array.getDimensionPixelSize(
R.styleable.RadiusImageView_riv_selected_border_width, mBorderWidth);
mSelectedBorderColor = array.getColor(R.styleable.RadiusImageView_riv_selected_border_color, mBorderColor);
mSelectedMaskColor = array.getColor(R.styleable.RadiusImageView_riv_selected_mask_color, Color.TRANSPARENT);
if (mSelectedMaskColor != Color.TRANSPARENT) {
mSelectedColorFilter = new PorterDuffColorFilter(mSelectedMaskColor, PorterDuff.Mode.DARKEN);
}
mIsTouchSelectModeEnabled = array.getBoolean(R.styleable.RadiusImageView_riv_is_touch_select_mode_enabled, true);
mIsCircle = array.getBoolean(R.styleable.RadiusImageView_riv_is_circle, false);
if (!mIsCircle) {
mIsOval = array.getBoolean(R.styleable.RadiusImageView_riv_is_oval, false);
}
if (!mIsOval) {
mCornerRadius = array.getDimensionPixelSize(R.styleable.RadiusImageView_riv_corner_radius, 0);
}
array.recycle();
}
@Override
public void setScaleType(ScaleType scaleType) {
if (scaleType != ScaleType.CENTER_CROP) {
throw new IllegalArgumentException(String.format("不支持ScaleType %s", scaleType));
}
super.setScaleType(scaleType);
}
@Override
public void setAdjustViewBounds(boolean adjustViewBounds) {
if (adjustViewBounds) {
throw new IllegalArgumentException("不支持adjustViewBounds");
}
}
public void setBorderWidth(int borderWidth) {
if (mBorderWidth != borderWidth) {
mBorderWidth = borderWidth;
invalidate();
}
}
public void setBorderColor(@ColorInt int borderColor) {
if (mBorderColor != borderColor) {
mBorderColor = borderColor;
invalidate();
}
}
public void setCornerRadius(int cornerRadius) {
if (mCornerRadius != cornerRadius) {
mCornerRadius = cornerRadius;
if (!mIsCircle && !mIsOval) {
invalidate();
}
}
}
public void setSelectedBorderColor(@ColorInt int selectedBorderColor) {
if (mSelectedBorderColor != selectedBorderColor) {
mSelectedBorderColor = selectedBorderColor;
if (mIsSelected) {
invalidate();
}
}
}
public void setSelectedBorderWidth(int selectedBorderWidth) {
if (mSelectedBorderWidth != selectedBorderWidth) {
mSelectedBorderWidth = selectedBorderWidth;
if (mIsSelected) {
invalidate();
}
}
}
public void setSelectedMaskColor(@ColorInt int selectedMaskColor) {
if (mSelectedMaskColor != selectedMaskColor) {
mSelectedMaskColor = selectedMaskColor;
if (mSelectedMaskColor != Color.TRANSPARENT) {
mSelectedColorFilter = new PorterDuffColorFilter(mSelectedMaskColor, PorterDuff.Mode.DARKEN);
} else {
mSelectedColorFilter = null;
}
if (mIsSelected) {
invalidate();
}
}
mSelectedMaskColor = selectedMaskColor;
}
public void setCircle(boolean isCircle) {
if (mIsCircle != isCircle) {
mIsCircle = isCircle;
requestLayout();
invalidate();
}
}
public void setOval(boolean isOval) {
boolean forceUpdate = false;
if (isOval) {
if (mIsCircle) {
// 必须先取消圆形
mIsCircle = false;
forceUpdate = true;
}
}
if (mIsOval != isOval || forceUpdate) {
mIsOval = isOval;
requestLayout();
invalidate();
}
}
public int getBorderColor() {
return mBorderColor;
}
public int getBorderWidth() {
return mBorderWidth;
}
public int getCornerRadius() {
return mCornerRadius;
}
public int getSelectedBorderColor() {
return mSelectedBorderColor;
}
public int getSelectedBorderWidth() {
return mSelectedBorderWidth;
}
public int getSelectedMaskColor() {
return mSelectedMaskColor;
}
public boolean isCircle() {
return mIsCircle;
}
public boolean isOval() {
return !mIsCircle && mIsOval;
}
@Override
public boolean isSelected() {
return mIsSelected;
}
@Override
public void setSelected(boolean isSelected) {
if (mIsSelected != isSelected) {
mIsSelected = isSelected;
invalidate();
}
}
public void setTouchSelectModeEnabled(boolean touchSelectModeEnabled) {
mIsTouchSelectModeEnabled = touchSelectModeEnabled;
}
public boolean isTouchSelectModeEnabled() {
return mIsTouchSelectModeEnabled;
}
public void setSelectedColorFilter(ColorFilter cf) {
if (mSelectedColorFilter == cf) {
return;
}
mSelectedColorFilter = cf;
if (mIsSelected) {
invalidate();
}
}
@Override
public void setColorFilter(ColorFilter cf) {
if (mColorFilter == cf) {
return;
}
mColorFilter = cf;
if (!mIsSelected) {
invalidate();
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = getMeasuredWidth();
int height = getMeasuredHeight();
if (mIsCircle) {
int size = Math.min(width, height);
setMeasuredDimension(size, size);
} else {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
if (mBitmap == null) {
return;
}
boolean widthWrapContent = widthMode == MeasureSpec.AT_MOST || widthMode == MeasureSpec.UNSPECIFIED;
boolean heightWrapContent = heightMode == MeasureSpec.AT_MOST || heightMode == MeasureSpec.UNSPECIFIED;
float bmWidth = mBitmap.getWidth(), bmHeight = mBitmap.getHeight();
float scaleX = width / bmWidth, scaleY = height / bmHeight;
if (widthWrapContent && heightWrapContent) {
// 保证长宽比
if (scaleX >= 1 && scaleY >= 1) {
setMeasuredDimension((int) bmWidth, (int) bmHeight);
return;
}
if (scaleX >= 1) {
setMeasuredDimension((int) (bmHeight * scaleY), height);
return;
}
if (scaleY >= 1) {
setMeasuredDimension(width, (int) (bmHeight * scaleX));
return;
}
if (scaleX < scaleY) {
setMeasuredDimension(width, (int) (bmHeight * scaleX));
} else {
setMeasuredDimension((int) (bmWidth * scaleY), height);
}
} else if (widthWrapContent) {
setMeasuredDimension((int) (bmWidth * scaleY), height);
} else if (heightWrapContent) {
setMeasuredDimension(width, (int) (bmHeight * scaleX));
}
}
}
@Override
public void setImageDrawable(Drawable drawable) {
super.setImageDrawable(drawable);
setupBitmap();
}
@Override
public void setImageURI(Uri uri) {
super.setImageURI(uri);
setupBitmap();
}
private Bitmap getBitmap() {
Drawable drawable = getDrawable();
if (drawable == null) {
return null;
}
if (drawable instanceof BitmapDrawable) {
Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
if (bitmap == null) {
return null;
}
float bmWidth = bitmap.getWidth(), bmHeight = bitmap.getHeight();
if (bmWidth == 0 || bmHeight == 0) {
return null;
}
// ensure minWidth and minHeight
float minScaleX = getMinimumWidth() / bmWidth, minScaleY = getMinimumHeight() / bmHeight;
if (minScaleX > 1 || minScaleY > 1) {
float scale = Math.max(minScaleX, minScaleY);
Matrix matrix = new Matrix();
matrix.postScale(scale, scale);
return Bitmap.createBitmap(bitmap, 0, 0, (int) bmWidth, (int) bmHeight, matrix, false);
} else {
return bitmap;
}
}
try {
Bitmap bitmap;
if (drawable instanceof ColorDrawable) {
bitmap = Bitmap.createBitmap(COLOR_DRAWABLE_DIMEN, COLOR_DRAWABLE_DIMEN, BITMAP_CONFIG);
} else {
bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), BITMAP_CONFIG);
}
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
drawable.draw(canvas);
return bitmap;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public void setupBitmap() {
Bitmap bm = getBitmap();
if (bm == mBitmap) {
return;
}
mBitmap = bm;
if (mBitmap == null) {
mBitmapShader = null;
invalidate();
return;
}
mNeedResetShader = true;
mBitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
if (mBitmapPaint == null) {
mBitmapPaint = new Paint();
mBitmapPaint.setAntiAlias(true);
}
mBitmapPaint.setShader(mBitmapShader);
requestLayout();
invalidate();
}
private void updateBitmapShader() {
mMatrix.reset();
mNeedResetShader = false;
if (mBitmapShader == null || mBitmap == null) {
return;
}
final float bmWidth = mBitmap.getWidth();
final float bmHeight = mBitmap.getHeight();
final float scaleX = mWidth / bmWidth;
final float scaleY = mHeight / bmHeight;
final float scale = Math.max(scaleX, scaleY);
mMatrix.setScale(scale, scale);
mMatrix.postTranslate(-(scale * bmWidth - mWidth) / 2, -(scale * bmHeight - mHeight) / 2);
mBitmapShader.setLocalMatrix(mMatrix);
mBitmapPaint.setShader(mBitmapShader);
}
private void drawBitmap(Canvas canvas, int borderWidth) {
final float halfBorderWidth = borderWidth * 1.0f / 2;
mBitmapPaint.setColorFilter(mIsSelected ? mSelectedColorFilter : mColorFilter);
if (mIsCircle) {
int center = getWidth() / 2;
canvas.drawCircle(center, center, center - halfBorderWidth, mBitmapPaint);
} else {
mRectF.left = halfBorderWidth;
//noinspection SuspiciousNameCombination
mRectF.top = halfBorderWidth;
mRectF.right = getWidth() - halfBorderWidth;
mRectF.bottom = getHeight() - halfBorderWidth;
if (mIsOval) {
canvas.drawOval(mRectF, mBitmapPaint);
} else {
canvas.drawRoundRect(mRectF, mCornerRadius, mCornerRadius, mBitmapPaint);
}
}
}
private void drawBorder(Canvas canvas, int borderWidth) {
if (borderWidth <= 0) {
return;
}
final float halfBorderWidth = borderWidth * 1.0f / 2;
mBorderPaint.setColor(mIsSelected ? mSelectedBorderColor : mBorderColor);
mBorderPaint.setStrokeWidth(borderWidth);
if (mIsCircle) {
int radius = getWidth() / 2;
canvas.drawCircle(radius, radius, radius - halfBorderWidth, mBorderPaint);
} else {
mRectF.left = halfBorderWidth;
//noinspection SuspiciousNameCombination
mRectF.top = halfBorderWidth;
mRectF.right = getWidth() - halfBorderWidth;
mRectF.bottom = getHeight() - halfBorderWidth;
if (mIsOval) {
canvas.drawOval(mRectF, mBorderPaint);
} else {
canvas.drawRoundRect(mRectF, mCornerRadius, mCornerRadius, mBorderPaint);
}
}
}
@Override
protected void onDraw(Canvas canvas) {
int width = getWidth(), height = getHeight();
if (width <= 0 || height <= 0) {
return;
}
int borderWidth = mIsSelected ? mSelectedBorderWidth : mBorderWidth;
if (mBitmap == null || mBitmapShader == null) {
drawBorder(canvas, borderWidth);
return;
}
if (mWidth != width || mHeight != height || mNeedResetShader) {
mWidth = width;
mHeight = height;
updateBitmapShader();
}
drawBitmap(canvas, borderWidth);
drawBorder(canvas, borderWidth);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (!this.isClickable()) {
this.setSelected(false);
return super.onTouchEvent(event);
}
if (!mIsTouchSelectModeEnabled) {
return super.onTouchEvent(event);
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
this.setSelected(true);
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_SCROLL:
case MotionEvent.ACTION_OUTSIDE:
case MotionEvent.ACTION_CANCEL:
this.setSelected(false);
break;
default:
break;
}
return super.onTouchEvent(event);
}
}
需要的属性,自己在attrs中增加自定义属性
<!--圆角的img-->
<attr name="RadiusImageViewStyle" format="reference" />
<declare-styleable name="RadiusImageView">
<!--边框宽度,默认:0-->
<attr name="riv_border_width" format="dimension" />
<!--边框的颜色,默认:Color.GRAY-->
<attr name="riv_border_color" format="color" />
<!--选中时,覆盖在图片上的图层颜色,默认:Color.TRANSPARENT-->
<attr name="riv_selected_mask_color" format="color" />
<!--边框选中时的颜色,默认:Color.GRAY-->
<attr name="riv_selected_border_color" format="color" />
<!--边框选中时的宽度,默认:0-->
<attr name="riv_selected_border_width" format="dimension" />
<!--圆角的大小,默认0-->
<attr name="riv_corner_radius" format="dimension" />
<!--是否是椭圆形,默认:false-->
<attr name="riv_is_oval" format="boolean" />
<!--图片是否是圆形的,默认:false-->
<attr name="riv_is_circle" format="boolean" />
<!--图片是否可触摸选中,默认:true-->
<attr name="riv_is_touch_select_mode_enabled" format="boolean" />
</declare-styleable>
使用
控件的包名自己修改自己的
<com.debug.widget.RadiusImageView
android:id="@+id/iv_loop_cover"
android:layout_width="@dimen/sw_40dp"
android:layout_height="@dimen/sw_40dp"
android:scaleType="centerCrop"
app:riv_border_color="#ff0000"
app:riv_border_width="@dimen/sw_1dp"
app:riv_corner_radius="@dimen/sw_4dp"
tools:src="@mipmap/ic_launcher" />
可以增加边框颜色,边框大小,圆角的大小。
其他方案
我之前使用过glide的圆角api,在某些手机上面失效,貌似是vivo的具体型号不记得了。 目前我一直使用这个控件,因为现在没有Android 11的设备没测试还不知道情况。 线上也有11的设备,但是用户也没反馈过有图片问题,暂时就表明正常使用吧。 如果发现有问题,记得联系我:1124466590:qq