Android ImageView支持每个角的不同半径

news/2024/9/23 13:08:31/

Android ImageView支持每个角的不同半径


import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.Resources.NotFoundException;
import android.content.res.TypedArray;
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.Path;
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.net.Uri;
import android.util.AttributeSet;
import android.widget.ImageView;/*** <code>*     Usage* Define in xml:** <com.xxx.widget.SelectableRoundedImageView*         xmlns:app="http://schemas.android.com/apk/res-auto"*         android:id="@+id/image"*         android:src="@drawable/photo1"*         android:scaleType="centerCrop"*         app:left_top_corner_radius="16dip"*         app:right_top_corner_radius="0dip"*         app:left_bottom_corner_radius="48dip"*         app:right_bottom_corner_radius="16dip"*         app:border_width="2dip"*         app:border_color="#008fea"*         app:oval="true" />* Or in code:** SelectableRoundedImageView sriv = new SelectableRoundedImageView(context);* sriv.setScaleType(ScaleType.CENTER_CROP);* sriv.setCornerRadiiDP(4, 4, 0, 0);* sriv.setBorderWidthDP(4);* sriv.setBorderColor(Color.BLUE);* sriv.setImageDrawable(drawable);* sriv.setOval(true);* </code>* ${tags}*/
@SuppressLint("AppCompatCustomView")
public class SelectableRoundedImageView extends ImageView {public static final String TAG = "SelectableRoundedImageView";private int mResource = 0;private static final ScaleType[] sScaleTypeArray = {ScaleType.MATRIX,ScaleType.FIT_XY,ScaleType.FIT_START,ScaleType.FIT_CENTER,ScaleType.FIT_END,ScaleType.CENTER,ScaleType.CENTER_CROP,ScaleType.CENTER_INSIDE};// Set default scale type to FIT_CENTER, which is default scale type of// original ImageView.private ScaleType mScaleType = ScaleType.FIT_CENTER;private float mLeftTopCornerRadius = 0.0f;private float mRightTopCornerRadius = 0.0f;private float mLeftBottomCornerRadius = 0.0f;private float mRightBottomCornerRadius = 0.0f;private float mBorderWidth = 0.0f;private static final int DEFAULT_BORDER_COLOR = Color.BLACK;private ColorStateList mBorderColor = ColorStateList.valueOf(DEFAULT_BORDER_COLOR);private boolean isOval = false;private Drawable mDrawable;private float[] mRadii = new float[] { 0, 0, 0, 0, 0, 0, 0, 0 };public SelectableRoundedImageView(Context context) {super(context);}public SelectableRoundedImageView(Context context, AttributeSet attrs) {this(context, attrs, 0);}public SelectableRoundedImageView(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);TypedArray a = context.obtainStyledAttributes(attrs,R.styleable.SelectableRoundedImageView, defStyle, 0);final int index = a.getInt(R.styleable.SelectableRoundedImageView_android_scaleType, -1);if (index >= 0) {setScaleType(sScaleTypeArray[index]);}mLeftTopCornerRadius = a.getDimensionPixelSize(R.styleable.SelectableRoundedImageView_left_top_corner_radius, 0);mRightTopCornerRadius = a.getDimensionPixelSize(R.styleable.SelectableRoundedImageView_right_top_corner_radius, 0);mLeftBottomCornerRadius = a.getDimensionPixelSize(R.styleable.SelectableRoundedImageView_left_bottom_corner_radius, 0);mRightBottomCornerRadius = a.getDimensionPixelSize(R.styleable.SelectableRoundedImageView_right_bottom_corner_radius, 0);if (mLeftTopCornerRadius < 0.0f || mRightTopCornerRadius < 0.0f|| mLeftBottomCornerRadius < 0.0f || mRightBottomCornerRadius < 0.0f) {throw new IllegalArgumentException("radius values cannot be negative.");}mRadii = new float[] {mLeftTopCornerRadius, mLeftTopCornerRadius,mRightTopCornerRadius, mRightTopCornerRadius,mRightBottomCornerRadius, mRightBottomCornerRadius,mLeftBottomCornerRadius, mLeftBottomCornerRadius };mBorderWidth = a.getDimensionPixelSize(R.styleable.SelectableRoundedImageView_border_width, 0);if (mBorderWidth < 0) {throw new IllegalArgumentException("border width cannot be negative.");}mBorderColor = a.getColorStateList(R.styleable.SelectableRoundedImageView_border_color);if (mBorderColor == null) {mBorderColor = ColorStateList.valueOf(DEFAULT_BORDER_COLOR);}isOval = a.getBoolean(R.styleable.SelectableRoundedImageView_oval, false);a.recycle();updateDrawable();}@Overrideprotected void drawableStateChanged() {super.drawableStateChanged();invalidate();}@Overridepublic ScaleType getScaleType() {return mScaleType;}@Overridepublic void setScaleType(ScaleType scaleType) {super.setScaleType(scaleType);mScaleType = scaleType;updateDrawable();}@Overridepublic void setImageDrawable(Drawable drawable) {mResource = 0;mDrawable = SelectableRoundedCornerDrawable.fromDrawable(drawable, getResources());super.setImageDrawable(mDrawable);updateDrawable();}@Overridepublic void setImageBitmap(Bitmap bm) {mResource = 0;mDrawable = SelectableRoundedCornerDrawable.fromBitmap(bm, getResources());super.setImageDrawable(mDrawable);updateDrawable();}@Overridepublic void setImageResource(int resId) {if (mResource != resId) {mResource = resId;mDrawable = resolveResource();super.setImageDrawable(mDrawable);updateDrawable();}}@Overridepublic 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 (NotFoundException e) {LogUtils.logw(TAG+" Unable to find resource: " + mResource, e);// Don't try again.mResource = 0;}}return SelectableRoundedCornerDrawable.fromDrawable(d, getResources());}private void updateDrawable() {if (mDrawable == null) {return;}((SelectableRoundedCornerDrawable) mDrawable).setScaleType(mScaleType);((SelectableRoundedCornerDrawable) mDrawable).setCornerRadii(mRadii);((SelectableRoundedCornerDrawable) mDrawable).setBorderWidth(mBorderWidth);((SelectableRoundedCornerDrawable) mDrawable).setBorderColor(mBorderColor);((SelectableRoundedCornerDrawable) mDrawable).setOval(isOval);}public float getCornerRadius() {return mLeftTopCornerRadius;}/*** Set radii for each corner.** @param leftTop The desired radius for left-top corner in dip.* @param rightTop The desired desired radius for right-top corner in dip.* @param leftBottom The desired radius for left-bottom corner in dip.* @param rightBottom The desired radius for right-bottom corner in dip.**/public void setCornerRadiiDP(float leftTop, float rightTop, float leftBottom, float rightBottom) {final float density = getResources().getDisplayMetrics().density;final float lt = leftTop * density;final float rt = rightTop * density;final float lb = leftBottom * density;final float rb = rightBottom * density;mRadii = new float[] { lt, lt, rt, rt, rb, rb, lb, lb };updateDrawable();}public float getBorderWidth() {return mBorderWidth;}/*** Set border width.** @param width*            The desired width in dip.*/public void setBorderWidthDP(float width) {float scaledWidth = getResources().getDisplayMetrics().density * width;if (mBorderWidth == scaledWidth) {return;}mBorderWidth = scaledWidth;updateDrawable();invalidate();}public int getBorderColor() {return mBorderColor.getDefaultColor();}public void setBorderColor(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(DEFAULT_BORDER_COLOR);updateDrawable();if (mBorderWidth > 0) {invalidate();}}public boolean isOval() {return isOval;}public void setOval(boolean oval) {isOval = oval;updateDrawable();invalidate();}static class SelectableRoundedCornerDrawable extends Drawable {private static final String TAG = "SelectableRoundedCornerDrawable";private static final int DEFAULT_BORDER_COLOR = Color.BLACK;private RectF mBounds = new RectF();private RectF mBorderBounds = new RectF();private final RectF mBitmapRect = new RectF();private final int mBitmapWidth;private final int mBitmapHeight;private final Paint mBitmapPaint;private final Paint mBorderPaint;private BitmapShader mBitmapShader;private float[] mRadii = new float[] { 0, 0, 0, 0, 0, 0, 0, 0 };private float[] mBorderRadii = new float[] { 0, 0, 0, 0, 0, 0, 0, 0 };private boolean mOval = false;private float mBorderWidth = 0;private ColorStateList mBorderColor = ColorStateList.valueOf(DEFAULT_BORDER_COLOR);// Set default scale type to FIT_CENTER, which is default scale type of// original ImageView.private ScaleType mScaleType = ScaleType.FIT_CENTER;private Path mPath = new Path();private Bitmap mBitmap;private boolean mBoundsConfigured = false;public SelectableRoundedCornerDrawable(Bitmap bitmap, Resources r) {mBitmap = bitmap;mBitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);if (bitmap != null) {mBitmapWidth = bitmap.getScaledWidth(r.getDisplayMetrics());mBitmapHeight = bitmap.getScaledHeight(r.getDisplayMetrics());} else {mBitmapWidth = mBitmapHeight = -1;}mBitmapRect.set(0, 0, mBitmapWidth, mBitmapHeight);mBitmapPaint = new Paint(Paint.ANTI_ALIAS_FLAG);mBitmapPaint.setStyle(Paint.Style.FILL);mBitmapPaint.setShader(mBitmapShader);mBorderPaint = new Paint(Paint.ANTI_ALIAS_FLAG);mBorderPaint.setStyle(Paint.Style.STROKE);mBorderPaint.setColor(mBorderColor.getColorForState(getState(), DEFAULT_BORDER_COLOR));mBorderPaint.setStrokeWidth(mBorderWidth);}public static SelectableRoundedCornerDrawable fromBitmap(Bitmap bitmap, Resources r) {if (bitmap != null) {return new SelectableRoundedCornerDrawable(bitmap, r);} else {return null;}}public static Drawable fromDrawable(Drawable drawable, Resources r) {if (drawable != null) {if (drawable instanceof SelectableRoundedCornerDrawable) {return drawable;} else if (drawable instanceof LayerDrawable) {LayerDrawable ld = (LayerDrawable) drawable;final int num = ld.getNumberOfLayers();for (int i = 0; i < num; i++) {Drawable d = ld.getDrawable(i);ld.setDrawableByLayerId(ld.getId(i), fromDrawable(d, r));}return ld;}Bitmap bm = drawableToBitmap(drawable);if (bm != null) {return new SelectableRoundedCornerDrawable(bm, r);} else {LogUtils.logw(TAG+" Failed to create bitmap from drawable!");}}return drawable;}public static Bitmap drawableToBitmap(Drawable drawable) {if (drawable == null) {return null;}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 (IllegalArgumentException e) {e.printStackTrace();bitmap = null;}return bitmap;}@Overridepublic boolean isStateful() {return mBorderColor.isStateful();}@Overrideprotected 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 configureBounds(Canvas canvas) {// I have discovered a truly marvelous explanation of this,// which this comment space is too narrow to contain. :)// If you want to understand what's going on here,// See http://www.joooooooooonhokim.com/?p=289Rect clipBounds = canvas.getClipBounds();Matrix canvasMatrix = canvas.getMatrix();if (ScaleType.CENTER == mScaleType) {mBounds.set(clipBounds);} else if (ScaleType.CENTER_CROP == mScaleType) {applyScaleToRadii(canvasMatrix);mBounds.set(clipBounds);} else if (ScaleType.FIT_XY == mScaleType) {Matrix m = new Matrix();m.setRectToRect(mBitmapRect, new RectF(clipBounds), Matrix.ScaleToFit.FILL);mBitmapShader.setLocalMatrix(m);mBounds.set(clipBounds);} else if (ScaleType.FIT_START == mScaleType || ScaleType.FIT_END == mScaleType|| ScaleType.FIT_CENTER == mScaleType || ScaleType.CENTER_INSIDE == mScaleType) {applyScaleToRadii(canvasMatrix);mBounds.set(mBitmapRect);} else if (ScaleType.MATRIX == mScaleType) {applyScaleToRadii(canvasMatrix);mBounds.set(mBitmapRect);}}private void applyScaleToRadii(Matrix m) {float[] values = new float[9];m.getValues(values);for (int i = 0; i < mRadii.length; i++) {mRadii[i] = mRadii[i] / values[0];}}private void adjustCanvasForBorder(Canvas canvas) {Matrix canvasMatrix = canvas.getMatrix();final float[] values = new float[9];canvasMatrix.getValues(values);final float scaleFactorX = values[0];final float scaleFactorY = values[4];final float translateX = values[2];final float translateY = values[5];final float newScaleX = mBounds.width()/ (mBounds.width() + mBorderWidth + mBorderWidth);final float newScaleY = mBounds.height()/ (mBounds.height() + mBorderWidth + mBorderWidth);canvas.scale(newScaleX, newScaleY);if (ScaleType.FIT_START == mScaleType || ScaleType.FIT_END == mScaleType|| ScaleType.FIT_XY == mScaleType || ScaleType.FIT_CENTER == mScaleType|| ScaleType.CENTER_INSIDE == mScaleType || ScaleType.MATRIX == mScaleType) {canvas.translate(mBorderWidth, mBorderWidth);} else if (ScaleType.CENTER == mScaleType || ScaleType.CENTER_CROP == mScaleType) {// First, make translate values to 0canvas.translate(-translateX / (newScaleX * scaleFactorX),-translateY / (newScaleY * scaleFactorY));// Then, set the final translate values.canvas.translate(-(mBounds.left - mBorderWidth), -(mBounds.top - mBorderWidth));}}private void adjustBorderWidthAndBorderBounds(Canvas canvas) {Matrix canvasMatrix = canvas.getMatrix();final float[] values = new float[9];canvasMatrix.getValues(values);final float scaleFactor = values[0];float viewWidth = mBounds.width() * scaleFactor;mBorderWidth = (mBorderWidth * mBounds.width()) / (viewWidth - (2 * mBorderWidth));mBorderPaint.setStrokeWidth(mBorderWidth);mBorderBounds.set(mBounds);mBorderBounds.inset(- mBorderWidth / 2, - mBorderWidth / 2);}private void setBorderRadii() {for (int i = 0; i < mRadii.length; i++) {if (mRadii[i] > 0) {mBorderRadii[i] = mRadii[i];mRadii[i] = mRadii[i] - mBorderWidth;}}}@Overridepublic void draw(Canvas canvas) {canvas.save();if (!mBoundsConfigured) {configureBounds(canvas);if (mBorderWidth > 0) {adjustBorderWidthAndBorderBounds(canvas);setBorderRadii();}mBoundsConfigured = true;}if (mOval) {if (mBorderWidth > 0) {adjustCanvasForBorder(canvas);mPath.addOval(mBounds, Path.Direction.CW);canvas.drawPath(mPath, mBitmapPaint);mPath.reset();mPath.addOval(mBorderBounds, Path.Direction.CW);canvas.drawPath(mPath, mBorderPaint);} else {mPath.addOval(mBounds, Path.Direction.CW);canvas.drawPath(mPath, mBitmapPaint);}} else {if (mBorderWidth > 0) {adjustCanvasForBorder(canvas);mPath.addRoundRect(mBounds, mRadii, Path.Direction.CW);canvas.drawPath(mPath, mBitmapPaint);mPath.reset();mPath.addRoundRect(mBorderBounds, mBorderRadii, Path.Direction.CW);canvas.drawPath(mPath, mBorderPaint);} else {mPath.addRoundRect(mBounds, mRadii, Path.Direction.CW);canvas.drawPath(mPath, mBitmapPaint);}}canvas.restore();}public void setCornerRadii(float[] radii) {if (radii == null)return;if (radii.length != 8) {throw new ArrayIndexOutOfBoundsException("radii[] needs 8 values");}for (int i = 0; i < radii.length; i++) {mRadii[i] = radii[i];}}@Overridepublic int getOpacity() {return (mBitmap == null || mBitmap.hasAlpha() || mBitmapPaint.getAlpha() < 255) ? PixelFormat.TRANSLUCENT: PixelFormat.OPAQUE;}@Overridepublic void setAlpha(int alpha) {mBitmapPaint.setAlpha(alpha);invalidateSelf();}@Overridepublic void setColorFilter(ColorFilter cf) {mBitmapPaint.setColorFilter(cf);invalidateSelf();}@Overridepublic void setDither(boolean dither) {mBitmapPaint.setDither(dither);invalidateSelf();}@Overridepublic void setFilterBitmap(boolean filter) {mBitmapPaint.setFilterBitmap(filter);invalidateSelf();}@Overridepublic int getIntrinsicWidth() {return mBitmapWidth;}@Overridepublic int getIntrinsicHeight() {return mBitmapHeight;}public float getBorderWidth() {return mBorderWidth;}public void setBorderWidth(float width) {mBorderWidth = width;mBorderPaint.setStrokeWidth(width);}public int getBorderColor() {return mBorderColor.getDefaultColor();}public void setBorderColor(int color) {setBorderColor(ColorStateList.valueOf(color));}public ColorStateList getBorderColors() {return mBorderColor;}/*** Controls border color of this ImageView.** @param colors*            The desired border color. If it's null, no border will be*            drawn.**/public void setBorderColor(ColorStateList colors) {if (colors == null) {mBorderWidth = 0;mBorderColor = ColorStateList.valueOf(Color.TRANSPARENT);mBorderPaint.setColor(Color.TRANSPARENT);} else {mBorderColor = colors;mBorderPaint.setColor(mBorderColor.getColorForState(getState(),DEFAULT_BORDER_COLOR));}}public boolean isOval() {return mOval;}public void setOval(boolean oval) {mOval = oval;}public ScaleType getScaleType() {return mScaleType;}public void setScaleType(ScaleType scaleType) {if (scaleType == null) {return;}mScaleType = scaleType;}}}

attrs.xml

  <declare-styleable name="SelectableRoundedImageView"><!--左上角圆角半径,例如:5dp、10px 等。--><attr name="left_top_corner_radius" format="dimension" /><!--右上角圆角半径,例如:5dp、10px 等。--><attr name="right_top_corner_radius" format="dimension" /><!--左下角圆角半径,例如:5dp、10px 等。--><attr name="left_bottom_corner_radius" format="dimension" /><!--右下角圆角半径,例如:5dp、10px 等。--><attr name="right_bottom_corner_radius" format="dimension" /><!--边框宽度,例如:1dp、2px 等。--><attr name="border_width" format="dimension" /><!--边框颜色,接受一个颜色值,例如:#FF0000(红色)等。--><attr name="border_color" format="color" /><!--是否为椭圆形,布尔值。如果设置为 true,则图像将显示为椭圆形;如果为 false,则根据圆角半径显示为圆角矩形或普通矩形。--><attr name="oval" format="boolean" /><!--继承自 Android 的 scaleType 属性,用于指定图像的缩放类型,如 fitXY、centerCrop 等。--><attr name="android:scaleType" /></declare-styleable>


http://www.ppmy.cn/news/1529325.html

相关文章

Flyway-SQL 脚本与 Java 迁移

Flyway SQL 脚本与 Java 迁移详解 Flyway 是一种数据库迁移工具&#xff0c;提供了 SQL 脚本和 Java 迁移两种方式来管理数据库变更。在 Flyway 中&#xff0c;数据库迁移是通过逐步执行迁移脚本或代码来完成的。Flyway 既可以通过 SQL 文件直接执行数据库操作&#xff0c;也可…

spark读取数据性能提升

1. 背景 spark默认的jdbc只会用单task读取数据&#xff0c;读取大数据量时&#xff0c;效率低。 2. 解决方案 根据分区字段&#xff0c;如日期进行划分&#xff0c;增加task数量提升效率。 /*** 返回每个task按时间段划分的过滤语句* param startDate* param endDate* param …

在项目管理中,项目进度由哪些要素决定?

在项目管理领域&#xff0c;项目进度受到多种要素的综合影响。以下是一些关键的决定要素&#xff1a; 一、项目范围 1、任务清单 明确的任务清单是项目进度的基础。详细列出项目中需要完成的各项任务&#xff0c;包括任务的先后顺序、并行任务等&#xff0c;直接关系到进度规划…

dedecms——四种webshell姿势

姿势一&#xff1a;通过文件管理器上传WebShell 步骤一&#xff1a;访问目标靶场其思路为 dedecms 后台可以直接上传任意文件&#xff0c;可以通过文件管理器上传php文件获取webshell 步骤二&#xff1a;登陆到后台点击【核心】--》 【文件式管理器】--》 【文件上传】将准备好…

前端开发者有福啦,循序渐进Vue.js 3.x前端开发实践已上线

目录 写在前面 推荐图书 推荐理由 写在最后 写在前面 好书推荐&#xff01;前端开发者的福利来喽&#xff0c;《循序渐进Vue.js 3.x前端开发实践》&#xff0c;你值得拥有。 推荐图书 《循序渐进Vue.js 3.x前端开发实践》 推荐理由 《循序渐进Vue.js 3.x前端开发实践》…

使用build_chain.sh离线搭建匹配的区块链,并通过命令配置各群组节点的MySQL数据库

【任务】 登陆Linux服务器&#xff0c;以MySQL分布式存储方式安装并部署如图所示的三群组、四机构、 七节点的星形组网拓扑区块链系统。其中&#xff0c;三群组名称分别为group1、group2和group3&#xff0c; 四个机构名称为agencyA、agencyB、agencyC、agencyD。p2p_port、cha…

SSC377/D, 5M30 64/128MB, 1Tops1. 支持双摄,甚至三摄;2. 夜视全彩;3. 省内存、省带宽;4. 算力较大,适合新的算法模型;

 High Performance Processor Core  ARM Cortex-A35  Clock rate up to 1.0 GHz  Neon and FPU  Memory Management Unit for Linux support  DMA Engine  Image/Video Processor  Supports 8/10/12-bit parallel interface for raw data inpu…

Docker部署Joplin Server教程

Joplin Server 是 Joplin 应用的后端服务,提供笔记和待办事项的同步功能。它允许用户在不同设备之间同步笔记,同时支持多用户和协作功能。Joplin Server使用现代技术栈,数据库使用的是 PostgreSQL 。 主要功能 同步:在桌面、移动设备和网页应用之间同步笔记。多用户支持:允…