圆角或圆形图片在Android是很常用的. 在CoreLibs中, 圆角相关均位于views包下的roundedimageview包中. 以下是包结构:
| roundedimageview
-RoundedDrawable 圆角Drawable, RoundedImageView与RoundedTransformationBuilder
内部均是使用RoundedDrawable来做圆角 或者圆形效果
-RoundedImageView 圆角ImageView, 任意图片在此控件中均可显示成圆角或圆形
-RoundedTransformationBuilder 圆角TransformationBuilder, 可配合Picasso使用产生圆角或圆形效果.
RoundedDrawable
首先来看看RoundedDrawable. 实现圆角最常见的就是利用Xfermode或Shader. RoundedDrawable就是使用的BitmapShader. 具体BitmapShader的原理或者用法可以自行谷歌.
RoundedDrawable提供了两个方法将Drawable/Bitmap转换成RoundedDrawable:
public static Drawable fromDrawable(Drawable drawable);
public static RoundedDrawable fromBitmap(Bitmap bitmap);
fromDrawable方法内部最终会将Drawable中的Bitmap取出赋值给RoundedDrawable并返回. RoundedDrawable还提供了一系列方法用以描述圆角信息, 如上下左右各个角的圆角角度, 或者是否是圆形等:
public RoundedDrawable setCornerRadius(Corner corner, float radius);
public RoundedDrawable setCornerRadius(float topLeft, float topRight, float bottomRight, float bottomLeft);
public RoundedDrawable setOval(boolean oval);
...
最后在draw方法中, 则会根据这些信息进行绘制, 调用canvas.drawRoundRect或canvas.drawOval得到圆角或圆形图片. 当然RoundedDrawable内部比这里说的复杂, 其内部会对不同的scaleType做适配, 不同的Drawable做适配, 以及其他兼容性问题. 也提供了一些设置Alpha, Shader的TileMode等方法.
RoundedImageView
我们可以使用RoundedImageView来显示圆角或圆形图片. 用法很简单:
XML:
<com.corelibs.views.roundedimageview.RoundedImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:riv_oval="true" // 设置是否是圆形, 如果为true, 则设置的圆角效果会无效
app:riv_corner_radius="5dp" // 四个角统一为5dp, 也可单独这是每个角
app:riv_corner_radius_top_left="5dp" // 左上5dp
app:riv_corner_radius_bottom_left="5dp" // 左下5dp
app:riv_corner_radius_bottom_right="5dp" // 右下5dp
app:riv_corner_radius_top_right="5dp" // 右上5dp
/>
代码:
RoundedImageView imageView;
imageView.setOval(true);
imageView.setCornerRadius(50);
imageView.setCornerRadius(50, 50, 50, 50);
用法很简单. 下面看看内部大概的原理. 其内部覆写了一系列的setImageXXX方法, 这些方法中均利用RoundedDrawable的fromBitmap或fromDrawable方法将其转换为RoundedDrawable, 并将RoundedDrawable设置到ImageView中. 通过RoundedImageView的外部方法收集圆角信息, 并将这些信息通过updateAttrs方法传递给RoundedDrawable.
@Override
public void setImageDrawable(Drawable drawable) {
mResource = 0;
mDrawable = RoundedDrawable.fromDrawable(drawable);
updateDrawableAttrs();
super.setImageDrawable(mDrawable);
}
private void updateAttrs(Drawable drawable) {
if (drawable == null) { return; }
if (drawable instanceof RoundedDrawable) {
((RoundedDrawable) drawable)
.setScaleType(mScaleType)
.setBorderWidth(mBorderWidth)
.setBorderColor(mBorderColor)
.setOval(mIsOval)
.setTileModeX(mTileModeX)
.setTileModeY(mTileModeY);
if (mCornerRadii != null) {
((RoundedDrawable) drawable)
.setCornerRadius(
mCornerRadii[0], mCornerRadii[1], mCornerRadii[2], mCornerRadii[3]);
}
applyColorMod();
} else if (drawable instanceof LayerDrawable) {
// loop through layers to and set drawable attrs
LayerDrawable ld = ((LayerDrawable) drawable);
for (int i = 0, layers = ld.getNumberOfLayers(); i < layers; i++) {
updateAttrs(ld.getDrawable(i));
}
}
}
每次ImageView进行绘制的时候, 均会调用RoundedDrawable的draw方法, 这样就实现了圆角/圆形图片的功能.
如果觉得使用自定义控件太麻烦, 则可以使用RoundedTransformationBuilder配合Picasso实现圆角. 使用系统默认ImageView即可. 首先看看用法:
圆形:
int size = (int) getResources().getDimension(R.dimen.avatar_height);
Picasso.with(context).load(user.icon).resize(size, size).centerCrop()
.transform(new RoundedTransformationBuilder().oval(true)
.build()).into(ivIcon);
圆角:
Picasso.with(context).load(user.icon).resize(size, size).centerCrop()
.transform(new RoundedTransformationBuilder().cornerRadius(50)
.build()).into(ivIcon);
Picasso.with(context).load(user.icon).resize(size, size).centerCrop()
.transform(new RoundedTransformationBuilder()
.cornerRadiusTopLeft(50)
.cornerRadiusBottomLeft(50)
.cornerRadiusTopRight(50)
.cornerRadiusBottomRight(50)
.build()).into(ivIcon);
RoundedTransformationBuilder内部也是使用了RoundedDrawable.fromBitmap(source)方法, 将源Bitmap转化为目标Bitmap. 具体原理是十分简单, 这里就不做过多解释.