Android自定义控件实例,圆形头像(图库 + 裁剪+设置),上传头像显示为圆形

mm19870604 8年前
   <p>Android项目开发中经常会遇见需要实现圆角或者圆形的图片功能,如果仅仅使用系统自带的ImageView控件显然无法实现此功能,所以通过系列文章的形式由简到繁全方位的介绍一下此功能的实现,巩固一下自身的学习,同时,和广大网友交流分享</p>    <p>首先看效果图</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/1e3322a4c746095951b0e3c6b4850efb.png"> <img src="https://simg.open-open.com/show/70983a9334c7c801d28412e201ede196.png"> <img src="https://simg.open-open.com/show/114992d95446dac11806e6d9a937da7d.png"> <img src="https://simg.open-open.com/show/08cfd6fb29454626ef4a5f1c7129cdab.png"></p>    <p>首先看一下CircleImageView的主要流程</p>    <p>1. 首先通过setImageXxx()方法设置图片Bitmap;</p>    <p>2. 进入构造函数CircleImageView()获取自定义参数,以及调用setup()函数; </p>    <p>3. 进入setup()函数(非常关键),进行图片画笔边界画笔(Paint)一些重绘参数初始化:构建渲染器BitmapShader用Bitmap来填充绘制区域,设置样式和内外圆半径计算等,以及调用updateShaderMatrix()函数和 invalidate()函数; </p>    <p>4. 进入updateShaderMatrix()函数,计算缩放比例和平移,设置BitmapShader的Matrix参数等; </p>    <p>5. 触发ondraw()函数完成最终的绘制。使用配置好的Paint先画出绘制内圆形来以后再画边界圆形。</p>    <p>下面看具体的代码实现:</p>    <p>attr.xml</p>    <pre>  <code class="language-java"><?xml version="1.0" encoding="utf-8"?>  <resources>      <declare-styleable name="roundedimageview">          <attr name="border_thickness" format="dimension" />          <attr name="border_inside_color" format="color" />          <attr name="border_outside_color" format="color"></attr>      </declare-styleable>  </resources>  </code></pre>    <p>然后是自定义ImageView的实现类:</p>    <pre>  <code class="language-java">RoundImageView.class          </code></pre>    <pre>  <code class="language-java">**   * 圆形ImageView,可设置最多两个宽度不同且颜色不同的圆形边框。   * 设置颜色在xml布局文件中由自定义属性配置参数指定   */  public class RoundImageView extends ImageView {      private int mBorderThickness = 0;      private Context mContext;      private int defaultColor = 0xFFFFFFFF;      // 如果只有其中一个有值,则只画一个圆形边框      private int mBorderOutsideColor = 0;      private int mBorderInsideColor = 0;      // 控件默认长、宽      private int defaultWidth = 0;      private int defaultHeight = 0;  //构造方法,参数上下文      public RoundImageView(Context context) {          super(context);          mContext = context;      }        public RoundImageView(Context context, AttributeSet attrs) {          super(context, attrs);          mContext = context;          setCustomAttributes(attrs);      }        public RoundImageView(Context context, AttributeSet attrs, int defStyle) {          super(context, attrs, defStyle);          mContext = context;          setCustomAttributes(attrs);      }        private void setCustomAttributes(AttributeSet attrs) {          TypedArray a = mContext.obtainStyledAttributes(attrs, R.styleable.roundedimageview);          mBorderThickness = a.getDimensionPixelSize(R.styleable.roundedimageview_border_thickness, 0);          mBorderOutsideColor = a.getColor(R.styleable.roundedimageview_border_outside_color, defaultColor);          mBorderInsideColor = a.getColor(R.styleable.roundedimageview_border_inside_color, defaultColor);      }        @Override      protected void onDraw(Canvas canvas) {          Drawable drawable = getDrawable();          if (drawable == null) {              return;          }          if (getWidth() == 0 || getHeight() == 0) {              return;          }          this.measure(0, 0);          if (drawable.getClass() == NinePatchDrawable.class)              return;          Bitmap b = ((BitmapDrawable) drawable).getBitmap();          Bitmap bitmap = b.copy(Bitmap.Config.ARGB_8888, true);          if (defaultWidth == 0) {              defaultWidth = getWidth();          }          if (defaultHeight == 0) {              defaultHeight = getHeight();          }          int radius = 0;          if (mBorderInsideColor != defaultColor && mBorderOutsideColor != defaultColor) {// 定义画两个边框,分别为外圆边框和内圆边框              radius = (defaultWidth < defaultHeight ? defaultWidth : defaultHeight) / 2 - 2 * mBorderThickness;              // 画内圆              drawCircleBorder(canvas, radius + mBorderThickness / 2, mBorderInsideColor);              // 画外圆              drawCircleBorder(canvas, radius + mBorderThickness + mBorderThickness / 2, mBorderOutsideColor);          } else if (mBorderInsideColor != defaultColor && mBorderOutsideColor == defaultColor) {// 定义画一个边框              radius = (defaultWidth < defaultHeight ? defaultWidth : defaultHeight) / 2 - mBorderThickness;              drawCircleBorder(canvas, radius + mBorderThickness / 2, mBorderInsideColor);          } else if (mBorderInsideColor == defaultColor && mBorderOutsideColor != defaultColor) {// 定义画一个边框              radius = (defaultWidth < defaultHeight ? defaultWidth : defaultHeight) / 2 - mBorderThickness;              drawCircleBorder(canvas, radius + mBorderThickness / 2, mBorderOutsideColor);          } else {// 没有边框              radius = (defaultWidth < defaultHeight ? defaultWidth : defaultHeight) / 2;          }          Bitmap roundBitmap = getCroppedRoundBitmap(bitmap, radius);          canvas.drawBitmap(roundBitmap, defaultWidth / 2 - radius, defaultHeight / 2 - radius, null);      }        /**       * 获取裁剪后的圆形图片       *       * @param       */  //    radius半径      public Bitmap getCroppedRoundBitmap(Bitmap bmp, int radius) {          Bitmap scaledSrcBmp;          int diameter = radius * 2;          // 为了防止宽高不相等,造成圆形图片变形,因此截取长方形中处于中间位置最大的正方形图片          int bmpWidth = bmp.getWidth();          int bmpHeight = bmp.getHeight();          int squareWidth = 0, squareHeight = 0;          int x = 0, y = 0;          Bitmap squareBitmap;          if (bmpHeight > bmpWidth) {// 高大于宽              squareWidth = squareHeight = bmpWidth;              x = 0;              y = (bmpHeight - bmpWidth) / 2;              // 截取正方形图片              squareBitmap = Bitmap.createBitmap(bmp, x, y, squareWidth, squareHeight);          } else if (bmpHeight < bmpWidth) {// 宽大于高              squareWidth = squareHeight = bmpHeight;              x = (bmpWidth - bmpHeight) / 2;              y = 0;              squareBitmap = Bitmap.createBitmap(bmp, x, y, squareWidth, squareHeight);          } else {              squareBitmap = bmp;          }          if (squareBitmap.getWidth() != diameter || squareBitmap.getHeight() != diameter) {              scaledSrcBmp = Bitmap.createScaledBitmap(squareBitmap, diameter, diameter, true);          } else {              scaledSrcBmp = squareBitmap;          }          Bitmap output = Bitmap.createBitmap(scaledSrcBmp.getWidth(),                  scaledSrcBmp.getHeight(),                  Bitmap.Config.ARGB_8888);          Canvas canvas = new Canvas(output);            Paint paint = new Paint();          Rect rect = new Rect(0, 0, scaledSrcBmp.getWidth(), scaledSrcBmp.getHeight());            paint.setAntiAlias(true);          paint.setFilterBitmap(true);          paint.setDither(true);          canvas.drawARGB(0, 0, 0, 0);          canvas.drawCircle(scaledSrcBmp.getWidth() / 2,                  scaledSrcBmp.getHeight() / 2,                  scaledSrcBmp.getWidth() / 2,                  paint);          paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));          canvas.drawBitmap(scaledSrcBmp, rect, rect, paint);          bmp = null;          squareBitmap = null;          scaledSrcBmp = null;          return output;      }        /**       * 边缘画圆       */      private void drawCircleBorder(Canvas canvas, int radius, int color) {          Paint paint = new Paint();              /* 去锯齿 */          paint.setAntiAlias(true);          paint.setFilterBitmap(true);          paint.setDither(true);          paint.setColor(color);              /* 设置paint的    style    为STROKE:空心 */          paint.setStyle(Paint.Style.STROKE);              /* 设置paint的外框宽度 */          paint.setStrokeWidth(mBorderThickness);          canvas.drawCircle(defaultWidth / 2, defaultHeight / 2, radius, paint);      }  }  </code></pre>    <p>使用时非常简单,直接在布局中调用自定义的类</p>    <p>activity_main.xml,这里要注意的是,第二个是带有边框的,效果图如上</p>    <pre>  <code class="language-java"><?xml version="1.0" encoding="utf-8"?>  <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"      xmlns:imagecontrol="http://schemas.android.com/apk/res-auto"      xmlns:tools="http://schemas.android.com/tools"      android:id="@+id/activity_main"      android:layout_width="match_parent"      android:layout_height="match_parent"      android:orientation="vertical"      android:paddingBottom="@dimen/activity_vertical_margin"      android:paddingLeft="@dimen/activity_horizontal_margin"      android:paddingRight="@dimen/activity_horizontal_margin"      android:paddingTop="@dimen/activity_vertical_margin"      tools:context="com.example.ceshi.MainActivity">        <Button          android:id="@+id/aaa"          android:layout_width="match_parent"          android:layout_height="wrap_content"          android:text="点击设置头像" />        <LinearLayout          android:layout_width="match_parent"          android:layout_height="wrap_content"          android:orientation="horizontal">            <com.example.ceshi.RoundImageView              android:id="@+id/image"              android:layout_width="0dp"              android:layout_weight="1"              android:layout_height="wrap_content" />          <!-- border_outside_color 外部圆圈的颜色 -->          <!-- border_inside_color 内部部圆圈的颜色 -->          <!-- border_thickness 外圆和内圆的宽度 -->          <com.example.ceshi.RoundImageView              android:id="@+id/img2"              android:layout_width="0dp"              android:layout_weight="1"              android:layout_height="wrap_content"              imagecontrol:border_inside_color="#bc0978"              imagecontrol:border_outside_color="#ba3456"              imagecontrol:border_thickness="1dp" />      </LinearLayout>  </code></pre>    <p>打开图库选取图片,截取图片和设置图片在MainActivity.class中实现</p>    <p>MainActivity.class</p>    <pre>  <code class="language-java">public class MainActivity extends AppCompatActivity {      private Button bt;      private static final int PHOTO_REQUEST_GALLERY = 2;// 从相册中选择      private static final int PHOTO_REQUEST_CUT = 3;// 结果      private ImageView photo,photo2;      private File tempFile;      @Override      protected void onCreate(Bundle savedInstanceState) {          super.onCreate(savedInstanceState);          setContentView(R.layout.activity_main);          photo= (ImageView) findViewById(R.id.image);          photo2= (ImageView) findViewById(R.id.img2);            bt = (Button) findViewById(R.id.aaa);          bt.setOnClickListener(new View.OnClickListener() {              @Override              public void onClick(View v) {  //                Intent intent=new Intent(Intent.ACTION_PICK);  //                intent.setType("image/*");  //                startActivityForResult(intent,PHOTO_REQUEST_GALLERY);                  // 激活系统图库,选择一张图片                  Intent intent = new Intent(Intent.ACTION_PICK);                  intent.setType("image/*");                  // 开启一个带有返回值的Activity,请求码为PHOTO_REQUEST_GALLERY                  startActivityForResult(intent, PHOTO_REQUEST_GALLERY);              }          });      }        /*              * 剪切图片              */      private void crop(Uri uri) {          // 裁剪图片意图          Intent intent = new Intent("com.android.camera.action.CROP");          intent.setDataAndType(uri, "image/*");          intent.putExtra("crop", "true");          // 裁剪框的比例,1:1          intent.putExtra("aspectX", 1);          intent.putExtra("aspectY", 1);          // 裁剪后输出图片的尺寸大小          intent.putExtra("outputX", 250);          intent.putExtra("outputY", 250);            intent.putExtra("outputFormat", "JPEG");// 图片格式          intent.putExtra("noFaceDetection", true);// 取消人脸识别          intent.putExtra("return-data", true);          // 开启一个带有返回值的Activity,请求码为PHOTO_REQUEST_CUT          startActivityForResult(intent, PHOTO_REQUEST_CUT);      }        @Override      protected void onActivityResult(int requestCode, int resultCode, Intent data) {          super.onActivityResult(requestCode, resultCode, data);          if (requestCode == PHOTO_REQUEST_GALLERY) {              // 从相册返回的数据              if (data != null) {                  // 得到图片的全路径                  Uri uri = data.getData();                  crop(uri);              }          } else if (requestCode == PHOTO_REQUEST_CUT) {              // 从剪切图片返回的数据              if (data != null) {                  Bitmap bitmap = data.getParcelableExtra("data");                  photo.setImageBitmap(bitmap);                  photo2.setImageBitmap(bitmap);              }              try {                  // 将临时文件删除                  tempFile.delete();              } catch (Exception e) {                  e.printStackTrace();              }            }          super.onActivityResult(requestCode, resultCode, data);        }  }  </code></pre>    <p>以上就可以实现自定义控件,设置圆形头像</p>    <p> </p>    <p>来自:http://www.cnblogs.com/dingxiansen/p/6135795.html</p>    <p> </p>