Android 圆形/圆角图片的方法
Android 圓形/圓角圖片的方法
眼下網(wǎng)上有非常多圓角圖片的實(shí)例,Github上也有一些成熟的項(xiàng)目。之前做項(xiàng)目,為了穩(wěn)定高效都是選用Github上的項(xiàng)目直接用。但這樣的結(jié)束也是Android開(kāi)發(fā)必備技能 。所以今天就來(lái)簡(jiǎn)單研究一下該技術(shù),分享給大家。
預(yù)備知識(shí):
Xfermode介紹:
以下是Android ApiDemo里的“Xfermodes”實(shí)例,效果圖。
Xfermode有三個(gè)子類。結(jié)構(gòu)例如以下:
view sourceprint? 1.publicclass 2.Xfermode 3.extendsObject 4.java.lang.Object 5.???? android.graphics.Xfermode
6.Known Direct Subclasses 7.AvoidXfermode, PixelXorXfermode, PorterDuffXfermodeAvoidXfermode 指定了一個(gè)顏色和容差,強(qiáng)制Paint避免在它上面畫圖(或者僅僅在它上面畫圖)。
PixelXorXfermode 當(dāng)覆蓋已有的顏色時(shí)。應(yīng)用一個(gè)簡(jiǎn)單的像素異或操作。
PorterDuffXfermode 這是一個(gè)很強(qiáng)大的轉(zhuǎn)換模式,使用它,能夠使用圖像合成的16條Porter-Duff規(guī)則的隨意一條來(lái)控制Paint怎樣與已有的Canvas圖像進(jìn)行交互。
上面圖片種顯示的16種模式介紹例如以下:
1.PorterDuff.Mode.CLEAR
所繪制不會(huì)提交到畫布上。
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
取兩圖層所有區(qū)域,交集部分顏色加深
14.PorterDuff.Mode.LIGHTEN
取兩圖層所有。點(diǎn)亮交集部分顏色
15.PorterDuff.Mode.MULTIPLY
取兩圖層交集部分疊加后顏色
16.PorterDuff.Mode.SCREEN
取兩圖層所有區(qū)域,交集部分變?yōu)橥该魃?/p>
了解了上面的知識(shí)點(diǎn)后,我們依據(jù)上面的知識(shí)點(diǎn)先來(lái)實(shí)現(xiàn)第一種圓角圖片制作方式:
原圖:
先看這一段代碼
view sourceprint? 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中設(shè)置的圖片 10.BitmapDrawable drawable = (BitmapDrawable) mImg.getDrawable(); 11.Bitmap bmp = drawable.getBitmap(); 12.//獲得圖片的寬,并創(chuàng)建結(jié)果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.}通過(guò)執(zhí)行上面的代碼,我們得出的結(jié)果例如以下:
大家看到這是我們須要的結(jié)果。但是這樣做可能導(dǎo)致OutOfMomery異常。假如圖片非常大或者你可能并不是通過(guò)ImageView的getDrawable獲得圖像,而是直接Decode一張非常大的圖片載入到內(nèi)存,你會(huì)發(fā)現(xiàn)可能會(huì)出現(xiàn)異常。我們做一下改變。
view sourceprint? 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 outwidth: " + options.outWidth); 15.// 此寬度是目標(biāo)ImageView希望的大小,你能夠自己定義ImageView。然后獲得ImageView的寬度。 16.intdstWidth = 150; 17.// 我們須要載入的圖片可能非常大,我們先對(duì)原有的圖片進(jìn)行裁剪 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.}再來(lái)看一下效果:
首先我們須要了解一個(gè)類BitmapShader
引用的介紹例如以下:
public BitmapShader(Bitmap bitmap,Shader.TileMode tileX,Shader.TileMode tileY)
調(diào)用這種方法來(lái)產(chǎn)生一個(gè)畫有一個(gè)位圖的渲染器(Shader)。
bitmap 在渲染器內(nèi)使用的位圖
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 :假設(shè)渲染器超出原始邊界范圍。會(huì)復(fù)制范圍內(nèi)邊緣染色。
REPEAT :橫向和縱向的反復(fù)渲染器圖片,平鋪。
MIRROR :橫向和縱向的反復(fù)渲染器圖片,這個(gè)和REPEAT 反復(fù)方式不一樣,他是以鏡像方式平鋪。
知道這個(gè)原理后,我們貼出相應(yīng)的代碼: view sourceprint? 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.}是不是挺簡(jiǎn)單的
結(jié)果我就不顯示了,跟上面的一樣。
上面也是最原始的代碼,文章的結(jié)尾貼出一份完整優(yōu)化過(guò)的代碼共大家參考例如以下:
view sourceprint? 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.}轉(zhuǎn)載于:https://www.cnblogs.com/brucemengbm/p/6854871.html
總結(jié)
以上是生活随笔為你收集整理的Android 圆形/圆角图片的方法的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 命令行参数解析
- 下一篇: 20155307 实验四 Android