您的位置:首页 > 移动开发 > Android开发

Android——RoundedImageView应用及源码总结笔记

2017-08-19 15:47 274 查看

介绍

源码参考文章,RoundedImageView是用来对图片进行裁剪,可以在代码块中实现,也可以在布局xml中实现。

github源码地址:https://github.com/vinc3m1/RoundedImageView

作者对这个开源框架的评价就是

There are many ways to create rounded corners in android, but this is the fastest and best one that I know of because it:
does not create a copy of the original bitmap
does not use a clipPath which is not hardware accelerated and not anti-aliased.
does not use setXfermode to clip the bitmap and draw twice to the canvas.
第一是无需对原始的bitmap进行复制,第二是不需硬件上的加速和反锯齿,第三是没有使用用Xfermode,所以需要使用画布处理两次

应用

第一个当然是对库的支持 
compile 'com.makeramen:roundedimageview:2.3.0'

在xml文件中引用:

<com.makeramen.roundedimageview.RoundedImageView
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/imageView1"
android:src="@drawable/photo1"
android:scaleType="fitCenter"
app:riv_corner_radius="30dip"
app:riv_border_width="2dip"
app:riv_border_color="#333333"
app:riv_mutate_background="true"
app:riv_tile_mode="repeat"
app:riv_oval="true" />


在代码中引用:

RoundedImageView riv = new RoundedImageView(context);
riv.setScaleType(ScaleType.CENTER_CROP);
riv.setCornerRadius((float) 10);
riv.setBorderWidth((float) 2);
riv.setBorderColor(Color.DKGRAY);
riv.mutateBackground(true);
riv.setImageDrawable(drawable);
riv.setBackground(backgroundDrawable);
riv.setOval(true);
riv.setTileModeX(Shader.TileMode.REPEAT);
riv.setTileModeY(Shader.TileMode.REPEAT);


可以参考下别人详细的实现过程http://blog.csdn.net/aaawqqq/article/details/38057145

开始

RoundedImageView主要的核心是在RoundedDrawer所以核心是对于RoundedDrawer的分析

一、Drawable转化为圆角的Drawable

1、构造方法
public RoundedDrawable(Bitmap bitmap) {
mBitmap = bitmap;

mBitmapWidth = bitmap.getWidth();
mBitmapHeight = bitmap.getHeight();
mBitmapRect.set(0, 0, mBitmapWidth, mBitmapHeight);

mBitmapPaint = new Paint();
mBitmapPaint.setStyle(Paint.Style.FILL);
mBitmapPaint.setAntiAlias(true);

mBorderPaint = new Paint();
mBorderPaint.setStyle(Paint.Style.STROKE);
mBorderPaint.setAntiAlias(true);
mBorderPaint.setColor(mBorderColor.getColorForState(getState(), DEFAULT_BORDER_COLOR));
mBorderPaint.setStrokeWidth(mBorderWidth);
}


用到了2个Paint,分别是mBitmapPaint和mBorderPaint。一个用于绘制内部具体内容,比如填充什么的,一个用于绘制边框。
这里就能用来解释为什么上面作者说的第三点没使用setXfermode所以需要使用画布画两次,如果使用setXfermode进行绘制就只需要一次。

2、fromDrawable方法

public static Drawable fromDrawable(Drawable drawable) {
if (drawable != null) {
if (drawable instanceof RoundedDrawable) {
// just return if it's already a RoundedDrawable
return drawable;
} else if (drawable instanceof LayerDrawable) {
LayerDrawable ld = (LayerDrawable) drawable;
int num = ld.getNumberOfLayers();

// loop through layers to and change to RoundedDrawables if possible
for (int i = 0; i < num; i++) {
Drawable d = ld.getDrawable(i);
ld.setDrawableByLayerId(ld.getId(i), fromDrawable(d));
}
return ld;
}

// try to get a bitmap from the drawable and
Bitmap bm = drawableToBitmap(drawable);
if (bm != null) {
return new RoundedDrawable(bm);
}
}
return drawable;
}


这个方法中我们可以看到如果drawable本身就是RoundedDrawable,那么直接返回,如果不是就每一个LayoutDrawable的item改造成RoundedDrawable,我们可以看到其中使用fromDrawable的递归调用改造。

后将drawable转成bitmap后直接返回RoundedDrawable

3、生成圆角Draw
@Override
public void draw(@NonNull Canvas canvas) {
if (mRebuildShader) {
BitmapShader bitmapShader = new BitmapShader(mBitmap, mTileModeX, mTileModeY);
if (mTileModeX == Shader.TileMode.CLAMP && mTileModeY == Shader.TileMode.CLAMP) {
bitmapShader.setLocalMatrix(mShaderMatrix);
}
mBitmapPaint.setShader(bitmapShader);
mRebuildShader = false;
}

if (mOval) {
if (mBorderWidth > 0) {
canvas.drawOval(mDrawableRect, mBitmapPaint);
canvas.drawOval(mBorderRect, mBorderPaint);
} else {
canvas.drawOval(mDrawableRect, mBitmapPaint);
}
} else {
if (any(mCornersRounded)) {
float radius = mCornerRadius;
if (mBorderWidth > 0) {
canvas.drawRoundRect(mDrawableRect, radius, radius, mBitmapPaint);
canvas.drawRoundRect(mBorderRect, radius, radius, mBorderPaint);
redrawBitmapForSquareCorners(canvas);
redrawBorderForSquareCorners(canvas);
} else {
canvas.drawRoundRect(mDrawableRect, radius, radius, mBitmapPaint);
redrawBitmapForSquareCorners(canvas);
}
} else {
canvas.drawRect(mDrawableRect, mBitmapPaint);
if (mBorderWidth > 0) {
canvas.drawRect(mBorderRect, mBorderPaint);
}
}
}
}

我们先看看第4行代码,这里用到BitmapShader,这里表明,圆角是通过BitmapShader实现的。接下来我们看到BitmapShader调用了setLocalMatrix方法,最后mBitmapPaint.setShader(bitmapShader);将bitmapShader设置给mBitmapPaint。

我们可以看到里面有一个判断就是判断是否要求剪裁为圆角,如果是那么就就对其四周都剪裁成圆角,如果不是的话就使用
redrawBitmapForSquareCorners(canvas);
方法,将已经是圆角的恢复成直角,有兴趣可以看下源码,他是绘制一个正方形填充圆角的部分。

4、ScaleType
public RoundedDrawable setScaleType(ScaleType scaleType) {
if (scaleType == null) {
scaleType = ScaleType.FIT_CENTER;
}
if (mScaleType != scaleType) {
mScaleType = scaleType;
updateShaderMatrix();
}
return this;
}


这里就是对于ScaleType的支持,相关的操作在updateShaderMatrix()方法里面,就fitCenter等等,有兴趣可以进去看看

二、src属性

1、重写setImageDrawable,setImageBitmap和setImageResource
@Override
public void setImageDrawable(Drawable drawable) {
mResource = 0;
mDrawable = RoundedDrawable.fromDrawable(drawable);
updateDrawableAttrs();
super.setImageDrawable(mDrawable);
}

@Override
public void setImageBitmap(Bitmap bm) {
mResource = 0;
mDrawable = RoundedDrawable.fromBitmap(bm);
updateDrawableAttrs();
super.setImageDrawable(mDrawable);
}

@Override
public void setImageResource(@DrawableRes int resId) {
if (mResource != resId) {
mResource = resId;
mDrawable = resolveResource();
updateDrawableAttrs
bc68
();
super.setImageDrawable(mDrawable);
}
}


这两个方法都是获取Drawable之后进入UpdateDrawableAttrs(),那我们就进去看看里面是什么
private void updateDrawableAttrs() {
updateAttrs(mDrawable);
}
继续进入updateAttrs
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[Corner.TOP_LEFT],
mCornerRadii[Corner.TOP_RIGHT],
mCornerRadii[Corner.BOTTOM_RIGHT],
mCornerRadii[Corner.BOTTOM_LEFT]);
}

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));
}
}
}


这部分代码跟第一点的fromDrawable()方法有点类似,判断如果是RoundedDrawable那我们就设置相关属性,如果不是那就把LayoutDrawable重新提取传去updateAttrs(),到这里的圆角属性就可以实现

三、background属性

@Override
public void setBackground(Drawable background) {
setBackgroundDrawable(background);
}

@Override
@Deprecated
public void setBackgroundDrawable(Drawable background) {
mBackgroundDrawable = background;
updateBackgroundDrawableAttrs(true);
super.setBackgroundDrawable(mBackgroundDrawable);
}


这两个函数都用到了setBackgroundDrawable(),那我们进去看看

private void updateBackgroundDrawableAttrs(boolean convert) {
if (mMutateBackground) {
if (convert) {
mBackgroundDrawable = RoundedDrawable.fromDrawable(mBackgroundDrawable);
}
updateAttrs(mBackgroundDrawable);
}
}


这里面涉及到mMutateBackground属性,如果mMutateBackground为true,并且传入的参数也为true的话,会调用RoundedDrawable的方法把mBackgroundDrawable转化为圆角,最后通过之前讲的updateAttrs(Drawable)方法实现更新mBackgroundDrawable有关RoundedDrawable的属性。

这里就是设置背景是否设置为圆角,可以在xml里面设置也可以在代码里面设置

xml里面设置app:riv_mutate_background="true"或者再代码里面riv.mutateBackground(true)。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: