微信号:jszj2014215

介绍:主要分享移动互联网的相关产品和资讯,关注你将学习到更多,在互联网的当下你会赚更多的钱...

一个类搞定自定义圆形、圆角矩形View

2017-05-04 16:07 Android技术之家

有时显示的图片(如用户头像)是圆形或者圆角矩形的,如果我们把每一种形状的图片都裁剪成一个图片文件,这样既麻烦也浪费空间,所以最好的办法是通过代码来设置图片的显示形状。显示图片用到的是ImageView,最简单的设置图片形状的方法就是在draw()里面通过canvas.clipPath()把画布裁剪成相应形状,但这种方法有个很大的缺点,就是边缘锯齿明显。

这里我通过BitmapShader来绘制图片,可以很好地解决锯齿的问题,将画笔的渲染器设置成BitmapShader,则通过画笔绘制的图案则以图片为背景。关键步骤为:

[java] view plain copy 

  1. // 获取图片  

  2. Bitmap bitmap = Util.getBitmapFromDrawable(getDrawable());  

  3. // 设置图片渲染器  

  4. mBitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);  

  5. // 把渲染器放入画笔中  

  6. mBitmapPaint.setShader(mBitmapShader);  

  7. // 在画布上画圆,即可绘制出圆形图片  

  8. canvas.drawCircle(cx, cy, radius, mBitmapPaint);  


效果如下:



关键代码:

[java] view plain copy 

  1. public class ShapeImageView extends ImageView {  

  2.   

  3.     public static int SHAPE_REC = 1// 矩形  

  4.     public static int SHAPE_CIRCLE = 2// 圆形  

  5.     public static int SHAPE_OVAL = 3// 椭圆  

  6.   

  7.     private float mBorderSize = 0// 边框大小,默认为0,即无边框  

  8.     private int mBorderColor = Color.WHITE; // 边框颜色,默认为白色  

  9.     private int mShape = SHAPE_REC; // 形状,默认为直接矩形  

  10.     private float mRoundRadius = 0// 矩形的圆角半径,默认为0,即直角矩形  

  11.     private Paint mBorderPaint = new Paint(Paint.ANTI_ALIAS_FLAG);  

  12.     private RectF mViewRect = new RectF(); // imageview的矩形区域  

  13.     private RectF mBorderRect = new RectF(); // 边框的矩形区域  

  14.   

  15.     private final Matrix mShaderMatrix = new Matrix();  

  16.     private Paint mBitmapPaint = new Paint();  

  17.     private BitmapShader mBitmapShader;  

  18.     private Bitmap mBitmap;  

  19.   

  20.     public ShapeImageView(Context context, AttributeSet attrs) {  

  21.         this(context, attrs, 0);  

  22.     }  

  23.   

  24.     public ShapeImageView(Context context, AttributeSet attrs, int defStyle) {  

  25.         super(context, attrs, defStyle); // 虽然此处会调用setImageDrawable,但此时成员变量还未被正确初始化  

  26.         init(attrs);  

  27.         mBorderPaint.setStyle(Style.STROKE);  

  28.         mBorderPaint.setStrokeWidth(mBorderSize);  

  29.         mBorderPaint.setColor(mBorderColor);  

  30.         mBorderPaint.setAntiAlias(true);  

  31.         mBitmapPaint.setAntiAlias(true);  

  32.         super.setScaleType(ScaleType.CENTER_CROP); // 固定为CENTER_CROP,其他不生效  

  33.     }  

  34.   

  35.   

  36.     @Override  

  37.     public void setImageResource(int resId) {  

  38.         super.setImageResource(resId);  

  39.         mBitmap = Util.getBitmapFromDrawable(getDrawable());  

  40.         setupBitmapShader();  

  41.     }  

  42.   

  43.     @Override  

  44.     public void setImageDrawable(Drawable drawable) {  

  45.         super.setImageDrawable(drawable);  

  46.         mBitmap = Util.getBitmapFromDrawable(drawable);  

  47.         setupBitmapShader();  

  48.     }  

  49.   

  50.     @Override  

  51.     public void setScaleType(ScaleType scaleType) {  

  52.         if (scaleType != ScaleType.CENTER_CROP) {  

  53.             throw new IllegalArgumentException(String.format("ScaleType %s not supported.", scaleType));  

  54.         }  

  55.     }  

  56.   

  57.     private void init(AttributeSet attrs) {  

  58.   

  59.         TypedArray a = getContext().obtainStyledAttributes(attrs,  

  60.                 R.styleable.ShapeImageView);  

  61.         mShape = a.getInt(R.styleable.ShapeImageView_shape, mShape);  

  62.         mRoundRadius = a.getDimension(R.styleable.ShapeImageView_round_radius, mRoundRadius);  

  63.         mBorderSize = a.getDimension(R.styleable.ShapeImageView_border_size, mBorderSize);  

  64.         mBorderColor = a.getColor(R.styleable.ShapeImageView_border_color, mBorderColor);  

  65.         a.recycle();  

  66.     }  

  67.   

  68.     /** 

  69.      * 对于普通的view,在执行到onDraw()时,背景图已绘制完成 

  70.      * <p/> 

  71.      * 对于ViewGroup,当它没有背景时直接调用的是dispatchDraw()方法, 而绕过了draw()方法, 

  72.      * 当它有背景的时候就调用draw()方法,而draw()方法里包含了dispatchDraw()方法的调用, 

  73.      */  

  74.     @Override  

  75.     public void onDraw(Canvas canvas) {  

  76.   

  77.         if (getDrawable() != null) {  

  78.             if (mShape == SHAPE_CIRCLE) {  

  79.                 canvas.drawCircle(getWidth() / 2, getHeight() / 2,  

  80.                         Math.min(getWidth(), getHeight()) / 2, mBitmapPaint);  

  81.             } else if (mShape == SHAPE_OVAL) {  

  82.                 canvas.drawOval(mViewRect, mBitmapPaint);  

  83.             } else {  

  84.                 canvas.drawRoundRect(mViewRect, mRoundRadius, mRoundRadius, mBitmapPaint);  

  85.             }  

  86.         }  

  87.   

  88.   

  89.         if (mBorderSize > 0) { // 绘制边框  

  90.             if (mShape == SHAPE_CIRCLE) {  

  91.                 canvas.drawCircle(mViewRect.right / 2, mViewRect.bottom / 2,  

  92.                         Math.min(mViewRect.right, mViewRect.bottom) / 2 - mBorderSize / 2, mBorderPaint);  

  93.             } else if (mShape == SHAPE_OVAL) {  

  94.                 canvas.drawOval(mBorderRect, mBorderPaint);  

  95.             } else {  

  96.                 canvas.drawRoundRect(mBorderRect, mRoundRadius, mRoundRadius, mBorderPaint);  

  97.             }  

  98.         }  

  99.     }  

  100.   

  101.     @Override  

  102.     protected void onSizeChanged(int w, int h, int oldw, int oldh) {  

  103.         super.onSizeChanged(w, h, oldw, oldh);  

  104.         initRect();  

  105.         setupBitmapShader();  

  106.     }  

  107.   

  108.     // 不能在onLayout()调用invalidate(),否则导致绘制异常。(setupBitmapShader()中调用了invalidate())  

  109.     @Override  

  110.     protected void onLayout(boolean changed, int left, int top, int right,  

  111.                             int bottom) {  

  112.         super.onLayout(changed, left, top, right, bottom);  

  113. //        initRect();  

  114. //        setupBitmapShader();  

  115.     }  

  116.   

  117.     private void setupBitmapShader() {  

  118.         // super(context, attrs, defStyle)调用setImageDrawable时,成员变量还未被正确初始化  

  119.         if (mBitmapPaint == null) {  

  120.             return;  

  121.         }  

  122.         if (mBitmap == null) {  

  123.             invalidate();  

  124.             return;  

  125.         }  

  126.         mBitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);  

  127.         mBitmapPaint.setShader(mBitmapShader);  

  128.   

  129.         // 固定为CENTER_CROP,使图片在view中居中并裁剪  

  130.         mShaderMatrix.set(null);  

  131.         // 缩放到高或宽 与view的高或宽 匹配  

  132.         float scale = Math.max(getWidth() * 1f / mBitmap.getWidth(), getHeight() * 1f / mBitmap.getHeight());  

  133.         // 由于BitmapShader默认是从画布的左上角开始绘制,所以把其平移到画布中间,即居中  

  134.         float dx = (getWidth() - mBitmap.getWidth() * scale) / 2;  

  135.         float dy = (getHeight() - mBitmap.getHeight() * scale) / 2;  

  136.         mShaderMatrix.setScale(scale, scale);  

  137.         mShaderMatrix.postTranslate(dx, dy);  

  138.         mBitmapShader.setLocalMatrix(mShaderMatrix);  

  139.         invalidate();  

  140.     }  

  141.   

  142.     // 设置图片的绘制区域  

  143.     private void initRect() {  

  144.   

  145.         mViewRect.top = 0;  

  146.         mViewRect.left = 0;  

  147.         mViewRect.right = getWidth(); // 宽度  

  148.         mViewRect.bottom = getHeight(); // 高度  

  149.   

  150.         // 边框的矩形区域不能等于ImageView的矩形区域,否则边框的宽度只显示了一半  

  151.         mBorderRect.top = mBorderSize / 2;  

  152.         mBorderRect.left = mBorderSize / 2;  

  153.         mBorderRect.right = getWidth() - mBorderSize / 2;  

  154.         mBorderRect.bottom = getHeight() - mBorderSize / 2;  

  155.     }  

  156.   

  157.     public int getShape() {  

  158.         return mShape;  

  159.     }  

  160.   

  161.     public void setShape(int shape) {  

  162.         mShape = shape;  

  163.     }  

  164.   

  165.     public float getBorderSize() {  

  166.         return mBorderSize;  

  167.     }  

  168.   

  169.     public void setBorderSize(int mBorderSize) {  

  170.         this.mBorderSize = mBorderSize;  

  171.         mBorderPaint.setStrokeWidth(mBorderSize);  

  172.         initRect();  

  173.         invalidate();  

  174.     }  

  175.   

  176.     public int getBorderColor() {  

  177.         return mBorderColor;  

  178.     }  

  179.   

  180.     public void setBorderColor(int mBorderColor) {  

  181.         this.mBorderColor = mBorderColor;  

  182.         mBorderPaint.setColor(mBorderColor);  

  183.         invalidate();  

  184.     }  

  185.   

  186.     public float getRoundRadius() {  

  187.         return mRoundRadius;  

  188.     }  

  189.   

  190.     public void setRoundRadius(float mRoundRadius) {  

  191.         this.mRoundRadius = mRoundRadius;  

  192.         invalidate();  

  193.     }  

  194. }  



 res/values/attrs.xml

[html] view plain copy 

  1. <declare-styleable name="ShapeImageView">  

  2.         <attr name="shape" format="enum">  

  3.             <enum name="rect" value="1"/>  

  4.             <enum name="circle" value="2"/>  

  5.             <enum name="oval" value="3"/>  

  6.         </attr>  

  7.         <attr name="round_radius" format="dimension"/>  

  8.         <attr name="border_size" format="dimension"/>  

  9.         <attr name="border_color" format="color"/>  

  10.     </declare-styleable>  

相关代码我放在了github上:https://github.com/1993hzw/Androids


 
Android技术之家 更多文章 Android全套动画使用技巧 Android中内存优化的那些事 - 一个有关图片的优化记录 调用手机相机拍照之后Activity变量回收 十大程序员开发用到的基本算法 Android中Gradle详细实用指南
猜您喜欢 【2017年2月16日】年过30后面的路怎么选 PostgreSQL与Linux内核版本 科普:浅谈DDoS那些事儿@盛大游戏安全部负责人 intel: CAT技术助力数据中心资源隔离 ES、Kibana权限控制