-
Android 自定义的一个圆角Imageview(可设置边框及颜色,圆角度数)
2019-08-30 10:58:11先看一下效果,边框宽度颜色都可以设置,也可以改变其中某个角的度数 一:Corner注解 @Retention(RetentionPolicy.SOURCE) @IntDef({ Corner.TOP_LEFT, Corner.TOP_RIGHT, Corner.BOTTOM_LEFT, Corner.BOTTOM_...共三个文件,和一些自定义属性,根据自己的需求使用
先看一下效果,边框宽度颜色都可以设置,也可以改变其中某个角的度数
一:Corner注解@Retention(RetentionPolicy.SOURCE) @IntDef({ Corner.TOP_LEFT, Corner.TOP_RIGHT, Corner.BOTTOM_LEFT, Corner.BOTTOM_RIGHT }) public @interface Corner { int TOP_LEFT = 0; int TOP_RIGHT = 1; int BOTTOM_RIGHT = 2; int BOTTOM_LEFT = 3; }
二:RoundedDrawable类
@SuppressWarnings("UnusedDeclaration") 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 Bitmap mBitmap; 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 final RectF mSquareCornersRect = new RectF(); private Shader.TileMode mTileModeX = Shader.TileMode.CLAMP; private Shader.TileMode mTileModeY = Shader.TileMode.CLAMP; private boolean mRebuildShader = true; private float mCornerRadius = 0f; // [ topLeft, topRight, bottomLeft, bottomRight ] private final boolean[] mCornersRounded = new boolean[]{true, true, true, true}; 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) { mBitmap = bitmap; mBitmapWidth = bitmap.getWidth(); mBitmapHeight = bitmap.getHeight(); mBitmapRect.set(0, 0, mBitmapWidth, mBitmapHeight); mBitmapPaint = new Paint(); mBitmapPaint.setStyle(Paint.Style.FILL); mBitmapPaint.setAntiAlias(true); 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); } } return drawable; } public static Bitmap drawableToBitmap(Drawable drawable) { if (drawable instanceof BitmapDrawable) { return ((BitmapDrawable) drawable).getBitmap(); } Bitmap bitmap; int width = Math.max(drawable.getIntrinsicWidth(), 2); int height = Math.max(drawable.getIntrinsicHeight(), 2); 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 (Throwable e) { e.printStackTrace(); Log.w(TAG, "Failed to create bitmap from drawable!"); bitmap = null; } return bitmap; } public Bitmap getSourceBitmap() { return mBitmap; } @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.reset(); 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.reset(); 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 / 2, (int) (dy + 0.5f) + mBorderWidth / 2); break; case CENTER_INSIDE: mShaderMatrix.reset(); 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.reset(); mShaderMatrix.setRectToRect(mBitmapRect, mBorderRect, Matrix.ScaleToFit.FILL); break; } mDrawableRect.set(mBorderRect); mRebuildShader = true; } @Override protected void onBoundsChange(@NonNull Rect bounds) { super.onBoundsChange(bounds); mBounds.set(bounds); updateShaderMatrix(); } @Override public void draw(@NonNull Canvas canvas) { if (mRebuildShader) { BitmapShader bitmapShader = new BitmapShader(mBitmap, mTileModeX, mTileModeY); if (mTileModeX == Shader.TileMode.CLAMP && mTileModeY == Shader.TileMode.CLAMP) { bitmapShader.setLocalMatrix(mShaderMatrix); } mBitmapPaint.setShader(bitmapShader); mRebuildShader = false; } if (mOval) { if (mBorderWidth > 0) { canvas.drawOval(mDrawableRect, mBitmapPaint); canvas.drawOval(mBorderRect, mBorderPaint); } else { canvas.drawOval(mDrawableRect, mBitmapPaint); } } else { if (any(mCornersRounded)) { float radius = mCornerRadius; if (mBorderWidth > 0) { canvas.drawRoundRect(mDrawableRect, radius, radius, mBitmapPaint); canvas.drawRoundRect(mBorderRect, radius, radius, mBorderPaint); redrawBitmapForSquareCorners(canvas); redrawBorderForSquareCorners(canvas); } else { canvas.drawRoundRect(mDrawableRect, radius, radius, mBitmapPaint); redrawBitmapForSquareCorners(canvas); } } else { canvas.drawRect(mDrawableRect, mBitmapPaint); if (mBorderWidth > 0) { canvas.drawRect(mBorderRect, mBorderPaint); } } } } private void redrawBitmapForSquareCorners(Canvas canvas) { if (all(mCornersRounded)) { // no square corners return; } if (mCornerRadius == 0) { return; // no round corners } float left = mDrawableRect.left; float top = mDrawableRect.top; float right = left + mDrawableRect.width(); float bottom = top + mDrawableRect.height(); float radius = mCornerRadius; if (!mCornersRounded[Corner.TOP_LEFT]) { mSquareCornersRect.set(left, top, left + radius, top + radius); canvas.drawRect(mSquareCornersRect, mBitmapPaint); } if (!mCornersRounded[Corner.TOP_RIGHT]) { mSquareCornersRect.set(right - radius, top, right, radius); canvas.drawRect(mSquareCornersRect, mBitmapPaint); } if (!mCornersRounded[Corner.BOTTOM_RIGHT]) { mSquareCornersRect.set(right - radius, bottom - radius, right, bottom); canvas.drawRect(mSquareCornersRect, mBitmapPaint); } if (!mCornersRounded[Corner.BOTTOM_LEFT]) { mSquareCornersRect.set(left, bottom - radius, left + radius, bottom); canvas.drawRect(mSquareCornersRect, mBitmapPaint); } } private void redrawBorderForSquareCorners(Canvas canvas) { if (all(mCornersRounded)) { // no square corners return; } if (mCornerRadius == 0) { return; // no round corners } float left = mDrawableRect.left; float top = mDrawableRect.top; float right = left + mDrawableRect.width(); float bottom = top + mDrawableRect.height(); float radius = mCornerRadius; float offset = mBorderWidth / 2; if (!mCornersRounded[Corner.TOP_LEFT]) { canvas.drawLine(left - offset, top, left + radius, top, mBorderPaint); canvas.drawLine(left, top - offset, left, top + radius, mBorderPaint); } if (!mCornersRounded[Corner.TOP_RIGHT]) { canvas.drawLine(right - radius - offset, top, right, top, mBorderPaint); canvas.drawLine(right, top - offset, right, top + radius, mBorderPaint); } if (!mCornersRounded[Corner.BOTTOM_RIGHT]) { canvas.drawLine(right - radius - offset, bottom, right + offset, bottom, mBorderPaint); canvas.drawLine(right, bottom - radius, right, bottom, mBorderPaint); } if (!mCornersRounded[Corner.BOTTOM_LEFT]) { canvas.drawLine(left - offset, bottom, left + radius, bottom, mBorderPaint); canvas.drawLine(left, bottom - radius, left, bottom, mBorderPaint); } } @Override public int getOpacity() { return PixelFormat.TRANSLUCENT; } @Override public int getAlpha() { return mBitmapPaint.getAlpha(); } @Override public void setAlpha(int alpha) { mBitmapPaint.setAlpha(alpha); invalidateSelf(); } @Override public ColorFilter getColorFilter() { return mBitmapPaint.getColorFilter(); } @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; } /** * @return the corner radius. */ public float getCornerRadius() { return mCornerRadius; } /** * @param corner the specific corner to get radius of. * @return the corner radius of the specified corner. */ public float getCornerRadius(@Corner int corner) { return mCornersRounded[corner] ? mCornerRadius : 0f; } /** * Sets all corners to the specified radius. * * @param radius the radius. * @return the {@link RoundedDrawable} for chaining. */ public RoundedDrawable setCornerRadius(float radius) { setCornerRadius(radius, radius, radius, radius); return this; } /** * Sets the corner radius of one specific corner. * * @param corner the corner. * @param radius the radius. * @return the {@link RoundedDrawable} for chaining. */ public RoundedDrawable setCornerRadius(@Corner int corner, float radius) { if (radius != 0 && mCornerRadius != 0 && mCornerRadius != radius) { throw new IllegalArgumentException("Multiple nonzero corner radii not yet supported."); } if (radius == 0) { if (only(corner, mCornersRounded)) { mCornerRadius = 0; } mCornersRounded[corner] = false; } else { if (mCornerRadius == 0) { mCornerRadius = radius; } mCornersRounded[corner] = true; } return this; } /** * Sets the corner radii of all the corners. * * @param topLeft top left corner radius. * @param topRight top right corner radius * @param bottomRight bototm right corner radius. * @param bottomLeft bottom left corner radius. * @return the {@link RoundedDrawable} for chaining. */ public RoundedDrawable setCornerRadius(float topLeft, float topRight, float bottomRight, float bottomLeft) { Set<Float> radiusSet = new HashSet<>(4); radiusSet.add(topLeft); radiusSet.add(topRight); radiusSet.add(bottomRight); radiusSet.add(bottomLeft); radiusSet.remove(0f); if (radiusSet.size() > 1) { throw new IllegalArgumentException("Multiple nonzero corner radii not yet supported."); } if (!radiusSet.isEmpty()) { float radius = radiusSet.iterator().next(); if (Float.isInfinite(radius) || Float.isNaN(radius) || radius < 0) { throw new IllegalArgumentException("Invalid radius value: " + radius); } mCornerRadius = radius; } else { mCornerRadius = 0f; } mCornersRounded[Corner.TOP_LEFT] = topLeft > 0; mCornersRounded[Corner.TOP_RIGHT] = topRight > 0; mCornersRounded[Corner.BOTTOM_RIGHT] = bottomRight > 0; mCornersRounded[Corner.BOTTOM_LEFT] = bottomLeft > 0; 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(@ColorInt 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 Shader.TileMode getTileModeX() { return mTileModeX; } public RoundedDrawable setTileModeX(Shader.TileMode tileModeX) { if (mTileModeX != tileModeX) { mTileModeX = tileModeX; mRebuildShader = true; invalidateSelf(); } return this; } public Shader.TileMode getTileModeY() { return mTileModeY; } public RoundedDrawable setTileModeY(Shader.TileMode tileModeY) { if (mTileModeY != tileModeY) { mTileModeY = tileModeY; mRebuildShader = true; invalidateSelf(); } return this; } private static boolean only(int index, boolean[] booleans) { for (int i = 0, len = booleans.length; i < len; i++) { if (booleans[i] != (i == index)) { return false; } } return true; } private static boolean any(boolean[] booleans) { for (boolean b : booleans) { if (b) { return true; } } return false; } private static boolean all(boolean[] booleans) { for (boolean b : booleans) { if (b) { return false; } } return true; } public Bitmap toBitmap() { return drawableToBitmap(this); } }
三:RoundedImageView类
@SuppressWarnings("UnusedDeclaration") public class RoundedImageView extends AppCompatImageView { // Constants for tile mode attributes private static final int TILE_MODE_UNDEFINED = -2; private static final int TILE_MODE_CLAMP = 0; private static final int TILE_MODE_REPEAT = 1; private static final int TILE_MODE_MIRROR = 2; public static final String TAG = "RoundedImageView"; public static final float DEFAULT_RADIUS = 0f; public static final float DEFAULT_BORDER_WIDTH = 0f; public static final Shader.TileMode DEFAULT_TILE_MODE = Shader.TileMode.CLAMP; 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 final float[] mCornerRadii = new float[] { DEFAULT_RADIUS, DEFAULT_RADIUS, DEFAULT_RADIUS, DEFAULT_RADIUS }; private Drawable mBackgroundDrawable; private ColorStateList mBorderColor = ColorStateList.valueOf(RoundedDrawable.DEFAULT_BORDER_COLOR); private float mBorderWidth = DEFAULT_BORDER_WIDTH; private ColorFilter mColorFilter = null; private boolean mColorMod = false; private Drawable mDrawable; private boolean mHasColorFilter = false; private boolean mIsOval = false; private boolean mMutateBackground = false; private int mResource; private int mBackgroundResource; private ScaleType mScaleType; private Shader.TileMode mTileModeX = DEFAULT_TILE_MODE; private Shader.TileMode mTileModeY = DEFAULT_TILE_MODE; 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); } float cornerRadiusOverride = a.getDimensionPixelSize(R.styleable.RoundedImageView_riv_corner_radius, -1); mCornerRadii[Corner.TOP_LEFT] = a.getDimensionPixelSize(R.styleable.RoundedImageView_riv_corner_radius_top_left, -1); mCornerRadii[Corner.TOP_RIGHT] = a.getDimensionPixelSize(R.styleable.RoundedImageView_riv_corner_radius_top_right, -1); mCornerRadii[Corner.BOTTOM_RIGHT] = a.getDimensionPixelSize(R.styleable.RoundedImageView_riv_corner_radius_bottom_right, -1); mCornerRadii[Corner.BOTTOM_LEFT] = a.getDimensionPixelSize(R.styleable.RoundedImageView_riv_corner_radius_bottom_left, -1); boolean any = false; for (int i = 0, len = mCornerRadii.length; i < len; i++) { if (mCornerRadii[i] < 0) { mCornerRadii[i] = 0f; } else { any = true; } } if (!any) { if (cornerRadiusOverride < 0) { cornerRadiusOverride = DEFAULT_RADIUS; } for (int i = 0, len = mCornerRadii.length; i < len; i++) { mCornerRadii[i] = cornerRadiusOverride; } } mBorderWidth = a.getDimensionPixelSize(R.styleable.RoundedImageView_riv_border_width, -1); if (mBorderWidth < 0) { mBorderWidth = DEFAULT_BORDER_WIDTH; } mBorderColor = a.getColorStateList(R.styleable.RoundedImageView_riv_border_color); if (mBorderColor == null) { mBorderColor = ColorStateList.valueOf(RoundedDrawable.DEFAULT_BORDER_COLOR); } mMutateBackground = a.getBoolean(R.styleable.RoundedImageView_riv_mutate_background, false); mIsOval = a.getBoolean(R.styleable.RoundedImageView_riv_oval, false); final int tileMode = a.getInt(R.styleable.RoundedImageView_riv_tile_mode, TILE_MODE_UNDEFINED); if (tileMode != TILE_MODE_UNDEFINED) { setTileModeX(parseTileMode(tileMode)); setTileModeY(parseTileMode(tileMode)); } final int tileModeX = a.getInt(R.styleable.RoundedImageView_riv_tile_mode_x, TILE_MODE_UNDEFINED); if (tileModeX != TILE_MODE_UNDEFINED) { setTileModeX(parseTileMode(tileModeX)); } final int tileModeY = a.getInt(R.styleable.RoundedImageView_riv_tile_mode_y, TILE_MODE_UNDEFINED); if (tileModeY != TILE_MODE_UNDEFINED) { setTileModeY(parseTileMode(tileModeY)); } updateDrawableAttrs(); updateBackgroundDrawableAttrs(true); if (mMutateBackground) { //noinspection deprecation super.setBackgroundDrawable(mBackgroundDrawable); } a.recycle(); } private static Shader.TileMode parseTileMode(int tileMode) { switch (tileMode) { case TILE_MODE_CLAMP: return Shader.TileMode.CLAMP; case TILE_MODE_REPEAT: return Shader.TileMode.REPEAT; case TILE_MODE_MIRROR: return Shader.TileMode.MIRROR; default: return null; } } @Override protected void drawableStateChanged() { super.drawableStateChanged(); invalidate(); } @Override public ScaleType getScaleType() { return mScaleType; } @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(@DrawableRes 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); } @Override public void setBackgroundResource(@DrawableRes int resId) { if (mBackgroundResource != resId) { mBackgroundResource = resId; mBackgroundDrawable = resolveBackgroundResource(); setBackgroundDrawable(mBackgroundDrawable); } } @Override public void setBackgroundColor(int color) { mBackgroundDrawable = new ColorDrawable(color); setBackgroundDrawable(mBackgroundDrawable); } private Drawable resolveBackgroundResource() { Resources rsrc = getResources(); if (rsrc == null) { return null; } Drawable d = null; if (mBackgroundResource != 0) { try { d = rsrc.getDrawable(mBackgroundResource); } catch (Exception e) { Log.w(TAG, "Unable to find resource: " + mBackgroundResource, e); // Don't try again. mBackgroundResource = 0; } } return RoundedDrawable.fromDrawable(d); } private void updateDrawableAttrs() { updateAttrs(mDrawable, mScaleType); } private void updateBackgroundDrawableAttrs(boolean convert) { if (mMutateBackground) { if (convert) { mBackgroundDrawable = RoundedDrawable.fromDrawable(mBackgroundDrawable); } updateAttrs(mBackgroundDrawable, ScaleType.FIT_XY); } } @Override public void setColorFilter(ColorFilter cf) { if (mColorFilter != cf) { mColorFilter = cf; mHasColorFilter = true; mColorMod = true; applyColorMod(); invalidate(); } } private void applyColorMod() { // Only mutate and apply when modifications have occurred. This should // not reset the mColorMod flag, since these filters need to be // re-applied if the Drawable is changed. if (mDrawable != null && mColorMod) { mDrawable = mDrawable.mutate(); if (mHasColorFilter) { mDrawable.setColorFilter(mColorFilter); } // TODO: support, eventually... //mDrawable.setXfermode(mXfermode); //mDrawable.setAlpha(mAlpha * mViewAlphaScale >> 8); } } private void updateAttrs(Drawable drawable, ScaleType scaleType) { if (drawable == null) { return; } if (drawable instanceof RoundedDrawable) { ((RoundedDrawable) drawable) .setScaleType(scaleType) .setBorderWidth(mBorderWidth) .setBorderColor(mBorderColor) .setOval(mIsOval) .setTileModeX(mTileModeX) .setTileModeY(mTileModeY); if (mCornerRadii != null) { ((RoundedDrawable) drawable).setCornerRadius( mCornerRadii[Corner.TOP_LEFT], mCornerRadii[Corner.TOP_RIGHT], mCornerRadii[Corner.BOTTOM_RIGHT], mCornerRadii[Corner.BOTTOM_LEFT]); } applyColorMod(); } 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), scaleType); } } } @Override @Deprecated public void setBackgroundDrawable(Drawable background) { mBackgroundDrawable = background; updateBackgroundDrawableAttrs(true); //noinspection deprecation super.setBackgroundDrawable(mBackgroundDrawable); } /** * @return the largest corner radius. */ public float getCornerRadius() { return getMaxCornerRadius(); } /** * @return the largest corner radius. */ public float getMaxCornerRadius() { float maxRadius = 0; for (float r : mCornerRadii) { maxRadius = Math.max(r, maxRadius); } return maxRadius; } /** * Get the corner radius of a specified corner. * * @param corner the corner. * @return the radius. */ public float getCornerRadius(@Corner int corner) { return mCornerRadii[corner]; } /** * Set all the corner radii from a dimension resource id. * * @param resId dimension resource id of radii. */ public void setCornerRadiusDimen(@DimenRes int resId) { float radius = getResources().getDimension(resId); setCornerRadius(radius, radius, radius, radius); } /** * Set the corner radius of a specific corner from a dimension resource id. * * @param corner the corner to set. * @param resId the dimension resource id of the corner radius. */ public void setCornerRadiusDimen(@Corner int corner, @DimenRes int resId) { setCornerRadius(corner, getResources().getDimensionPixelSize(resId)); } /** * Set the corner radii of all corners in px. * * @param radius the radius to set. */ public void setCornerRadius(float radius) { setCornerRadius(radius, radius, radius, radius); } /** * Set the corner radius of a specific corner in px. * * @param corner the corner to set. * @param radius the corner radius to set in px. */ public void setCornerRadius(@Corner int corner, float radius) { if (mCornerRadii[corner] == radius) { return; } mCornerRadii[corner] = radius; updateDrawableAttrs(); updateBackgroundDrawableAttrs(false); invalidate(); } /** * Set the corner radii of each corner individually. Currently only one unique nonzero value is * supported. * * @param topLeft radius of the top left corner in px. * @param topRight radius of the top right corner in px. * @param bottomRight radius of the bottom right corner in px. * @param bottomLeft radius of the bottom left corner in px. */ public void setCornerRadius(float topLeft, float topRight, float bottomLeft, float bottomRight) { if (mCornerRadii[Corner.TOP_LEFT] == topLeft && mCornerRadii[Corner.TOP_RIGHT] == topRight && mCornerRadii[Corner.BOTTOM_RIGHT] == bottomRight && mCornerRadii[Corner.BOTTOM_LEFT] == bottomLeft) { return; } mCornerRadii[Corner.TOP_LEFT] = topLeft; mCornerRadii[Corner.TOP_RIGHT] = topRight; mCornerRadii[Corner.BOTTOM_LEFT] = bottomLeft; mCornerRadii[Corner.BOTTOM_RIGHT] = bottomRight; updateDrawableAttrs(); updateBackgroundDrawableAttrs(false); invalidate(); } public float getBorderWidth() { return mBorderWidth; } public void setBorderWidth(@DimenRes int resId) { setBorderWidth(getResources().getDimension(resId)); } public void setBorderWidth(float width) { if (mBorderWidth == width) { return; } mBorderWidth = width; updateDrawableAttrs(); updateBackgroundDrawableAttrs(false); invalidate(); } @ColorInt public int getBorderColor() { return mBorderColor.getDefaultColor(); } public void setBorderColor(@ColorInt int color) { setBorderColor(ColorStateList.valueOf(color)); } public ColorStateList getBorderColors() { return mBorderColor; } public void setBorderColor(ColorStateList colors) { if (mBorderColor.equals(colors)) { return; } mBorderColor = (colors != null) ? colors : ColorStateList.valueOf(RoundedDrawable.DEFAULT_BORDER_COLOR); updateDrawableAttrs(); updateBackgroundDrawableAttrs(false); if (mBorderWidth > 0) { invalidate(); } } /** * Return true if this view should be oval and always set corner radii to half the height or * width. * * @return if this {@link RoundedImageView} is set to oval. */ public boolean isOval() { return mIsOval; } /** * Set if the drawable should ignore the corner radii set and always round the source to * exactly half the height or width. * * @param oval if this {@link RoundedImageView} should be oval. */ public void setOval(boolean oval) { mIsOval = oval; updateDrawableAttrs(); updateBackgroundDrawableAttrs(false); invalidate(); } public Shader.TileMode getTileModeX() { return mTileModeX; } public void setTileModeX(Shader.TileMode tileModeX) { if (this.mTileModeX == tileModeX) { return; } this.mTileModeX = tileModeX; updateDrawableAttrs(); updateBackgroundDrawableAttrs(false); invalidate(); } public Shader.TileMode getTileModeY() { return mTileModeY; } public void setTileModeY(Shader.TileMode tileModeY) { if (this.mTileModeY == tileModeY) { return; } this.mTileModeY = tileModeY; updateDrawableAttrs(); updateBackgroundDrawableAttrs(false); invalidate(); } /** * If {@code true}, we will also round the background drawable according to the settings on this * ImageView. * * @return whether the background is mutated. */ public boolean mutatesBackground() { return mMutateBackground; } /** * Set whether the {@link RoundedImageView} should round the background drawable according to * the settings in addition to the source drawable. * * @param mutate true if this view should mutate the background drawable. */ public void mutateBackground(boolean mutate) { if (mMutateBackground == mutate) { return; } mMutateBackground = mutate; updateBackgroundDrawableAttrs(true); invalidate(); } }
四:自定义的属性
<declare-styleable name="RoundedImageView"> <attr name="riv_corner_radius" format="dimension" /> <attr name="riv_corner_radius_top_left" format="dimension" /> <attr name="riv_corner_radius_top_right" format="dimension" /> <attr name="riv_corner_radius_bottom_left" format="dimension" /> <attr name="riv_corner_radius_bottom_right" format="dimension" /> <attr name="riv_border_width" format="dimension" /> <attr name="riv_border_color" format="color" /> <attr name="riv_mutate_background" format="boolean" /> <attr name="riv_oval" format="boolean" /> <attr name="android:scaleType" format="integer" /> <attr name="riv_tile_mode" format="integer"> <enum name="clamp" value="0" /> <enum name="repeat" value="1" /> <enum name="mirror" value="2" /> </attr> <attr name="riv_tile_mode_x" format="integer"> <enum name="clamp" value="0" /> <enum name="repeat" value="1" /> <enum name="mirror" value="2" /> </attr> <attr name="riv_tile_mode_y" format="integer"> <enum name="clamp" value="0" /> <enum name="repeat" value="1" /> <enum name="mirror" value="2" /> </attr> </declare-styleable>
-
SwiftUI 中级之如何自定义圆角位置和度数 (2020年教程)
2020-01-22 22:18:59SwiftUI 中级之如何自定义圆角位置和度数 (2020年教程) 需求描述 我知道你可以使用 .cornerRadius() 来设置 UI 视图为圆角,但有没有办法只输入特定角,如顶部。 解决方案 自定义 struct ContentView : View { ...SwiftUI 中级之如何自定义圆角位置和度数 (2020年教程)
需求描述
我知道你可以使用 .cornerRadius() 来设置 UI 视图为圆角,但有没有办法只输入特定角,如顶部。
解决方案
自定义
struct ContentView : View { var body: some View { Text("Hello World!") .foregroundColor(.white) .font(.largeTitle) .padding(20) .background(RoundedCorners(color: .blue, tl: 0, tr: 30, bl: 30, br: 0)) } }
struct RoundedCorners: View { var color: Color = .blue var tl: CGFloat = 0.0 var tr: CGFloat = 0.0 var bl: CGFloat = 0.0 var br: CGFloat = 0.0 var body: some View { GeometryReader { geometry in Path { path in let w = geometry.size.width let h = geometry.size.height // Make sure we do not exceed the size of the rectangle let tr = min(min(self.tr, h/2), w/2) let tl = min(min(self.tl, h/2), w/2) let bl = min(min(self.bl, h/2), w/2) let br = min(min(self.br, h/2), w/2) path.move(to: CGPoint(x: w / 2.0, y: 0)) path.addLine(to: CGPoint(x: w - tr, y: 0)) path.addArc(center: CGPoint(x: w - tr, y: tr), radius: tr, startAngle: Angle(degrees: -90), endAngle: Angle(degrees: 0), clockwise: false) path.addLine(to: CGPoint(x: w, y: h - br)) path.addArc(center: CGPoint(x: w - br, y: h - br), radius: br, startAngle: Angle(degrees: 0), endAngle: Angle(degrees: 90), clockwise: false) path.addLine(to: CGPoint(x: bl, y: h)) path.addArc(center: CGPoint(x: bl, y: h - bl), radius: bl, startAngle: Angle(degrees: 90), endAngle: Angle(degrees: 180), clockwise: false) path.addLine(to: CGPoint(x: 0, y: tl)) path.addArc(center: CGPoint(x: tl, y: tl), radius: tl, startAngle: Angle(degrees: 180), endAngle: Angle(degrees: 270), clockwise: false) } .fill(self.color) } } }
效果
SwiftUI 中级之如何自定义圆角位置和度数参考资料
更多SwiftUI教程和代码关注专栏
- 请关注我的专栏 SwiftUI教程与源码
-
圆角图片生成器
2015-09-30 22:17:37本软件可以将图片处理成圆角图片,您可以自定义圆角度数,还可以保存生成的圆角图片。简单实用!生活中会经常用到,比如处理头像图片等。 -
Adnroid 安卓Glide加载圆角
2019-10-24 09:08:00写好了一个工具类 要在自己项目中build.gradle加入以下内容(导入Glide) implementation('...} 可以自己设置圆角度数,也可以自动设置,如第5行,我写了20 public class Glide...写好了一个工具类
要在自己项目中build.gradle加入以下内容(导入Glide)
implementation('com.github.bumptech.glide:glide:4.7.1') { exclude group: "com.android.support" }
可以自己设置圆角度数,也可以自动设置,如第5行,我写了20
public class GlideTool { @SuppressLint("CheckResult") public static void GlideRadius(Context context, String url, ImageView imageView) { //设置图片圆角角度 RoundedCorners roundedCorners = new RoundedCorners(20); RequestOptions options = RequestOptions.bitmapTransform(roundedCorners).override(0, 0); Glide.with(context).load(url).apply(options).into(imageView); } @SuppressLint("CheckResult") public static void GlideRadius(Context context, String url, ImageView imageView, int radius) { //设置图片圆角角度 RoundedCorners roundedCorners = new RoundedCorners(radius); RequestOptions options = RequestOptions.bitmapTransform(roundedCorners).override(0, 0); Glide.with(context).load(url).apply(options).into(imageView); } }
其运行效果如下:
-
Android中用GradientDrawable动态设置不同度数的圆角
2019-01-23 13:43:39最近在做毕业设计时有个自定义的PopWindow,布局用代码构建,其中就有用到GradientDrawable设置不同圆角的背景,在这里做下笔记。 效果 角度大小只是为了举例 实现 这里用的是Kotlin val gd = ...问题
最近在做毕业设计时有个自定义的PopWindow,布局用代码构建,其中就有用到GradientDrawable设置不同圆角的背景,在这里做下笔记。
效果
角度大小只是为了举例
实现
这里用的是Kotlin
val gd = GradientDrawable() gd.cornerRadii = getCornerRadii(30F,8F,20F,5F) private fun getCornerRadii(leftTop:Float,rightTop:Float, leftBottom:Float,rightBottom:Float):FloatArray{ //这里返回的一个浮点型的数组,一定要有8个元素,不然会报错 return floatArrayOf(dp2px(leftTop), dp2px(leftTop), dp2px(rightTop),dp2px(rightTop),dp2px(rightBottom), dp2px(rightBottom),dp2px(leftBottom),dp2px(leftBottom)) } private fun dp2px(dpVal: Float): Float { return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpVal, mContext.resources.displayMetrics) }
-
Glide加载圆角图片
2018-12-06 18:06:34RoundedCorners roundedCorners = new RoundedCorners(6);//数字为圆角度数 RequestOptions coverRequestOptions = new RequestOptions() .transforms(new CenterCrop(mContext),roundedCorners) .diskCac... -
Glide加载圆角矩形图片
2020-07-28 11:21:54Glide加载圆角矩形图片 注意:xml里面不能设置CenterCrop RoundedCorners rounded...//数字为圆角度数 RequestOptions coverRequestOptions = new RequestOptions() .transforms(new CenterCrop(),roundedCorners) -
Fresco(各种特效)——圆角
2015-04-14 17:42:49//设置圆角度数 fresco:roundedCornerRadius="50dp" //设置左下角不进行圆角化 fresco:roundBottomLeft="false" //设置右下角不进行圆角化 fresco:roundBottomRight="false" //填充颜色(黄色**看下图) fresco:... -
Flutter中的圆角和圆形效果
2019-06-18 13:45:58Flutter中的圆角和圆形效果第一种:以图片为容器背景,设置容器四角的圆角角度第二种 ClipRRect 裁剪矩形四角 可自定义圆角度数第三种 ClipOval 直接就是圆形第四种 CircleAvatar拓展 自定义裁剪样式 ClipPath 路径... -
android背景图的圆角样式_Android 动态实现圆角背景和图标换色小技巧
2020-12-29 08:34:59前言不知道你们有没有遇到这样一种场景:设计师:“首页这个按钮圆角度数为5个像素”你:“OK”,言语间你已经在drawable目录下创建了一个xml文件,定义了圆角的shape,然后给Imageview设置上:过了5分钟……设计师... -
Android获取网络图片之后改圆形或者圆角
2019-01-05 11:45:12这里是圆角度数自拟。 这里是圆形。 要记得在写图片控件的时候一定要设置属性值,加上宽高给固定的值,如果设置包裹内容就会发生图片消失或者图片没有变化。 ... -
CSS3 border-radius(边框圆角)
2020-11-24 11:26:39属性 描述 border-radius 所有四个边角 border---radius 属性的缩写 border-top-left-radius 定义了左上角的弧度 border-top-right-radius ...边框四个角圆角度数5px border-radius:5px... -
自定义View—使用clipPath或者BitmapShader实现圆角图片
2015-08-16 22:30:57实现圆角图片的方式有三种,上篇文章中是使用了Xfermode,这篇文章则将总结剩下的两种clipPath、...自定义属性我们这个圆角图片可以定义图片的圆角度数,因此需要自定义这个属性如些:<?xml version="1.0" encod -
自定义ImageView,实现圆角矩形、原型、固定宽高比样式
2019-07-22 21:11:59最近,项目中动态展示图片的样式做了调整,宽高比从...在此基础上,我又加了圆角度数和原型的适配。算是一个小综合吧。 展示图片,我用的Glide。相关Glide,我这里就不做过多解释了。有兴趣的请看 http://blog.c... -
vml(iE支持的第三方插件)圆角表格相关语法
2008-11-17 21:15:00例子:////v/:*{behavior:url(#default#vml)}//////其中:strokecolor=green边框颜色strokeweight=1px边框粗细arcsize=0.1圆角度数,范围为0-0.5,默认为0.05fillcolor=yellow填充颜色filled=ture或false,可以简写为t或... -
关于圆角按钮
2019-09-28 01:25:58其中: layer.cornerRadius = 10 //圆角的度数 clipsToBounds = ture //除去边界,让其生效 转载于:https://www.cnblogs.com/jerry-q1/articles/5790014.html -
android 实现图片圆角
2014-10-08 14:44:03实现圆角的方法,此方法可以将一个位图改变为一个圆角位图: [java] view ... * @param pixels 圆角的度数,数值越大,圆角越大 * @return 处理后的圆角位图 */ public stati -
Android 实现图片的圆角
2016-11-22 13:40:57实现圆角的方法,此方法可以将一个位图改变为一个圆角位图: ... * @param pixels 圆角的度数,数值越大,圆角越大 * @return 处理后的圆角位图 */ public static Bitmap toRoundCorner(Bitmap bitmap, int pixels) -
Android 实现圆角ImageView
2018-05-31 17:53:08先上效果图:实现方法一:[java] view plain copy/** * 获取圆角位图的方法 * * @param bitmap * 需要转化成圆角的位图 * @param pixels * 圆角的度数,数值越大,圆角越大 * @return 处理后的... -
android 将图片变为圆角
2017-05-04 14:49:09/** * 图片变为圆角 ...* @param pixels:圆角的度数,值越大,圆角越大 * @return bitmap:加入圆角的bitmap */ public static Bitmap toRoundCorner(Bitmap bitmap, int pixels) { if(bitmap == null) { -
Android 实现图片圆角
2013-04-24 21:20:58调用这个方法,第一个参数是传入需要转化成圆角的图片,第二个参数是圆角的度数,数值越大,圆角越大 package com.example.android_test3_1_2; import android.app.Activity; import android.graphics.... -
获取圆角位图的方法
2016-03-12 16:47:27/** * 获取圆角位图的方法 * * @param bitmap * 需要转化成圆角的... * 圆角的度数,数值越大,圆角越大 * @return 处理后的圆角位图 */ public static Bitmap toRoundCorner(Bitmap bitmap -
android中3中方法实现圆角,请借鉴!
2015-01-03 16:03:16这是第一种方法:pixels圆角的度数,数值越大,圆角越大.90度为圆角 /** * 获取圆角位图的方法 * * @param bitmap * 需要转化成圆角的位图 * @param pixels * 圆角的度数,数值越大,圆角越大 * @return ... -
Android自定义ImageView实现圆角图片
2020-07-29 21:23:02大概就是这个样子的 四个直角改成圆角的 度数可以自定义 自定义ImageView 方法一:BitmapShader方式 首先简单了解下BitmapShader BitmapShader是Shader的子类 Shader在三维软件中我们称之为着色器 通俗的理解,... -
Android bitmap图片圆角工具方法
2016-04-13 11:52:08实现圆角的方法,此方法可以将一个位图改变为一个圆角位图: [java] view ... * @param pixels 圆角的度数,数值越大,圆角越大 * @return 处理后的圆角位图 */ public stat -
Android 自定义圆角的实现
2017-11-23 17:18:091.概述: 本文所说的方法是继承ImageView,使用Xfermode实现。 步骤: 在values目录下新建attrs文件, ...1.自定义属性,可以定制颜色,...下面定义圆角的度数: 2.然后在构造方法获得属性对应的值 -
Android 实现图片的圆角 (转)
2016-04-13 09:41:38/** * 获取圆角位图的方法 * @param bitmap 需要转化成圆角的位图 * @param pixels 圆角的度数,数值越大,圆角越大 * @return 处理后的圆角位图 */ public static Bitmap toRoundCorner(Bitmap ...
收藏数
57
精华内容
22