zoukankan      html  css  js  c++  java
  • Android 圆形/圆角图片的方法

    Android 圆形/圆角图片的方法

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

    预备知识:

    Xfermode介绍:

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


    Xfermode有三个子类。结构例如以下:

    1.publicclass
    2.Xfermode
    3.extendsObject
    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

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


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

    原图:


    先看这一段代码

    01.privateImageView mImg;
    02. 
    03.@Override
    04.protectedvoid onCreate(Bundle savedInstanceState) {
    05.super.onCreate(savedInstanceState);
    06.setContentView(R.layout.activity_main);
    07.mImg = (ImageView) findViewById(R.id.img);
    08. 
    09.//获得imageview中设置的图片
    10.BitmapDrawable drawable = (BitmapDrawable) mImg.getDrawable();
    11.Bitmap bmp = drawable.getBitmap();
    12.//获得图片的宽,并创建结果bitmap
    13.intwidth = 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(newPorterDuffXfermode(PorterDuff.Mode.SRC_IN));// 选择交集去上层图片
    21.canvas.drawBitmap(bmp,0, 0, paint);
    22.mImg.setImageBitmap(resultBmp);
    23.bmp.recycle();
    24. 
    25.}

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


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

    01.privatestatic final String TAG = "RoundImage";
    02.privateImageView mImg;
    03. 
    04.@Override
    05.protectedvoid 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 out " + options.outWidth);
    15.// 此宽度是目标ImageView希望的大小,你能够自己定义ImageView。然后获得ImageView的宽度。

    16.intdstWidth = 150;
    17.// 我们须要载入的图片可能非常大,我们先对原有的图片进行裁剪
    18.intsampleSize = 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);
    24. 
    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(newPorterDuffXfermode(PorterDuff.Mode.SRC_IN));
    35.canvas.drawBitmap(bmp,new Rect(0,0, bmp.getWidth(), bmp.getWidth()),
    36.newRect(0,0, dstWidth, dstWidth), paint);
    37.mImg.setImageBitmap(resultBmp);
    38.bmp.recycle();
    39.}
    40. 
    41.privateint calculateInSampleSize(BitmapFactory.Options options,
    42.intreqWidth, int reqHeight) {
    43.// Raw height and width of image
    44.finalint height = options.outHeight;
    45.finalint width = options.outWidth;
    46.intinSampleSize = 1;
    47. 
    48.if(height > reqHeight || width > reqWidth) {
    49. 
    50.finalint halfHeight = height / 2;
    51.finalint halfWidth = width / 2;
    52. 
    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.returninSampleSize;
    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 反复方式不一样,他是以镜像方式平铺。

    知道这个原理后,我们贴出相应的代码:
    01.publicclass CircleImageView extendsImageView {
    02. 
    03.privatestatic final String TAG = CircleImageView.class.getSimpleName();
    04.privatePaint mBitmapPaint = new Paint();
    05.privateint mRadius;
    06. 
    07.publicCircleImageView(Context context, AttributeSet attrs, intdefStyleAttr) {
    08.super(context, attrs, defStyleAttr);
    09.init();
    10.}
    11. 
    12.publicCircleImageView(Context context, AttributeSet attrs) {
    13.super(context, attrs);
    14.init();
    15.}
    16. 
    17.publicCircleImageView(Context context) {
    18.super(context);
    19.init();
    20.}
    21. 
    22.privatevoid 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.}
    35. 
    36.@Override
    37.protectedvoid 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.}
    45. 
    46.}

    是不是挺简单的

    结果我就不显示了,跟上面的一样。

    上面也是最原始的代码,文章的结尾贴出一份完整优化过的代码共大家參考例如以下:

    001.publicclass CircleImageView extendsImageView {
    002. 
    003.privatestatic final ScaleType SCALE_TYPE = ScaleType.CENTER_CROP;
    004. 
    005.privatestatic final Bitmap.Config BITMAP_CONFIG = Bitmap.Config.ARGB_8888;
    006.privatestatic final int COLORDRAWABLE_DIMENSION = 1;
    007. 
    008.privatestatic final int DEFAULT_BORDER_WIDTH = 0;
    009.privatestatic final int DEFAULT_BORDER_COLOR = Color.BLACK;
    010. 
    011.privatefinal RectF mDrawableRect = new RectF();
    012.privatefinal RectF mBorderRect = new RectF();
    013. 
    014.privatefinal Matrix mShaderMatrix = new Matrix();
    015.privatefinal Paint mBitmapPaint = new Paint();
    016.privatefinal Paint mBorderPaint = new Paint();
    017. 
    018.privateint mBorderColor = DEFAULT_BORDER_COLOR;
    019.privateint mBorderWidth = DEFAULT_BORDER_WIDTH;
    020. 
    021.privateBitmap mBitmap;
    022.privateBitmapShader mBitmapShader;
    023.privateint mBitmapWidth;
    024.privateint mBitmapHeight;
    025. 
    026.privatefloat mDrawableRadius;
    027.privatefloat mBorderRadius;
    028. 
    029.privateboolean mReady;
    030.privateboolean mSetupPending;
    031. 
    032.publicCircleImageView(Context context) {
    033.super(context);
    034. 
    035.init();
    036.}
    037. 
    038.publicCircleImageView(Context context, AttributeSet attrs) {
    039.this(context, attrs,0);
    040.}
    041. 
    042.publicCircleImageView(Context context, AttributeSet attrs, intdefStyle) {
    043.super(context, attrs, defStyle);
    044. 
    045.TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CircleImageView, defStyle,0);
    046. 
    047.mBorderWidth = a.getDimensionPixelSize(R.styleable.CircleImageView_border_width, DEFAULT_BORDER_WIDTH);
    048.mBorderColor = a.getColor(R.styleable.CircleImageView_border_color, DEFAULT_BORDER_COLOR);
    049. 
    050.a.recycle();
    051. 
    052.init();
    053.}
    054. 
    055.privatevoid init() {
    056.super.setScaleType(SCALE_TYPE);
    057.mReady =true;
    058. 
    059.if(mSetupPending) {
    060.setup();
    061.mSetupPending =false;
    062.}
    063.}
    064. 
    065.@Override
    066.publicScaleType getScaleType() {
    067.returnSCALE_TYPE;
    068.}
    069. 
    070.@Override
    071.publicvoid setScaleType(ScaleType scaleType) {
    072.if(scaleType != SCALE_TYPE) {
    073.thrownew IllegalArgumentException(String.format("ScaleType %s not supported.", scaleType));
    074.}
    075.}
    076. 
    077.@Override
    078.protectedvoid onDraw(Canvas canvas) {
    079.if(getDrawable() == null) {
    080.return;
    081.}
    082. 
    083.canvas.drawCircle(getWidth() /2, getHeight() / 2, mDrawableRadius, mBitmapPaint);
    084.if(mBorderWidth != 0) {
    085.canvas.drawCircle(getWidth() /2, getHeight() / 2, mBorderRadius, mBorderPaint);
    086.}
    087.}
    088. 
    089.@Override
    090.protectedvoid onSizeChanged(intw, int h, int oldw, intoldh) {
    091.super.onSizeChanged(w, h, oldw, oldh);
    092.setup();
    093.}
    094. 
    095.publicint getBorderColor() {
    096.returnmBorderColor;
    097.}
    098. 
    099.publicvoid setBorderColor(intborderColor) {
    100.if(borderColor == mBorderColor) {
    101.return;
    102.}
    103. 
    104.mBorderColor = borderColor;
    105.mBorderPaint.setColor(mBorderColor);
    106.invalidate();
    107.}
    108. 
    109.publicint getBorderWidth() {
    110.returnmBorderWidth;
    111.}
    112. 
    113.publicvoid setBorderWidth(intborderWidth) {
    114.if(borderWidth == mBorderWidth) {
    115.return;
    116.}
    117. 
    118.mBorderWidth = borderWidth;
    119.setup();
    120.}
    121. 
    122.@Override
    123.publicvoid setImageBitmap(Bitmap bm) {
    124.super.setImageBitmap(bm);
    125.mBitmap = bm;
    126.setup();
    127.}
    128. 
    129.@Override
    130.publicvoid setImageDrawable(Drawable drawable) {
    131.super.setImageDrawable(drawable);
    132.mBitmap = getBitmapFromDrawable(drawable);
    133.setup();
    134.}
    135. 
    136.@Override
    137.publicvoid setImageResource(intresId) {
    138.super.setImageResource(resId);
    139.mBitmap = getBitmapFromDrawable(getDrawable());
    140.setup();
    141.}
    142. 
    143.@Override
    144.publicvoid setImageURI(Uri uri) {
    145.super.setImageURI(uri);
    146.mBitmap = getBitmapFromDrawable(getDrawable());
    147.setup();
    148.}
    149. 
    150.privateBitmap getBitmapFromDrawable(Drawable drawable) {
    151.if(drawable == null) {
    152.returnnull;
    153.}
    154. 
    155.if(drawable instanceof BitmapDrawable) {
    156.return((BitmapDrawable) drawable).getBitmap();
    157.}
    158. 
    159.try{
    160.Bitmap bitmap;
    161. 
    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.}
    167. 
    168.Canvas canvas =new Canvas(bitmap);
    169.drawable.setBounds(0,0, canvas.getWidth(), canvas.getHeight());
    170.drawable.draw(canvas);
    171.returnbitmap;
    172.}catch (OutOfMemoryError e) {
    173.returnnull;
    174.}
    175.}
    176. 
    177.privatevoid setup() {
    178.if(!mReady) {
    179.mSetupPending =true;
    180.return;
    181.}
    182. 
    183.if(mBitmap == null) {
    184.return;
    185.}
    186. 
    187.mBitmapShader =new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
    188. 
    189.mBitmapPaint.setAntiAlias(true);
    190.mBitmapPaint.setShader(mBitmapShader);
    191. 
    192.mBorderPaint.setStyle(Paint.Style.STROKE);
    193.mBorderPaint.setAntiAlias(true);
    194.mBorderPaint.setColor(mBorderColor);
    195.mBorderPaint.setStrokeWidth(mBorderWidth);
    196. 
    197.mBitmapHeight = mBitmap.getHeight();
    198.mBitmapWidth = mBitmap.getWidth();
    199. 
    200.mBorderRect.set(0,0, getWidth(), getHeight());
    201.mBorderRadius = Math.min((mBorderRect.height() - mBorderWidth) /2, (mBorderRect.width() - mBorderWidth) /2);
    202. 
    203.mDrawableRect.set(mBorderWidth, mBorderWidth, mBorderRect.width() - mBorderWidth, mBorderRect.height() - mBorderWidth);
    204.mDrawableRadius = Math.min(mDrawableRect.height() /2, mDrawableRect.width() / 2);
    205. 
    206.updateShaderMatrix();
    207.invalidate();
    208.}
    209. 
    210.privatevoid updateShaderMatrix() {
    211.floatscale;
    212.floatdx = 0;
    213.floatdy = 0;
    214. 
    215.mShaderMatrix.set(null);
    216. 
    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.}
    224. 
    225.mShaderMatrix.setScale(scale, scale);
    226.mShaderMatrix.postTranslate((int) (dx +0.5f) + mBorderWidth, (int) (dy +0.5f) + mBorderWidth);
    227. 
    228.mBitmapShader.setLocalMatrix(mShaderMatrix);
    229.}
    230. 
    231.}
  • 相关阅读:
    程序是访问数据库的组件
    Mac搭建Vue
    Sharding-JDBC基本使用,整合Springboot实现分库分表,读写分离
    随意写写
    maven打包工具assembly打包本地jar的解决办法
    Go语言笔记 (2) 变量命名与多重赋值
    Go语言笔记(1)变量的定义与赋值
    c期末笔记(4)
    c期末笔记(3)
    c期末笔记(2)
  • 原文地址:https://www.cnblogs.com/brucemengbm/p/6854871.html
Copyright © 2011-2022 走看看