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