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

介绍

源码参考文章,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();  
     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)。





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值