Android Drawable简介&CircleImageView简单实现

1.概述
圆形头像在很多app上都有使用,实现方式也有很多种。今天我们就先了解下Android drawable的知识点,最后再实现CircleImageView。
先看效果:
这里写图片描述

结合效果图:依次是CirclrImageView , 彩虹圈是整个布局的背景 (LayerDrawable结合ScaleDrawable实现) ,按钮(StateListDrawable)
HelloWord文本(背景添加了TrasitionDrawable)。

2.Drawable简介
Drawable的分类有很多,比较常用的有BitmapDrawable ,ShapeDrawable ,LayerDrawable,StateListDrawable等,一般由于当背景或者显示ImageView。
接下来简单介绍下这几种Drawable。

2.1 BitmapDrawable
这个就是一张图片,其中有几个常用的属性需要理解下。
属性
antialias:抗锯齿功能, 开启此功能是为了让图片看起来更加平滑,开启后图片清晰度也会有一定幅度降低(幅度较小可以忽略)。

dither:防抖动效果,此功能是为了适配图片像素与设备像素不一致时,保证图片的显示效果。 比如:我们设置的图片色彩模式为ARGB_8888,低端设备只支持ARGB_4444或者RGB_565模式时,开启此功能能保证图片较好的显示。

filter:过滤效果,此功能实在图片压缩或者拉伸时,开启可以较好的显示。
BitmapDrawable主要就介绍这三个属性,想了解更全面的可以参考官方api。

2.2 ShapeDrawable
ShapeDrawable 是通过color来构造的图行,通常用于xml中自定义图形图片。
属性
shape: 图形形状,有四种:rectangle oval line ring。这个很好理解。
corners: 角度 ,这个只能用于图形为rectangle的shape,就是设置矩形四个角的角度的。
gradient: 渐变效果,这个属性比较多
angle 渐变角度 0–90范围 0:左—>右 90:下—>上
centerX 渐变中心点x坐标
centerY 渐变中心点y坐标
startColor,centerColor,endCorlor,渐变起始色 中间色 结束色
gradientRadius 渐变半径 需要配合 type(渐变类别) 为radial一起使用
type 渐变类别,右三种 linear(线性) radial(径向) sweep(扫描)
具体效果可以自行体验下。
solid: 填充色 color属性指定色值
stroke: 描边 ,画shap的边框
width 边框宽度
color 边框线条颜色
dashWidth 虚线宽度
dashGap 虚线间距
size: shape大小 ,当shapDrawable作为背景时,这个大小会被拉伸或缩小为view的大小。如:上图,Hello World文本的背景

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="oval"
    >
 <solid
     android:color="#FFFFFF"/>

  <stroke
      android:width="5dp"
      android:color="#00FF00"
      android:dashWidth="15dp"
      android:dashGap="6dp"
      />
</shape>

2.3 LayerDrawable
层次化的drawable集合,通过组合不同的drawable达到一种叠加效果
在xml中通过layer-list标签使用,可以放多个item,每个item里面可以放shape或者其他drawable资源。 注意这一系列的item都会被拉伸成或压缩成view的大小,所以使用时根据想要的效果来设置gravity top left right bottom属性。如:上图 ,彩虹圈的实现代码

<?xml version="1.0" encoding="utf-8"?>
<layer-list
    xmlns:android="http://schemas.android.com/apk/res/android"
    >

  <item
      android:left="120dp"
      android:right="120dp"
      android:top="120dp"
      android:bottom="120dp"
      android:drawable="@drawable/ring1">
  </item>

  <item
      android:left="100dp"
      android:right="100dp"
      android:top="100dp"
      android:bottom="100dp"
      android:drawable="@drawable/ring2">
  </item>
  <item
      android:left="80dp"
      android:right="80dp"
      android:top="80dp"
      android:bottom="80dp"
      android:drawable="@drawable/ring3">
  </item>
  <item
      android:left="60dp"
      android:right="60dp"
      android:top="60dp"
      android:bottom="60dp"
      android:drawable="@drawable/ring4">
  </item>
  <item
      android:left="40dp"
      android:right="40dp"
      android:top="40dp"
      android:bottom="40dp"
      android:drawable="@drawable/ring5">
  </item>
  <item
      android:left="20dp"
      android:right="20dp"
      android:top="20dp"
      android:bottom="20dp"
      android:drawable="@drawable/ring6">
  </item>
  <item
      android:drawable="@drawable/ring7">
  </item>
</layer-list>

2.4 StateListDrawable
这个也是drawable的集合,每个drawable 表示view的一种状态。
在xml中通过selector标签使用,通常给按钮设置点击效果时使用。
如上图的 Hello Button

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
    android:state_pressed="true"
   >
  <shape
      android:shape="rectangle"
      >
    <corners
        android:radius="10dp"
        />
    <solid
        android:color="#F09"
        />
  </shape>
</item>

  <item>
    <shape
        android:shape="rectangle"
        >
      <corners
          android:radius="10dp"
          />
      <solid
          android:color="#F129"
          />
    </shape>
  </item>

</selector>

2.5 TransitionDrawable
这个也是drawable集合,用于实现view的淡入淡出的效果。没特殊属性。
如上如点击按钮 hello world文本会startTransition()

<?xml version="1.0" encoding="utf-8"?>
<transition xmlns:android="http://schemas.android.com/apk/res/android">

  <item android:drawable="@drawable/back"/>
  <item android:drawable="@drawable/back01"/>
  <item android:drawable="@drawable/back02"/>
</transition>

三个back drawable就是色值不一致的虚线圆圈(如:back)

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="oval"
    >
 <solid
     android:color="#FFFFFF"/>

  <stroke
      android:width="5dp"
      android:color="#00FF00"
      android:dashWidth="15dp"
      android:dashGap="6dp"
      />
</shape>

代码中开启

TransitionDrawable drawable = (TransitionDrawable) tv.getBackground();
drawable.startTransition(1500); //参数:完成整个效果的时间

2.6 ScaleDrawable
可缩放的drawable
属性
scaleGravity: 相当于shape的gravity
scaleWidth: 宽度缩放比例 (百分制)
scaleHeight: 高度缩放比例 (百分制)

缩放等级 0–10000, 0表示完全缩放 也就是不可见了, 10000表示不缩放
如上图,对彩虹圈进行缩放 比例原图* 90%

<?xml version="1.0" encoding="utf-8"?>
<scale
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/layer"
    android:scaleHeight="10%"
    android:scaleWidth="10%"
    android:scaleGravity="center">
</scale>
代码中使用
ScaleDrawable scaleDrawable = (ScaleDrawable) layout.getBackground();
scaleDrawable.setLevel(1);

xml中使用
android:background="@drawable/scale"

最终效果其实是近似60%,准确值应该是90% * 99.99%。

其他还有很多drawable类型,由于不常使用,就不介绍了。

掌握了以上系统提供的drawable,已经可以满足日常基本需求了,但是想要个性化,还得自定义。接下来快速实现简单的CircleImageVie.

CircleImageVIew 实现:
思路
1.自定义view 集成Imageview,重写ondraw方法
2.根据大小缩放图片, 依次画圆形和图片(重点:设置画笔属性 使图片结合起来)
关键代码:
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
当然这个不是写死的,如果先画圆形 再画图形就用SRC_IN ,意思就是取2层的交集部分,并且显示上层,也就是后画的。 反之就用DST_IN, 意思是取2层交集,显示下层。实现代码如下

public class CirclePic extends ImageView {

  private int defaultRadiu = 100;//默认圆形图片

  public CirclePic(Context context) {
    this(context, null);
  }

  public CirclePic(Context context, AttributeSet attrs) {
    this(context, attrs, 0);
  }

  public CirclePic(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    init(attrs);
  }

  private void init(AttributeSet attrs) {
    TypedArray a = getResources().obtainAttributes(attrs,R.styleable.CirclePic);
    defaultRadiu = (int) a.getDimension(R.styleable.CirclePic_radiu,defaultRadiu);
    a.recycle();
  }

  @Override protected void onDraw(Canvas canvas) {
    Drawable drawable = getDrawable();
    if (drawable != null) {
      if (drawable instanceof BitmapDrawable && drawable.getIntrinsicWidth() > 0
          && drawable.getIntrinsicHeight() > 0) {
        Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
        Bitmap circleBitmap = getCircleBitamp(bitmap);

        canvas.drawBitmap(circleBitmap, 0, 0, null);
      }
    } else {
      super.onDraw(canvas);
    }
  }

  /**
   * 生成圆形bitmap
   */
  private Bitmap getCircleBitamp(Bitmap bitmap) {

    Bitmap circleBitmap = null;
    if (bitmap.getWidth() != defaultRadiu || bitmap.getHeight() != defaultRadiu) {
      circleBitmap = Bitmap.createScaledBitmap(bitmap, defaultRadiu, defaultRadiu, false);
    } else {
      circleBitmap = bitmap;
    }

    Bitmap output = Bitmap.createBitmap(circleBitmap.getWidth(), circleBitmap.getHeight(),
        Bitmap.Config.ARGB_8888);

    Rect rect = new Rect(0, 0, circleBitmap.getWidth(), circleBitmap.getHeight());

    Canvas canvas = new Canvas(output);

    Paint paint = new Paint();
    paint.setAntiAlias(true);
    paint.setDither(true);

    canvas.drawCircle(circleBitmap.getWidth() / 2, circleBitmap.getHeight() / 2,
        circleBitmap.getWidth() / 2, paint);
    //取2层交集部分,只显示上层
    paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));

    canvas.drawBitmap(circleBitmap, rect, rect, paint);

    return output;
  }
}

利用这个参数大家可以发散脑洞组合出各种图形,下面给出PorterDuffXfermode所有参数说明:
1.PorterDuff.Mode.CLEAR :所绘制不会提交到画布上。
2.PorterDuff.Mode.SRC :显示上层绘制图片
3.PorterDuff.Mode.DST :显示下层绘制图片
4.PorterDuff.Mode.SRC_OVER :正常绘制显示,上下层绘制叠盖。
5.PorterDuff.Mode.DST_OVER :上下层都显示。下层居上显示。
6.PorterDuff.Mode.SRC_IN :取两层绘制交集。显示上层。
7.PorterDuff.Mode.DST_IN :取两层绘制交集。显示下层。
8.PorterDuff.Mode.SRC_OUT :取上层绘制非交集部分。
9.PorterDuff.Mode.DST_OUT :取下层绘制非交集部分。
10.PorterDuff.Mode.SRC_ATOP :取下层非交集部分与上层交集部分
11.PorterDuff.Mode.DST_ATOP :取上层非交集部分与下层交集部分
12.PorterDuff.Mode.XOR :去除两图层交集部分
13.PorterDuff.Mode.DARKEN :取两图层全部区域,交集部分颜色加深
14.PorterDuff.Mode.LIGHTEN :取两图层全部,点亮交集部分颜色
15.PorterDuff.Mode.MULTIPLY :取两图层交集部分叠加后颜色
16.PorterDuff.Mode.SCREEN :取两图层全部区域,交集部分变为透明色

最后看下对比效果图:
这里写图片描述
这里写图片描述

好了,今天就到这。下班开撸,大家有什么想法或者觉得可以改进的地方可以留言,互相学习。
源码地址:
https://github.com/ChenHaoLw/CircleImageView

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值