«

Android 圆形/圆角图片的方法

时间:2024-3-2 18:59     作者:韩俊     分类: Android


Android 圆形/圆角图片的方法

目前网上有很多圆角图片的实例,Github上也有一些成熟的项目。之前做项目,为了稳定高效都是选用Github上的项目直接用。但这种结束也是Android开发必备技能 ,所以今天就来简单研究一下该技术,分享给大家。

预备知识:

Xfermode介绍:

下面是Android ApiDemo里的“Xfermodes”实例,效果图。


Xfermode有三个子类,结构如下:

view sourceprint?

1.public
class
2.Xfermode
3.extends
Object
4.java.lang.Object
5.? android.graphics.Xfermode
6.Known Direct Subclasses
7.AvoidXfermode, PixelXorXfermode, PorterDuffXfermode


AvoidXfermode 指定了一个颜色和容差,强制Paint避免在它上面绘图(或者只在它上面绘图)。

PixelXorXfermode 当覆盖已有的颜色时,应用一个简单的像素异或操作。

PorterDuffXfermode 这是一个非常强大的转换模式,使用它,可以使用图像合成的16条Porter-Duff规则的任意一条来控制Paint如何与已有的Canvas图像进行交互。


上面图片种显示的16种模式介绍如下:

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

取两图层全部区域,交集部分变为透明色


了解了上面的知识点后,我们根据上面的知识点先来实现第一种圆角图片制作方式:

原图:


先看这一段代码

view sourceprint?

01.private
ImageView mImg;

  1. 03.@Override
    04.protected
    void onCreate(Bundle savedInstanceState) {
    05.super.onCreate(savedInstanceState);
    06.setContentView(R.layout.activity_main);
    07.mImg = (ImageView) findViewById(R.id.img);

  2. 09.//获得imageview中设置的图片
    10.BitmapDrawable drawable = (BitmapDrawable) mImg.getDrawable();
    11.Bitmap bmp = drawable.getBitmap();
    12.//获得图片的宽,并创建结果bitmap
    13.int
    width = bmp.getWidth();
    14.Bitmap resultBmp = Bitmap.createBitmap(width, width,
    15.Bitmap.Config.ARGB_8888);
    16.Paint paint =
    new Paint();
    17.Canvas canvas =
    new Canvas(resultBmp);
    18.//画圆
    19.canvas.drawCircle(width /
    2, width / 2, width /
    2, paint);
    20.paint.setXfermode(new
    PorterDuffXfermode(PorterDuff.Mode.SRC_IN));// 选择交集去上层图片
    21.canvas.drawBitmap(bmp,
    0, 0, paint);
    22.mImg.setImageBitmap(resultBmp);
    23.bmp.recycle();

  3. 25.}



通过运行上面的代码,我们得出的结果如下:


大家看到这是我们需要的结果。可是这样做可能导致OutOfMomery异常。假如图片很大或者你可能并非通过ImageView的getDrawable获得图像,而是直接Decode一张很大的图片加载到内存,你会发现可能会出现异常。我们做一下改变。

view sourceprint?

01.private
static final
String TAG = "RoundImage";
02.private
ImageView mImg;

  1. 04.@Override
    05.protected
    void onCreate(Bundle savedInstanceState) {
    06.super.onCreate(savedInstanceState);
    07.setContentView(R.layout.activity_main);
    08.mImg = (ImageView) findViewById(R.id.img);
    09.// 裁剪图片
    10.BitmapFactory.Options options =
    new BitmapFactory.Options();
    11.options.inJustDecodeBounds =
    true;
    12.BitmapFactory
    13..decodeResource(getResources(), R.drawable.avatar, options);
    14.Log.d(TAG,
    "original outwidth: " + options.outWidth);
    15.// 此宽度是目标ImageView希望的大小,你可以自定义ImageView,然后获得ImageView的宽度。
    16.int
    dstWidth = 150;
    17.// 我们需要加载的图片可能很大,我们先对原有的图片进行裁剪
    18.int
    sampleSize = calculateInSampleSize(options, dstWidth, dstWidth);
    19.options.inSampleSize = sampleSize;
    20.options.inJustDecodeBounds =
    false;
    21.Log.d(TAG,
    "sample size: " + sampleSize);
    22.Bitmap bmp = BitmapFactory.decodeResource(getResources(),
    23.R.drawable.avatar, options);

  2. 25.// 绘制图片
    26.Bitmap resultBmp = Bitmap.createBitmap(dstWidth, dstWidth,
    27.Bitmap.Config.ARGB_8888);
    28.Paint paint =
    new Paint();
    29.paint.setAntiAlias(true);
    30.Canvas canvas =
    new Canvas(resultBmp);
    31.// 画圆
    32.canvas.drawCircle(dstWidth /
    2, dstWidth / 2, dstWidth /
    2, paint);
    33.// 选择交集去上层图片
    34.paint.setXfermode(new
    PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
    35.canvas.drawBitmap(bmp,
    new Rect(0,
    0, bmp.getWidth(), bmp.getWidth()),
    36.new
    Rect(0,
    0, dstWidth, dstWidth), paint);
    37.mImg.setImageBitmap(resultBmp);
    38.bmp.recycle();
    39.}

  3. 41.private
    int calculateInSampleSize(BitmapFactory.Options options,
    42.int
    reqWidth, int
    reqHeight) {
    43.// Raw height and width of image
    44.final
    int height = options.outHeight;
    45.final
    int width = options.outWidth;
    46.int
    inSampleSize = 1;

  4. 48.if
    (height > reqHeight || width > reqWidth) {

  5. 50.final
    int halfHeight = height /
    2;
    51.final
    int halfWidth = width / 2;

  6. 53.// Calculate the largest inSampleSize value that is a power of 2 and
    54.// keeps both
    55.// height and width larger than the requested height and width.
    56.while
    ((halfHeight / inSampleSize) > reqHeight
    57.&& (halfWidth / inSampleSize) > reqWidth) {
    58.inSampleSize *=
    2;
    59.}
    60.}
    61.return
    inSampleSize;
    62.}

再来看一下效果:


上面提供了一种方式,更多细节,需要你自己去优化,下面介绍第二种绘制圆角图片的方式。

首先我们需要了解一个类BitmapShader

引用的介绍如下:

public BitmapShader(Bitmap bitmap,Shader.TileMode tileX,Shader.TileMode tileY)

调用这个方法来产生一个画有一个位图的渲染器(Shader)。

bitmap 在渲染器内使用的位图

tileX The tiling mode for x to draw the bitmap in. 在位图上X方向花砖模式

tileY The tiling mode for y to draw the bitmap in. 在位图上Y方向花砖模式

TileMode:(一共有三种)

CLAMP :如果渲染器超出原始边界范围,会复制范围内边缘染色。

REPEAT :横向和纵向的重复渲染器图片,平铺。

MIRROR :横向和纵向的重复渲染器图片,这个和REPEAT 重复方式不一样,他是以镜像方式平铺。

知道这个原理后,我们贴出对应的代码:

view sourceprint?

01.public
class CircleImageView extends
ImageView {

  1. 03.private
    static final
    String TAG = CircleImageView.class.getSimpleName();
    04.private
    Paint mBitmapPaint = new
    Paint();
    05.private
    int mRadius;

  2. 07.public
    CircleImageView(Context context, AttributeSet attrs, int
    defStyleAttr) {
    08.super(context, attrs, defStyleAttr);
    09.init();
    10.}

  3. 12.public
    CircleImageView(Context context, AttributeSet attrs) {
    13.super(context, attrs);
    14.init();
    15.}

  4. 17.public
    CircleImageView(Context context) {
    18.super(context);
    19.init();
    20.}

  5. 22.private
    void init() {
    23.BitmapDrawable drawable = (BitmapDrawable) getDrawable();
    24.if
    (drawable == null) {
    25.Log.i(TAG,
    "drawable: null");
    26.return;
    27.}
    28.Bitmap bmp = drawable.getBitmap();
    29.BitmapShader shader =
    new BitmapShader(bmp, TileMode.CLAMP,
    30.TileMode.CLAMP);
    31.mBitmapPaint.setShader(shader);
    32.mBitmapPaint.setAntiAlias(true);
    33.invalidate();
    34.}

  6. 36.@Override
    37.protected
    void onDraw(Canvas canvas) {
    38.if
    (getDrawable() == null) {
    39.return;
    40.}
    41.mRadius = Math.min(getWidth()/2, getHeight()/2);
    42.canvas.drawCircle(getWidth() /
    2, getHeight() / 2, mRadius,
    43.mBitmapPaint);
    44.}

  7. 46.}


是不是挺简单的

结果我就不显示了,跟上面的一样。上面也是最原始的代码,文章的结尾贴出一份完整优化过的代码共大家参考如下:

view sourceprint?

001.public
class CircleImageView extends
ImageView {

  1. 003.private
    static final
    ScaleType SCALE_TYPE = ScaleType.CENTER_CROP;

  2. 005.private
    static final
    Bitmap.Config BITMAP_CONFIG = Bitmap.Config.ARGB_8888;
    006.private
    static final
    int COLORDRAWABLE_DIMENSION = 1;

  3. 008.private
    static final
    int DEFAULT_BORDER_WIDTH = 0;
    009.private
    static final
    int DEFAULT_BORDER_COLOR = Color.BLACK;

  4. 011.private
    final RectF mDrawableRect =
    new RectF();
    012.private
    final RectF mBorderRect =
    new RectF();

  5. 014.private
    final Matrix mShaderMatrix =
    new Matrix();
    015.private
    final Paint mBitmapPaint =
    new Paint();
    016.private
    final Paint mBorderPaint =
    new Paint();

  6. 018.private
    int mBorderColor = DEFAULT_BORDER_COLOR;
    019.private
    int mBorderWidth = DEFAULT_BORDER_WIDTH;

  7. 021.private
    Bitmap mBitmap;
    022.private
    BitmapShader mBitmapShader;
    023.private
    int mBitmapWidth;
    024.private
    int mBitmapHeight;

  8. 026.private
    float mDrawableRadius;
    027.private
    float mBorderRadius;

  9. 029.private
    boolean mReady;
    030.private
    boolean mSetupPending;

  10. 032.public
    CircleImageView(Context context) {
    033.super(context);

  11. 035.init();
    036.}

  12. 038.public
    CircleImageView(Context context, AttributeSet attrs) {
    039.this(context, attrs,
    0);
    040.}

  13. 042.public
    CircleImageView(Context context, AttributeSet attrs, int
    defStyle) {
    043.super(context, attrs, defStyle);

  14. 045.TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CircleImageView, defStyle,
    0);

  15. 047.mBorderWidth = a.getDimensionPixelSize(R.styleable.CircleImageView_border_width, DEFAULT_BORDER_WIDTH);
    048.mBorderColor = a.getColor(R.styleable.CircleImageView_border_color, DEFAULT_BORDER_COLOR);

  16. 050.a.recycle();

  17. 052.init();
    053.}

  18. 055.private
    void init() {
    056.super.setScaleType(SCALE_TYPE);
    057.mReady =
    true;

  19. 059.if
    (mSetupPending) {
    060.setup();
    061.mSetupPending =
    false;
    062.}
    063.}

  20. 065.@Override
    066.public
    ScaleType getScaleType() {
    067.return
    SCALE_TYPE;
    068.}

  21. 070.@Override
    071.public
    void setScaleType(ScaleType scaleType) {
    072.if
    (scaleType != SCALE_TYPE) {
    073.throw
    new IllegalArgumentException(String.format("ScaleType %s not supported.", scaleType));
    074.}
    075.}

  22. 077.@Override
    078.protected
    void onDraw(Canvas canvas) {
    079.if
    (getDrawable() == null) {
    080.return;
    081.}

  23. 083.canvas.drawCircle(getWidth() /
    2, getHeight() / 2, mDrawableRadius, mBitmapPaint);
    084.if
    (mBorderWidth != 0) {
    085.canvas.drawCircle(getWidth() /
    2, getHeight() / 2, mBorderRadius, mBorderPaint);
    086.}
    087.}

  24. 089.@Override
    090.protected
    void onSizeChanged(int
    w, int
    h, int oldw, int
    oldh) {
    091.super.onSizeChanged(w, h, oldw, oldh);
    092.setup();
    093.}

  25. 095.public
    int getBorderColor() {
    096.return
    mBorderColor;
    097.}

  26. 099.public
    void setBorderColor(int
    borderColor) {
    100.if
    (borderColor == mBorderColor) {
    101.return;
    102.}

  27. 104.mBorderColor = borderColor;
    105.mBorderPaint.setColor(mBorderColor);
    106.invalidate();
    107.}

  28. 109.public
    int getBorderWidth() {
    110.return
    mBorderWidth;
    111.}

  29. 113.public
    void setBorderWidth(int
    borderWidth) {
    114.if
    (borderWidth == mBorderWidth) {
    115.return;
    116.}

  30. 118.mBorderWidth = borderWidth;
    119.setup();
    120.}

  31. 122.@Override
    123.public
    void setImageBitmap(Bitmap bm) {
    124.super.setImageBitmap(bm);
    125.mBitmap = bm;
    126.setup();
    127.}

  32. 129.@Override
    130.public
    void setImageDrawable(Drawable drawable) {
    131.super.setImageDrawable(drawable);
    132.mBitmap = getBitmapFromDrawable(drawable);
    133.setup();
    134.}

  33. 136.@Override
    137.public
    void setImageResource(int
    resId) {
    138.super.setImageResource(resId);
    139.mBitmap = getBitmapFromDrawable(getDrawable());
    140.setup();
    141.}

  34. 143.@Override
    144.public
    void setImageURI(Uri uri) {
    145.super.setImageURI(uri);
    146.mBitmap = getBitmapFromDrawable(getDrawable());
    147.setup();
    148.}

  35. 150.private
    Bitmap getBitmapFromDrawable(Drawable drawable) {
    151.if
    (drawable == null) {
    152.return
    null;
    153.}

  36. 155.if
    (drawable instanceof
    BitmapDrawable) {
    156.return
    ((BitmapDrawable) drawable).getBitmap();
    157.}

  37. 159.try
    {
    160.Bitmap bitmap;

  38. 162.if
    (drawable instanceof
    ColorDrawable) {
    163.bitmap = Bitmap.createBitmap(COLORDRAWABLE_DIMENSION, COLORDRAWABLE_DIMENSION, BITMAP_CONFIG);
    164.}
    else {
    165.bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), BITMAP_CONFIG);
    166.}

  39. 168.Canvas canvas =
    new Canvas(bitmap);
    169.drawable.setBounds(0,
    0, canvas.getWidth(), canvas.getHeight());
    170.drawable.draw(canvas);
    171.return
    bitmap;
    172.}
    catch (OutOfMemoryError e) {
    173.return
    null;
    174.}
    175.}

  40. 177.private
    void setup() {
    178.if
    (!mReady) {
    179.mSetupPending =
    true;
    180.return;
    181.}

  41. 183.if
    (mBitmap == null) {
    184.return;
    185.}

  42. 187.mBitmapShader =
    new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);

  43. 189.mBitmapPaint.setAntiAlias(true);
    190.mBitmapPaint.setShader(mBitmapShader);

  44. 192.mBorderPaint.setStyle(Paint.Style.STROKE);
    193.mBorderPaint.setAntiAlias(true);
    194.mBorderPaint.setColor(mBorderColor);
    195.mBorderPaint.setStrokeWidth(mBorderWidth);

  45. 197.mBitmapHeight = mBitmap.getHeight();
    198.mBitmapWidth = mBitmap.getWidth();

  46. 200.mBorderRect.set(0,
    0, getWidth(), getHeight());
    201.mBorderRadius = Math.min((mBorderRect.height() - mBorderWidth) /
    2, (mBorderRect.width() - mBorderWidth) /
    2);

  47. 203.mDrawableRect.set(mBorderWidth, mBorderWidth, mBorderRect.width() - mBorderWidth, mBorderRect.height() - mBorderWidth);
    204.mDrawableRadius = Math.min(mDrawableRect.height() /
    2, mDrawableRect.width() /
    2);

  48. 206.updateShaderMatrix();
    207.invalidate();
    208.}

  49. 210.private
    void updateShaderMatrix() {
    211.float
    scale;
    212.float
    dx = 0;
    213.float
    dy = 0;

  50. 215.mShaderMatrix.set(null);

  51. 217.if
    (mBitmapWidth mDrawableRect.height() > mDrawableRect.width() mBitmapHeight) {
    218.scale = mDrawableRect.height() / (float) mBitmapHeight;
    219.dx = (mDrawableRect.width() - mBitmapWidth scale)
    0.5f;
    220.}
    else {
    221.scale = mDrawableRect.width() / (float) mBitmapWidth;
    222.dy = (mDrawableRect.height() - mBitmapHeight scale)
    0.5f;
    223.}

  52. 225.mShaderMatrix.setScale(scale, scale);
    226.mShaderMatrix.postTranslate((int) (dx +
    0.5f) + mBorderWidth, (int) (dy +
    0.5f) + mBorderWidth);

  53. 228.mBitmapShader.setLocalMatrix(mShaderMatrix);
    229.}

  54. 231.}

标签: android

热门推荐