圆角

圆角或圆形图片在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

如果觉得使用自定义控件太麻烦, 则可以使用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. 具体原理是十分简单, 这里就不做过多解释.

Last updated