Android实战简易教程<六十一>(圆形显示的ImageView)

本文介绍了一种自定义圆形ImageView的方法,包括创建CircularImageView类并重写onMeasure和onDraw方法,以及如何在布局文件中使用它。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

ImageView在我们的项目中经常使用,一般ImageView是正方形的,要使用圆形的ImageView可以通过自定义View来实现,下面我们介绍一下如何实现。

1.CircularImageView.java 继承自ImageView:

[java]  view plain copy
  1. package com.yayun.circularimageview;  
  2.   
  3. import com.mikhaellopez.circularimageview.R;  
  4.   
  5. import android.content.Context;  
  6. import android.content.res.TypedArray;  
  7. import android.graphics.Bitmap;  
  8. import android.graphics.BitmapShader;  
  9. import android.graphics.Canvas;  
  10. import android.graphics.Color;  
  11. import android.graphics.Paint;  
  12. import android.graphics.Shader;  
  13. import android.graphics.drawable.BitmapDrawable;  
  14. import android.graphics.drawable.Drawable;  
  15. import android.util.AttributeSet;  
  16. import android.widget.ImageView;  
  17.   
  18. public class CircularImageView extends ImageView {  
  19.     private int borderWidth;  
  20.     private int canvasSize;  
  21.     private Bitmap image;  
  22.     private Paint paint;  
  23.     private Paint paintBorder;  
  24.   
  25.     public CircularImageView(final Context context) {  
  26.         this(context, null);  
  27.     }  
  28.   
  29.     public CircularImageView(Context context, AttributeSet attrs) {  
  30.         this(context, attrs, R.attr.circularImageViewStyle);  
  31.     }  
  32.   
  33.     public CircularImageView(Context context, AttributeSet attrs, int defStyle) {  
  34.         super(context, attrs, defStyle);  
  35.   
  36.         // init paint  
  37.         paint = new Paint();  
  38.         paint.setAntiAlias(true);  
  39.   
  40.         paintBorder = new Paint();  
  41.         paintBorder.setAntiAlias(true);// 抗锯齿  
  42.   
  43.         TypedArray attributes = context.obtainStyledAttributes(attrs,  
  44.                 R.styleable.CircularImageView, defStyle, 0);  
  45.   
  46.         /** 
  47.          * 有边框 
  48.          */  
  49.         if (attributes.getBoolean(R.styleable.CircularImageView_border, true)) {  
  50.             int defaultBorderSize = (int) (4 * getContext().getResources()  
  51.                     .getDisplayMetrics().density + 0.5f);// 默认宽度  
  52.             setBorderWidth(attributes.getDimensionPixelOffset(  
  53.                     R.styleable.CircularImageView_border_width,  
  54.                     defaultBorderSize));// 边框大小  
  55.             setBorderColor(attributes.getColor(  
  56.                     R.styleable.CircularImageView_border_color, Color.WHITE));// 边框颜色  
  57.         }  
  58.   
  59.         /** 
  60.          * 无边框 
  61.          */  
  62.         if (attributes.getBoolean(R.styleable.CircularImageView_shadow, false))  
  63.             addShadow();  
  64.     }  
  65.   
  66.     /** 
  67.      * 设置边框宽度 
  68.      *  
  69.      * @param borderWidth 
  70.      */  
  71.     public void setBorderWidth(int borderWidth) {  
  72.         this.borderWidth = borderWidth;  
  73.         this.requestLayout();  
  74.         this.invalidate();  
  75.     }  
  76.   
  77.     /** 
  78.      * 设置边框颜色 
  79.      *  
  80.      * @param borderColor 
  81.      */  
  82.     public void setBorderColor(int borderColor) {  
  83.         if (paintBorder != null)  
  84.             paintBorder.setColor(borderColor);  
  85.         this.invalidate();  
  86.     }  
  87.   
  88.     public void addShadow() {  
  89.         setLayerType(LAYER_TYPE_SOFTWARE, paintBorder);  
  90.         paintBorder.setShadowLayer(4.0f, 0.0f, 2.0f, Color.BLACK);  
  91.     }  
  92.   
  93.     @Override  
  94.     public void onDraw(Canvas canvas) {  
  95.         // load the bitmap  
  96.         image = drawableToBitmap(getDrawable());  
  97.   
  98.       
  99.         if (image != null) {  
  100.   
  101.             canvasSize = canvas.getWidth();  
  102.             if (canvas.getHeight() < canvasSize)// 取小值  
  103.                 canvasSize = canvas.getHeight();  
  104.   
  105.             BitmapShader shader = new BitmapShader(Bitmap.createScaledBitmap(  
  106.                     image, canvasSize, canvasSize, false),  
  107.                     Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);  
  108.             paint.setShader(shader);  
  109.   
  110.       
  111.             /** 
  112.              * 圆心 
  113.              */  
  114.             int circleCenter = (canvasSize - (borderWidth * 2)) / 2;  
  115.             // void android.graphics.Canvas.drawCircle(float cx, float cy, float radius, Paint paint)  
  116.             canvas.drawCircle(circleCenter + borderWidth, circleCenter  
  117.                     + borderWidth, ((canvasSize - (borderWidth * 2)) / 2)  
  118.                     + borderWidth - 4.0f, paintBorder);  
  119.             canvas.drawCircle(circleCenter + borderWidth, circleCenter  
  120.                     + borderWidth,  
  121.                     ((canvasSize - (borderWidth * 2)) / 2) - 4.0f, paint);  
  122.         }  
  123.     }  
  124.   
  125.     @Override  
  126.     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
  127.         int width = measureWidth(widthMeasureSpec);  
  128.         int height = measureHeight(heightMeasureSpec);  
  129.         setMeasuredDimension(width, height);  
  130.     }  
  131.   
  132.     private int measureWidth(int measureSpec) {  
  133.         int result = 0;  
  134.         int specMode = MeasureSpec.getMode(measureSpec);  
  135.         int specSize = MeasureSpec.getSize(measureSpec);  
  136.   
  137.         if (specMode == MeasureSpec.EXACTLY) {  
  138.             // The parent has determined an exact size for the child.  
  139.             result = specSize;  
  140.         } else if (specMode == MeasureSpec.AT_MOST) {  
  141.             // The child can be as large as it wants up to the specified size.  
  142.             result = specSize;  
  143.         } else {  
  144.             // The parent has not imposed any constraint on the child.  
  145.             result = canvasSize;  
  146.         }  
  147.   
  148.         return result;  
  149.     }  
  150.   
  151.     private int measureHeight(int measureSpecHeight) {  
  152.         int result = 0;  
  153.         int specMode = MeasureSpec.getMode(measureSpecHeight);  
  154.         int specSize = MeasureSpec.getSize(measureSpecHeight);  
  155.   
  156.         if (specMode == MeasureSpec.EXACTLY) {  
  157.             // We were told how big to be  
  158.             result = specSize;  
  159.         } else if (specMode == MeasureSpec.AT_MOST) {  
  160.             // The child can be as large as it wants up to the specified size.  
  161.             result = specSize;  
  162.         } else {  
  163.             // Measure the text (beware: ascent is a negative number)  
  164.             result = canvasSize;  
  165.         }  
  166.   
  167.         return (result + 2);  
  168.     }  
  169.   
  170.     /** 
  171.      * 获取bitmap 
  172.      *  
  173.      * @param drawable 
  174.      * @return 
  175.      */  
  176.     public Bitmap drawableToBitmap(Drawable drawable) {  
  177.         if (drawable == null) {  
  178.             return null;  
  179.         } else if (drawable instanceof BitmapDrawable) {  
  180.             return ((BitmapDrawable) drawable).getBitmap();  
  181.         }  
  182.   
  183.         Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(),  
  184.                 drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);  
  185.         Canvas canvas = new Canvas(bitmap);  
  186.         drawable.setBounds(00, canvas.getWidth(), canvas.getHeight());  
  187.         drawable.draw(canvas);  
  188.   
  189.         return bitmap;  
  190.     }  
  191. }  

知识:一般来说,自定义控件都会去重写View的onMeasure方法,因为该方法指定该控件在屏幕上的大小。

protected void?onMeasure?(int widthMeasureSpec, int heightMeasureSpec)

onMeasure传入的两个参数是由上一层控件传入的大小,有多种情况,重写该方法时需要对计算控件的实际大小,然后调用setMeasuredDimension(int, int)设置实际大小。

onMeasure传入的widthMeasureSpec和heightMeasureSpec不是一般的尺寸数值,而是将模式和尺寸组合在一起的数值。我们需要通过int mode = MeasureSpec.getMode(widthMeasureSpec)得到模式,用int size =?MeasureSpec.getSize(widthMeasureSpec)得到尺寸。

mode共有三种情况,取值分别为

MeasureSpec.UNSPECIFIEDMeasureSpec.EXACTLYMeasureSpec.AT_MOST

MeasureSpec.EXACTLY是精确尺寸,当我们将控件的layout_width或layout_height指定为具体数值时如andorid:layout_width="50dip",或者为FILL_PARENT是,都是控件大小已经确定的情况,都是精确尺寸。

MeasureSpec.AT_MOST是最大尺寸,当控件的layout_width或layout_height指定为WRAP_CONTENT时,控件大小一般随着控件的子空间或内容进行变化,此时控件尺寸只要不超过父控件允许的最大尺寸即可。因此,此时的mode是AT_MOST,size给出了父控件允许的最大尺寸。

MeasureSpec.UNSPECIFIED是未指定尺寸,这种情况不多,一般都是父控件是AdapterView,通过measure方法传入的模式。

2.属性文件:

[html]  view plain copy
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <resources>  
  3.   
  4.     <declare-styleable name="CircularImageView">  
  5.         <attr name="border" format="boolean"></attr>  
  6.         <attr name="border_width" format="dimension"></attr>  
  7.         <attr name="border_color" format="color"></attr>  
  8.         <attr name="shadow" format="boolean"></attr>  
  9.     </declare-styleable>  
  10.       
  11.     <declare-styleable name="Theme">  
  12.         <attr name="circularImageViewStyle" format="reference"></attr>  
  13.     </declare-styleable>  
  14.   
  15. </resources>  
3.我们在布局文件中引用:

[html]  view plain copy
  1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     xmlns:app="http://schemas.android.com/apk/res/com.mikhaellopez.circularimageviewsample"  
  3.     android:layout_width="fill_parent"  
  4.     android:layout_height="fill_parent"  
  5.     android:gravity="center"  
  6.     android:orientation="vertical" >  
  7.   
  8.     <com.yayun.circularimageview.CircularImageView  
  9.         android:layout_width="250dp"  
  10.         android:layout_height="250dp"  
  11.         android:src="@drawable/image"  
  12.         app:border="true"  
  13.         app:border_color="@color/Blue"  
  14.         app:border_width="14dp"  
  15.         app:shadow="true" />  
  16.       
  17.     <com.yayun.circularimageview.CircularImageView  
  18.         android:layout_width="200dp"  
  19.         android:layout_height="200dp"  
  20.         android:src="@drawable/image"  
  21.          />  
  22.   
  23. </LinearLayout>  
运行实例效果如下:


第一个图片有边框的效果,可以自行设置。

源码下载

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值