自定义圆形控件RoundImageView并认识一下attr.xml

昨天我们学习了自定义带图片和文字的ImageTextButton,非常简单,我承诺给大家要讲一下用自定义属性的方式学习真正的实现自定义控件,在布局文件中使用属性的方式就需要用到attr.xml这个文件,以前很多同学问我这个是干什么的,现在学了这篇内容,你就差不多知道了,以后就别再问了。自定义圆形控件 RoundImageView ,我相信大家在开发中会经常遇到设置圆形头像的情况,因为这样的头像显得漂亮。怎么做呢?先看效果图:

讲之前解释一下attr.xml的作用,我用土话废话说,这样容易理解:比如我自定义一个控件,怎么实现呢,以RoundImageView为例,首先是继承ImageView,然后实现其构造函数,在构造函数中,获取attr.xml中的属性值(再次解释:这里获取的具体的这个属性的值是怎么来的呢?比如颜色和宽度,这个在attr.xml中定义了相关的名字,而在使用RoundImageView的xml布局文件中,我们会为其设置值,这里需要用的值,就是从那里设置的),并设置在本控件中,然后继承onDraw方法,画出自己想要的图形或者形状即可。

由于我在代码中加了很多注释,我就直接给大家介绍代码了哈。

第一步:定义RoundImageView的属性配置文件:attr.xml

<?xml version="1.0" encoding="utf-8"?>

<resources>


    <declare-styleable name="round_image_view">
        <attr name="border_width" format="dimension" />
        <attr name="border_incolor" format="color" />
        <attr name="border_outcolor" format="color"></attr>
    </declare-styleable>


</resources>

第二步:自定义圆形控件RoundImageView并继承ImageView

package net.loonggg.rivd.demo.view;



import net.loonggg.rivd.demo.R;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.NinePatchDrawable;
import android.util.AttributeSet;
import android.widget.ImageView;


/**
 * 
 * @author loongggdroid
 * 
 */
public class RoundImageView extends ImageView {
 private int mBorderThickness = 0;
 private Context mContext;
 private int defaultColor = 0xFFFFFFFF;
 // 外圆边框颜色
 private int mBorderOutsideColor = 0;
 // 内圆边框颜色
 private int mBorderInsideColor = 0;
 // RoundImageView控件默认的长、宽
 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;
 // 设置RoundImageView的属性值,比如颜色,宽度等
 setRoundImageViewAttributes(attrs);
 }


 public RoundImageView(Context context, AttributeSet attrs, int defStyle) {
 super(context, attrs, defStyle);
 mContext = context;
 setRoundImageViewAttributes(attrs);
 }


 // 从attr.xml文件中获取属性值,并给RoundImageView设置
 private void setRoundImageViewAttributes(AttributeSet attrs) {
 TypedArray a = mContext.obtainStyledAttributes(attrs,
 R.styleable.round_image_view);
 mBorderThickness = a.getDimensionPixelSize(
 R.styleable.round_image_view_border_width, 0);
 mBorderOutsideColor = a.getColor(
 R.styleable.round_image_view_border_outcolor, defaultColor);
 mBorderInsideColor = a.getColor(
 R.styleable.round_image_view_border_incolor, defaultColor);
 a.recycle();
 }


 // 具体解释:比如我自定义一个控件,怎么实现呢,以RoundImageView为例,首先是继承ImageView,然后实现其构造函数,在构造函数中,获取attr中的属性值(再次解释:这里获取的具体的这个属性的值是怎么来的呢?比如颜色和宽度,这个在attr.xml中定义了相关的名字,而在使用RoundImageView的xml布局文件中,我们会设置其值,这里需要用的值,就是从那里设置的),并设置在本控件中,然后继承onDraw方法,画出自己想要的图形或者形状即可。
 /**
  * 这个是继承的父类的onDraw方法
  * 
  * onDraw和下面的方法不用管,基本和学习自定义没关系,就是实现怎么画圆的,你可以改变下面代码试着画三角形头像,哈哈
  */
 @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 bmp
  * @param radius
  *            半径
  * @return
  */
 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);
 }
}

第三步:在xml配置中使用控件:activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

    xmlns:tools="http://schemas.android.com/tools"
 xmlns:loonggg="http://schemas.android.com/apk/res-auto"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:gravity="center_horizontal"
    android:orientation="vertical"
    android:paddingTop="20dp" >


    <!-- 没有指定圆形ImageView属性时,默认没有外边圆颜色 -->


    <net.loonggg.rivd.demo.view.RoundImageView
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:src="@drawable/pigu" />


  <!-- border_outcolor 外部圆圈的颜色 -->
    <!-- border_incolor 内部部圆圈的颜色 -->
    <!-- border_width 外圆和内圆的宽度 -->
    <!-- 再解释一遍,我们在布局中使用了我们在sttr中定义的属性,并在这里的布局文件中赋了值,所以在RoundImageView类中的结构体设置属性使用的值,就是我们在这里赋的,如果不使用attr.xml文件也可以,这样就是我们在activity中使用这个控件的时候,再设置值,不如这样方便罢了,比如昨天我们讲的【自定义带图片和文字的ImageTextButton】那样罢了 -->


  <!-- 说明:这里的loonggg可能大家不太明白,这个名字可以随便起,你们也可以自己随便定义,只要上下统一即可,在布局声明的时候一样就行,比如我在布局顶端是这样声明的 xmlns:loonggg="http://schemas.android.com/apk/res-auto" -->


    <net.loonggg.rivd.demo.view.RoundImageView
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:layout_marginTop="20dp"
        android:src="@drawable/pigu"
 loonggg:border_incolor="#000fff"
 loonggg:border_outcolor="#fff000"
 loonggg:border_width="10dp" />


</LinearLayout>

本文分享自微信公众号 - 非著名程序员(non-famous-coder)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2015-10-13

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏向治洪

仿今日头条滑动评论效果

开发中碰到问题之后实现的,觉得可能有的开发者用的到或则希望独立成一个小功能DEMO,所以就放出来这么一个DEMO。 原本觉得是最后完成后发网站客户端的,可是这样...

28850
来自专栏向治洪

Android-Universal-Image-Loader图片异步加载并缓存

 这个图片异步加载并缓存的类已经被很多开发者所使用,是最常用的几个开源库之一,主流的应用,随便反编译几个火的项目,都可以见到它的身影。        可是有的人...

34260
来自专栏向治洪

android开关按钮

刚开始接触开关样式的按钮是在IOS系统上面,它的切换以及滑动十分帅气,深入人心。 所谓的开关按钮,就是只有2个状态:on和off,下图就是系统IOS 7上开关按...

36880
来自专栏微信终端开发团队的专栏

Android微信智能心跳方案

前言: 在13年11月中旬时,因为基础组件组人手紧张,Leo安排我和春哥去广州轮岗支援。刚到广州的时候,Ray让我和春哥对Line和WhatsApp的心跳机制进...

1.5K100
来自专栏QQ会员技术团队的专栏

Android新一代多渠道打包神器

关于作者: 李涛,腾讯Android工程师,14年加入腾讯SNG增值产品部,期间主要负责手Q动漫、企鹅电竞等项目的功能开发和技术优化。业务时间喜欢折腾新技术,写...

34790
来自专栏腾讯大数据的专栏

Android系统6.0举步维艰,占有率不到1%

移动互联网数据报告 本报告基于腾讯云分析(MTA)的用户数据,针对移动操作系统版本分布、各类机型的占有率排名、移动设备屏幕分辨率等分布进行了统计分析。 “ i...

30580
来自专栏微信终端开发团队的专栏

XCode基本使用及调试技巧

对于初学iOS开发的同学,了解了Objective-C的基本使用后,最关心的应该是如何把OC程序运行起来。由于Xcode的基本使用比较简单,所以本文着重介绍一些...

81670
来自专栏向治洪

可拖拽gridview

在Android开发中,我们常常用到ListView和GridView,而有的时候系统的ListView,GridView并不能满足我们的需求,所以我们需要自己...

36050
来自专栏向治洪

仿今日头条顶部导航效果

 之前发现很多人在群里面、论坛上求网易新闻客户端的源码,之后我就去下了个网易新闻客户端和今日头条新闻客户端,发现他们的大体是一样的,于是在最近的空闲时间,便去琢...

1.2K80
来自专栏微信终端开发团队的专栏

Android M doze特性预研

Android M doze特性预研 2015年5月29日GoogleI/O大会发布新一代Android系统 - Android M preview 版本(AP...

34790

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励