日常开发中,常要用圆角矩形或者圆形来显示头像,今天我们一个类搞定圆角矩形和圆形View的展示,同时支持自定义边框大小、边框颜色、按下态蒙层颜色等,当然你也可以根据你的需求定义成支持各种形状(椭圆、Path等)及其他属性;
实现效果如图:
- 支持:圆形,圆角矩形显示图片
- 支持显示边框
- 支持按下态蒙层
一,自定义属性:
自定义View的第一步,首先分析清楚它需要哪些自定义属性,基本包括形状,边框大小、边框颜色、按下态蒙层颜色、圆角半径等;在values/attrs.xml中定义如下属性
<declare-styleable name="MultiShapeView">
<attr name="shape" format="enum">
<enum name="rect" value="1"/>
<enum name="circle" value="2"/>
</attr>
<attr name="border_width" format="dimension" />
<attr name="border_color" format="color" />
<attr name="cover_color" format="color" />
<attr name="round_radius" format="dimension"/>
</declare-styleable>
二,类的实现:
我们需要解决这样几个问题
- 如何获取图片:参考ImageView,提供setImageBitmap(), setImageDrawable(), setImageResoure()等方法,传入bitmap;
- 如何实现图片的放缩:因为原始的图片大小不可能正好符合我们的显示要求,因此通过根据图片的原始大小和要显示的大小的比例,借助矩阵对图片进行伸缩;
- 如何实现圆形、圆角矩形显示:借助BitmapShader,前面的matrix设给bitmapShader对象,通过paint.setShader(BitmapShader),即可借助paint绘制各种形状;shader的简单意思就是着色器,因此这个方法你可以理解为,BitmapShader将Bitmap每个像素点的色值赋给Paint,因此Paint.draw各种形状时,draw出来的就是带有Bitmap各像素点色值的形状;即各种形状的图片
- 如何处理按下态:参考系统按下态的实现,覆写onTouchEvent(),分别在Down和Up事件调用setPressed(boolean isPressed); 通过给Paint设置colorFilter达到蒙层效果;
最后源码如下:
public class MultiShapeView extends android.support.v7.widget.AppCompatImageView { private Context mContext; /** * 显示图片 */ private Bitmap mBitmap; /** * Bitmap Paint */ private Paint mBitmapPaint; /** * 边框 Paint */ private Paint mBorderPaint; /** * 当前是否被按下 */ private boolean mIsPressed; private Shader mBitmapShader; /** * 变换矩阵 */ private Matrix mShaderMatrix; /** * 形状 */ private int mShape; /** * Rect Bitmap */ private RectF mRcBitmap; /** * Rect Border */ private RectF mRcBorder; /** * 圆角半径 */ private float mRoundRadius; /** * 边框半径 */ private float mBorderRadius; /** * 图片半径 */ private float mCircleRadius; /** * 边框color */ private int mBorderColor; /** * 边框大小 */ private int mBorderWidth; /** * 按下态蒙层color */ private int mCoverColor; /** * 默认BorderWidth */ private static final int DEFAULT_BORDER_WIDTH = 0; /** * 默认BorderColor */ private static final int DEFAULT_BORDER_COLOR = Color.TRANSPARENT; /** * 默认按下态蒙层Color */ private static final int DEFAULT_COVER_COLOR = Color.parseColor("#40333333"); /** * 默认圆角半径 */ private static final int DEFAULT_ROUND_RADIUS = 0; /** * 默认形状 */ public static final int SHAPE_REC = 1; // 矩形 public static final int SHAPE_CIRCLE = 2; // 圆形 public MultiShapeView(Context context) { this(context, null); } public MultiShapeView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public MultiShapeView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.MultiShapeView, defStyleAttr, 0); mContext = context; mBorderColor = typedArray.getColor(R.styleable.MultiShapeView_border_color, DEFAULT_BORDER_COLOR); mCoverColor = typedArray.getColor(R.styleable.MultiShapeView_cover_color, DEFAULT_COVER_COLOR); mBorderWidth = typedArray.getDimensionPixelSize(R.styleable.MultiShapeView_border_width, DEFAULT_BORDER_WIDTH); mShape = typedArray.getInteger(R.styleable.MultiShapeView_shape, SHAPE_REC); mRoundRadius = typedArray.getDimensionPixelSize(R.styleable.MultiShapeView_round_radius, DEFAULT_ROUND_RADIUS); typedArray.recycle(); init(); } private void init() { mBitmapPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mBorderPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mBorderPaint.setStyle(Paint.Style.STROKE); mBorderPaint.setColor(mBorderColor); mBorderPaint.setStrokeWidth(mBorderWidth); mRcBitmap = new RectF(); mRcBorder = new RectF(); mShaderMatrix = new Matrix(); } public void setImageBitmap(Bitmap bitmap) { mBitmap = bitmap; preDraw(); } public void setImageDrawable(Drawable drawable) { mBitmap = getBitmapFromDrawable(drawable); preDraw(); } public void setImageResource(int resId) { if (resId != 0) { try { mBitmap = getBitmapFromDrawable(mContext.getResources().getDrawable(resId)); } catch (Exception e) { Log.w("MultiShapeView", "Unable to find resource: " + resId, e); } } preDraw(); } private Bitmap getBitmapFromDrawable(Drawable drawable) { if (drawable == null) { return null; } if (drawable instanceof BitmapDrawable) { return ((BitmapDrawable) drawable).getBitmap(); } try { Bitmap bitmap; if (drawable instanceof ColorDrawable) { bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); } else { bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); } Canvas canvas = new Canvas(bitmap); drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); drawable.draw(canvas); return bitmap; } catch (OutOfMemoryError e) { return null; } } /** * 初始化基本参数 */ private void preDraw() { if (mBitmap == null) { return; } mBitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); mBitmapPaint.setShader(mBitmapShader); mRcBorder.set(0, 0, getWidth(), getHeight()); mBorderRadius = Math.min((mRcBorder.height() - mBorderWidth) / 2, (mRcBorder.width() - mBorderWidth) / 2); //!!!注意,圆角矩形边框注意位置,否则边框和矩形之间会露出空白 if (mShape == SHAPE_CIRCLE) { mRcBitmap.set(mBorderWidth, mBorderWidth, mRcBorder.width() - mBorderWidth, mRcBorder.height() - mBorderWidth); } else if (mShape == SHAPE_REC) { mRcBitmap.set(mBorderWidth/2, mBorderWidth/2, mRcBorder.width() - mBorderWidth/2, mRcBorder.height() - mBorderWidth/2); } mCircleRadius = Math.min(mRcBitmap.height() / 2, mRcBitmap.width() / 2); updateShaderMatrix(); invalidate(); } /** * 伸缩变换 */ private void updateShaderMatrix() { float scale; float dx = 0; float dy = 0; mShaderMatrix.set(null); if (mBitmap.getWidth() * mRcBitmap.height() > mRcBitmap.width() * mBitmap.getHeight()) { scale = mRcBitmap.height() / (float) mBitmap.getHeight(); dx = (mRcBitmap.width() - mBitmap.getWidth() * scale) * 0.5f; } else { scale = mRcBitmap.width() / (float) mBitmap.getWidth(); dy = (mRcBitmap.height() - mBitmap.getHeight() * scale) * 0.5f; } mShaderMatrix.setScale(scale, scale); mShaderMatrix.postTranslate((int) (dx + 0.5f) + mBorderWidth, (int) (dy + 0.5f) + mBorderWidth); mBitmapShader.setLocalMatrix(mShaderMatrix); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); preDraw(); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); preDraw(); } @Override protected void onDraw(Canvas canvas) { if (mBitmap != null) { if (mShape == SHAPE_CIRCLE) { canvas.drawCircle(getWidth() / 2, getHeight() / 2, mCircleRadius, mBitmapPaint); canvas.drawCircle(getWidth() / 2, getHeight() / 2, mBorderRadius, mBorderPaint); } else if (mShape == SHAPE_REC) { canvas.drawRoundRect(mRcBitmap, mRoundRadius, mRoundRadius, mBitmapPaint); canvas.drawRoundRect(mRcBorder, mRoundRadius, mRoundRadius, mBorderPaint); } } } @Override public void setPressed(boolean pressed) { super.setPressed(pressed); if (mIsPressed == pressed) { return; } mIsPressed = pressed; if (mIsPressed) { mBitmapPaint.setColorFilter(new PorterDuffColorFilter(mCoverColor, PorterDuff.Mode.SRC_ATOP)); } else { mBitmapPaint.setColorFilter(null); } invalidate(); } //-----------------------------------------------getter/setter------------------------------ public int getBorderColor() { return mBorderColor; } public void setBorderColor(int borderColor) { if (borderColor == mBorderColor) { return; } mBorderColor = borderColor; invalidate(); } public int getBorderWidth() { return mBorderWidth; } public void setBorderWidth(int borderWidth) { if (borderWidth == mBorderWidth) { return; } mBorderWidth = borderWidth; preDraw(); } public int getCoverColor() { return mCoverColor; } public void setCoverColor(int coverColor) { if (coverColor == mCoverColor) { return; } mCoverColor = coverColor; } public int getShape() { return mShape; } public void setShape(int shape) { mShape = shape; preDraw(); } public float getRoundRadius() { return mRoundRadius; } public void setRoundRadius(float roundRadius) { mRoundRadius = roundRadius; preDraw(); } }
xml布局使用:
<com.debugcat.multishapeview.widget.MultiShapeView android:id="@+id/iv_circle_two" android:layout_toRightOf="@id/iv_circle_one" android:layout_marginLeft="30dp" app:border_color="#ffccdd" app:border_width="1.5dp" app:shape="circle" android:layout_width="100dp" android:layout_height="100dp"/>
大佬的github地址:https://github.com/msandroid/MultiShapeView