Android自定義View之三角,五角星,圓形,心形圖片實(shí)現(xiàn)

轉(zhuǎn)載請(qǐng)標(biāo)明出處:http://www.aiuxian.com/article/p-1982467.html

接下來(lái)就是如何實(shí)現(xiàn)的了。啥也不說(shuō)了,先上圖:

首先是原圖:

接下來(lái)就是效果圖了:

怎么樣?是不是比什么都不弄直接src進(jìn)去的要好呢?根據(jù)該方法大家可以實(shí)現(xiàn)最新版QQ的消息列表界面:

說(shuō)了那么多了,還沒(méi)給你們講講是怎么樣的一個(gè)原理呢!接下來(lái)就給大家講解一下實(shí)現(xiàn)該功能的原理:

其實(shí)主要是靠畫(huà)筆paint中的一個(gè)方法:paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));來(lái)實(shí)現(xiàn)的。

下面簡(jiǎn)單的介紹下Xfermode和PorterDuffXfermode:

該Mode是設(shè)置兩張圖片相交時(shí)的模式。

在正常的情況下,在已有的圖像上繪圖將會(huì)在其上面添加一層新的形狀。如果新的Paint是完全不透明的, 那么它將完全遮擋住下面的Paint;如果它是部分透明的,那么它將會(huì)被染上下面的顏色。

而setXfermode就可以來(lái)解決這個(gè)問(wèn)題 .

1Canvas canvas =newCanvas(dstBitmap);

2paint.setXfermode(newPorterDuffXfermode(Mode.SRC_IN));

3canvas.drawBitmap(srcBitmap, 0f, 0f, paint);

canvas原有的圖片可以理解為背景,就是dst;

新畫(huà)上去的圖片可以理解為前景,就是src。

下圖可以讓大家更好的理解PorterDuffXfermode的Mode:

從上面我們可以看到PorterDuff.Mode為枚舉類(lèi),一共有16個(gè)枚舉值:

1.PorterDuff.Mode.CLEAR

所繪制不會(huì)提交到畫(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>

有沒(méi)有心動(dòng)了?好了。接下來(lái)就是看看如何實(shí)現(xiàn)的了。

新建一個(gè)名為ShapeViewDemo的項(xiàng)目。目錄如下:

在res的文件夾下新建一個(gè)名為attrs.xml文件用來(lái)定義自定義屬性。

1

2

3

4

5

6

7

8

9

接下來(lái)新建一個(gè)ShapeImageView.java

001packagecom.example.shapeimageviewdemo;

002

003importandroid.content.Context;

004importandroid.content.res.TypedArray;

005importandroid.graphics.Bitmap;

006importandroid.graphics.Canvas;

007importandroid.graphics.Paint;

008importandroid.graphics.Path;

009importandroid.graphics.PorterDuff;

010importandroid.graphics.PorterDuffXfermode;

011importandroid.graphics.Bitmap.Config;

012importandroid.graphics.drawable.BitmapDrawable;

013importandroid.graphics.drawable.Drawable;

014importandroid.graphics.drawable.NinePatchDrawable;

015importandroid.util.AttributeSet;

016importandroid.widget.ImageView;

017

018/**

019*

020* @author Joker_Ya

021*

022*/

023publicclassShapeImageViewextendsImageView {

024

025privateContext mContext;

026

027privateintborder_size =0;// 邊框厚度

028privateintin_border_color =0;// 內(nèi)圓邊框顏色

029privateintout_border_color =0;// 外圓邊框顏色

030privateintdefColor =0xFFFFFFFF;// 默認(rèn)顏色

031

032privateintwidth =0;// 控件的寬度

033privateintheight =0;// 控件的高度

034

035privateString shape_type;// 形狀的類(lèi)型

036

037publicShapeImageView(Context context) {

038super(context);

039// TODO Auto-generated constructor stub

040this.mContext = context;

041}

042

043publicShapeImageView(Context context, AttributeSet attrs) {

044super(context, attrs);

045// TODO Auto-generated constructor stub

046this.mContext = context;

047setAttributes(attrs);

048}

049

050publicShapeImageView(Context context, AttributeSet attrs,intdefStyle) {

051super(context, attrs, defStyle);

052// TODO Auto-generated constructor stub

053this.mContext = context;

054setAttributes(attrs);

055}

056

057/**

058* 獲得自定義屬性

059*

060* @param attrs

061*/

062privatevoidsetAttributes(AttributeSet attrs) {

063// TODO Auto-generated method stub

064TypedArray mArray = mContext.obtainStyledAttributes(attrs,

065R.styleable.shapeimageview);

066// 得到邊框厚度,否則返回0

067border_size = mArray.getDimensionPixelSize(

068R.styleable.shapeimageview_border_size,0);

069// 得到內(nèi)邊框顏色,否則返回默認(rèn)顏色

070in_border_color = mArray.getColor(

071R.styleable.shapeimageview_in_border_color, defColor);

072// 得到外邊框顏色,否則返回默認(rèn)顏色

073out_border_color = mArray.getColor(

074R.styleable.shapeimageview_out_border_color, defColor);

075// 得到形狀的類(lèi)型

076shape_type = mArray.getString(R.styleable.shapeimageview_shape_type);

077

078mArray.recycle();// 回收mArray

079}

080

081@Override

082protectedvoidonDraw(Canvas canvas) {

083// TODO Auto-generated method stub

084// super.onDraw(canvas); 必須去掉該行或注釋掉,否則會(huì)出現(xiàn)兩張圖片

085// 得到傳入的圖片

086Drawable drawable = getDrawable();

087if(drawable ==null) {

088return;

089}

090if(getWidth() ==0|| getHeight() ==0) {

091return;

092}

093this.measure(0,0);

094if(drawable.getClass() == NinePatchDrawable.class) {// 如果該傳入圖片是.9格式的圖片

095return;

096}

097

098// 將圖片轉(zhuǎn)為位圖

099Bitmap mBitmap = ((BitmapDrawable) drawable).getBitmap();

100

101Bitmap cpBitmap = mBitmap.copy(Bitmap.Config.ARGB_8888,true);

102// 得到畫(huà)布寬高

103width = getWidth();

104height = getHeight();

105

106intradius =0;//

107// 判斷是否是圓形

108if("round".equals(shape_type)) {

109// 如果內(nèi)圓邊框和外圓邊框的顏色不等于默認(rèn)顏色,則說(shuō)明該圓有兩個(gè)邊框

110if(in_border_color != defColor && out_border_color != defColor) {

111// 計(jì)算出半徑

112radius = (width < height ? width : height) /2-2

113* border_size;

114// 畫(huà)內(nèi)圓邊框

115drawCircleBorder(canvas, radius + border_size /2,

116in_border_color);

117// 畫(huà)外圓邊框

118drawCircleBorder(canvas,

119radius + border_size + border_size /2,

120out_border_color);

121}// 如果內(nèi)圓邊框顏色不等于默認(rèn)顏色,則說(shuō)明該圓有一個(gè)邊框

122elseif(in_border_color != defColor

123&& out_border_color == defColor) {

124radius = (width < height ? width : height) /2- border_size;

125

126drawCircleBorder(canvas, radius + border_size /2,

127in_border_color);

128}// 如果外圓邊框顏色不等于默認(rèn)顏色,則說(shuō)明該圓有一個(gè)邊框

129elseif(in_border_color == defColor

130&& out_border_color != defColor) {

131radius = (width < height ? width : height) /2- border_size;

132

133drawCircleBorder(canvas, radius + border_size /2,

134out_border_color);

135}else{// 沒(méi)有邊框

136radius = (width < height ? width : height) /2;

137}

138}else{

139radius = (width < height ? width : height) /2;

140}

141

142Bitmap shapeBitmap = drawShapeBitmap(cpBitmap, radius);

143canvas.drawBitmap(shapeBitmap, width /2- radius, height /2- radius,

144null);

145}

146

147/**

148* 畫(huà)出指定形狀的圖片

149*

150* @param cpBitmap

151* @param radius

152* @return

153*/

154privateBitmap drawShapeBitmap(Bitmap bmp,intradius) {

155// TODO Auto-generated method stub

156Bitmap squareBitmap;// 根據(jù)傳入的位圖截取合適的正方形位圖

157Bitmap scaledBitmap;// 根據(jù)diameter對(duì)截取的正方形位圖進(jìn)行縮放

158intdiameter = radius *2;

159// 傳入位圖的寬高

160intw = bmp.getWidth();

161inth = bmp.getHeight();

162// 為了防止寬高不相等,造成圓形圖片變形,因此截取長(zhǎng)方形中處于中間位置最大的正方形圖片

163intsquarewidth =0, squareheight =0;// 矩形的寬高

164intx =0, y =0;

165if(h > w) {// 如果高>寬

166squarewidth = squareheight = w;

167x =0;

168y = (h - w) /2;

169// 截取正方形圖片

170squareBitmap = Bitmap.createBitmap(bmp, x, y, squarewidth,

171squareheight);

172}elseif(h < w) {// 如果寬>高

173squarewidth = squareheight = h;

174x = (w - h) /2;

175y =0;

176squareBitmap = Bitmap.createBitmap(bmp, x, y, squarewidth,

177squareheight);

178}else{

179squareBitmap = bmp;

180}

181// 對(duì)squareBitmap進(jìn)行縮放為diameter邊長(zhǎng)的正方形位圖

182if(squareBitmap.getWidth() != diameter

183|| squareBitmap.getHeight() != diameter) {

184scaledBitmap = Bitmap.createScaledBitmap(squareBitmap, diameter,

185diameter,true);

186}else{

187scaledBitmap = squareBitmap;

188}

189

190Bitmap outputbmp = Bitmap.createBitmap(scaledBitmap.getWidth(),

191scaledBitmap.getHeight(), Config.ARGB_8888);

192Canvas canvas =newCanvas(outputbmp);// 創(chuàng)建一個(gè)相同大小的畫(huà)布

193Paint paint =newPaint();// 定義畫(huà)筆

194paint.setAntiAlias(true);// 設(shè)置抗鋸齒

195paint.setFilterBitmap(true);

196paint.setDither(true);

197canvas.drawARGB(0,0,0,0);

198

199if("star".equals(shape_type)) {// 如果繪制的形狀為五角星形

200Path path =newPath();

201floatradian = degree2Radian(36);// 36為五角星的角度

202floatradius_in = (float) (radius * Math.sin(radian /2) / Math

203.cos(radian));// 中間五邊形的半徑

204

205path.moveTo((float) (radius * Math.cos(radian /2)),0);// 此點(diǎn)為多邊形的起點(diǎn)

206path.lineTo((float) (radius * Math.cos(radian /2) + radius_in

207* Math.sin(radian)),

208(float) (radius - radius * Math.sin(radian /2)));

209path.lineTo((float) (radius * Math.cos(radian /2) *2),

210(float) (radius - radius * Math.sin(radian /2)));

211path.lineTo((float) (radius * Math.cos(radian /2) + radius_in

212* Math.cos(radian /2)),

213(float) (radius + radius_in * Math.sin(radian /2)));

214path.lineTo(

215(float) (radius * Math.cos(radian /2) + radius

216* Math.sin(radian)), (float) (radius + radius

217* Math.cos(radian)));

218path.lineTo((float) (radius * Math.cos(radian /2)),

219(float) (radius + radius_in));

220path.lineTo(

221(float) (radius * Math.cos(radian /2) - radius

222* Math.sin(radian)), (float) (radius + radius

223* Math.cos(radian)));

224path.lineTo((float) (radius * Math.cos(radian /2) - radius_in

225* Math.cos(radian /2)),

226(float) (radius + radius_in * Math.sin(radian /2)));

227path.lineTo(0, (float) (radius - radius * Math.sin(radian /2)));

228path.lineTo((float) (radius * Math.cos(radian /2) - radius_in

229* Math.sin(radian)),

230(float) (radius - radius * Math.sin(radian /2)));

231

232path.close();// 使這些點(diǎn)構(gòu)成封閉的多邊形

233canvas.drawPath(path, paint);

234}elseif("triangle".equals(shape_type)) {// 如果繪制的形狀為三角形

235Path path =newPath();

236

237path.moveTo(0,0);

238path.lineTo(diameter /2, diameter);

239path.lineTo(diameter,0);

240

241path.close();

242canvas.drawPath(path, paint);

243}elseif("heart".equals(shape_type)) {// 如果繪制的形狀為心形

244Path path =newPath();

245

246path.moveTo(diameter /2, diameter /5);

247path.quadTo(diameter,0, diameter /2, diameter /1.0f);

248path.quadTo(0,0, diameter /2, diameter /5);

249

250path.close();

251canvas.drawPath(path, paint);

252}else{// 這是默認(rèn)形狀,圓形

253// 繪制圓形

254canvas.drawCircle(scaledBitmap.getWidth() /2,

255scaledBitmap.getHeight() /2, scaledBitmap.getWidth() /2,

256paint);

257}

258// 設(shè)置Xfermode的Mode

259paint.setXfermode(newPorterDuffXfermode(PorterDuff.Mode.SRC_IN));

260canvas.drawBitmap(scaledBitmap,0,0, paint);

261

262bmp =null;

263squareBitmap =null;

264scaledBitmap =null;

265returnoutputbmp;

266

267}

268

269/**

270* 角度轉(zhuǎn)弧度公式

271*

272* @param degree

273* @return

274*/

275privatefloatdegree2Radian(intdegree) {

276// TODO Auto-generated method stub

277return(float) (Math.PI * degree /180);

278}

279

280/**

281* 如果圖片為圓形,這該方法為畫(huà)出圓形圖片的有色邊框

282*

283* @param canvas

284* @param radius 邊框半徑

285* @param color 邊框顏色

286*/

287privatevoiddrawCircleBorder(Canvas canvas,intradius,intcolor) {

288// TODO Auto-generated method stub

289Paint paint =newPaint();

290

291paint.setAntiAlias(true);// 抗鋸齒

292paint.setFilterBitmap(true);

293paint.setDither(true);

294paint.setColor(color);// 設(shè)置畫(huà)筆顏色

295paint.setStyle(Paint.Style.STROKE);// 設(shè)置畫(huà)筆的style為STROKE:空心

296paint.setStrokeWidth(border_size);// 設(shè)置畫(huà)筆的寬度

297// 畫(huà)出空心圓,也就是邊框

298canvas.drawCircle(width /2, height /2, radius, paint);

299}

300

301}

好了,ShapeImageView.java寫(xiě)完了,有沒(méi)有發(fā)現(xiàn)其原理很簡(jiǎn)單呢?在此過(guò)程中有一點(diǎn)大家要注意一下,那就是我們重寫(xiě)Ondraw(Canvas canvas)方法時(shí)一定要把super.onDraw(canvas);注釋掉或去掉,否則會(huì)出現(xiàn)兩張圖片疊在一起。不要問(wèn)我為什么。

最后附上Activity_main.xml

01http://schemas.android.com/apk/res/android"

02xmlns:myview="http://schemas.android.com/apk/res-auto"

03xmlns:tools="http://schemas.android.com/tools"

04android:layout_width="match_parent"

05android:layout_height="match_parent"

06android:paddingBottom="@dimen/activity_vertical_margin"

07android:paddingLeft="@dimen/activity_horizontal_margin"

08android:paddingRight="@dimen/activity_horizontal_margin"

09android:paddingTop="@dimen/activity_vertical_margin"

10android:orientation="vertical"

11tools:context=".MainActivity">

12

13

14android:layout_width="60dip"

15android:layout_height="60dip"

16android:src="@drawable/girl"

17/>

18

19

20android:layout_width="80dip"

21android:layout_height="80dip"

22android:layout_marginTop="10dip"

23android:src="@drawable/girl"

24myview:shape_type="triangle"

25/>

26

27

28android:layout_width="100dip"

29android:layout_height="100dip"

30android:layout_marginTop="10dip"

31android:src="@drawable/girl"

32myview:shape_type="star"

33/>

34

35

36android:layout_width="100dip"

37android:layout_height="100dip"

38android:layout_marginTop="10dip"

39android:src="@drawable/girl"

40myview:border_size="2dip"

41myview:in_border_color="#EE0000"

42myview:out_border_color="#00EEEE"

43myview:shape_type="round"

44/>

45

46

47android:layout_width="100dip"

48android:layout_height="100dip"

49android:layout_marginTop="10dip"

50android:src="@drawable/girl"

51myview:shape_type="heart"

52/>

53

由于用到了自定義屬性,因此要在主layout里加上x(chóng)mlns:myview="http://schemas.android.com/apk/res-auto",否則會(huì)報(bào)錯(cuò)。

至此,如何將一張圖片弄成三角,五角,圓形或心形的圖片的全部技術(shù)就給大家講解了。希望對(duì)大家有所幫助,也希望大家能理解。當(dāng)然不僅僅是三角,五角,圓形或心形的形狀。只要你想的到的都能弄出來(lái)(特殊形狀的圖片除外),就看你敢不敢了。

由于本人是第一次寫(xiě)博客,如果文中有什么地方出現(xiàn)錯(cuò)誤或不理解的可以在下面回復(fù)中指出來(lái)。謝謝

下面是源碼下載地址:

ShapeImageViewDemo轉(zhuǎn)載請(qǐng)標(biāo)明出處:http://www.aiuxian.com/article/p-1982467.html

好吧,寫(xiě)之前扯扯。如果是大神的話(huà),可以忽略此文檔。有興趣的話(huà)也可以看看。這是本人的第一篇技術(shù)博客吧(話(huà)說(shuō)也談不上是什么特別的技術(shù)

)!因?yàn)橹霸趯?xiě)一個(gè)項(xiàng)目需要用到圖片。但是把一張圖片原封不動(dòng)的src入ImageView里面去,看起來(lái)怪別扭的。因此不想走平民路線(xiàn),于是就冒出來(lái)想把圖片弄成三角形的,五角星或圓形的想法。說(shuō)干就干,所以趕緊上網(wǎng)查了查怎么實(shí)現(xiàn)該想法。在此過(guò)程中也發(fā)現(xiàn)了很多問(wèn)題,所以今天寫(xiě)出來(lái)和大家分享一下。本文是根據(jù)大牛鴻洋和alan_biao的博客編寫(xiě)粗來(lái)的。原理都和他們的一樣,只是在圖形上改了改,改成能畫(huà)出三角形,五角星,心形的形狀。大家可以去看看他們得博客,寫(xiě)的都很不錯(cuò)的。鴻洋的博客:鏈接地址alan_biao的博客:鏈接地址好吧就扯到這里吧!!寫(xiě)這篇博客的目的一個(gè)是為了和大家分享,另一個(gè)就是記錄自己的收獲和成長(zhǎng)。

接下來(lái)就是如何實(shí)現(xiàn)的了。啥也不說(shuō)了,先上圖:

首先是原圖:

接下來(lái)就是效果圖了:

怎么樣?是不是比什么都不弄直接src進(jìn)去的要好呢?根據(jù)該方法大家可以實(shí)現(xiàn)最新版QQ的消息列表界面:

說(shuō)了那么多了,還沒(méi)給你們講講是怎么樣的一個(gè)原理呢!接下來(lái)就給大家講解一下實(shí)現(xiàn)該功能的原理:

其實(shí)主要是靠畫(huà)筆paint中的一個(gè)方法:paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));來(lái)實(shí)現(xiàn)的。

下面簡(jiǎn)單的介紹下Xfermode和PorterDuffXfermode:

該Mode是設(shè)置兩張圖片相交時(shí)的模式。

在正常的情況下,在已有的圖像上繪圖將會(huì)在其上面添加一層新的形狀。如果新的Paint是完全不透明的, 那么它將完全遮擋住下面的Paint;如果它是部分透明的,那么它將會(huì)被染上下面的顏色。

而setXfermode就可以來(lái)解決這個(gè)問(wèn)題 .

1Canvas canvas =newCanvas(dstBitmap);

2paint.setXfermode(newPorterDuffXfermode(Mode.SRC_IN));

3canvas.drawBitmap(srcBitmap, 0f, 0f, paint);

canvas原有的圖片可以理解為背景,就是dst;

新畫(huà)上去的圖片可以理解為前景,就是src。

下圖可以讓大家更好的理解PorterDuffXfermode的Mode:

從上面我們可以看到PorterDuff.Mode為枚舉類(lèi),一共有16個(gè)枚舉值:

1.PorterDuff.Mode.CLEAR

所繪制不會(huì)提交到畫(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>

有沒(méi)有心動(dòng)了?好了。接下來(lái)就是看看如何實(shí)現(xiàn)的了。

新建一個(gè)名為ShapeViewDemo的項(xiàng)目。目錄如下:

在res的文件夾下新建一個(gè)名為attrs.xml文件用來(lái)定義自定義屬性。

1

2

3

4

5

6

7

8

9

接下來(lái)新建一個(gè)ShapeImageView.java

001packagecom.example.shapeimageviewdemo;

002

003importandroid.content.Context;

004importandroid.content.res.TypedArray;

005importandroid.graphics.Bitmap;

006importandroid.graphics.Canvas;

007importandroid.graphics.Paint;

008importandroid.graphics.Path;

009importandroid.graphics.PorterDuff;

010importandroid.graphics.PorterDuffXfermode;

011importandroid.graphics.Bitmap.Config;

012importandroid.graphics.drawable.BitmapDrawable;

013importandroid.graphics.drawable.Drawable;

014importandroid.graphics.drawable.NinePatchDrawable;

015importandroid.util.AttributeSet;

016importandroid.widget.ImageView;

017

018/**

019*

020* @author Joker_Ya

021*

022*/

023publicclassShapeImageViewextendsImageView {

024

025privateContext mContext;

026

027privateintborder_size =0;// 邊框厚度

028privateintin_border_color =0;// 內(nèi)圓邊框顏色

029privateintout_border_color =0;// 外圓邊框顏色

030privateintdefColor =0xFFFFFFFF;// 默認(rèn)顏色

031

032privateintwidth =0;// 控件的寬度

033privateintheight =0;// 控件的高度

034

035privateString shape_type;// 形狀的類(lèi)型

036

037publicShapeImageView(Context context) {

038super(context);

039// TODO Auto-generated constructor stub

040this.mContext = context;

041}

042

043publicShapeImageView(Context context, AttributeSet attrs) {

044super(context, attrs);

045// TODO Auto-generated constructor stub

046this.mContext = context;

047setAttributes(attrs);

048}

049

050publicShapeImageView(Context context, AttributeSet attrs,intdefStyle) {

051super(context, attrs, defStyle);

052// TODO Auto-generated constructor stub

053this.mContext = context;

054setAttributes(attrs);

055}

056

057/**

058* 獲得自定義屬性

059*

060* @param attrs

061*/

062privatevoidsetAttributes(AttributeSet attrs) {

063// TODO Auto-generated method stub

064TypedArray mArray = mContext.obtainStyledAttributes(attrs,

065R.styleable.shapeimageview);

066// 得到邊框厚度,否則返回0

067border_size = mArray.getDimensionPixelSize(

068R.styleable.shapeimageview_border_size,0);

069// 得到內(nèi)邊框顏色,否則返回默認(rèn)顏色

070in_border_color = mArray.getColor(

071R.styleable.shapeimageview_in_border_color, defColor);

072// 得到外邊框顏色,否則返回默認(rèn)顏色

073out_border_color = mArray.getColor(

074R.styleable.shapeimageview_out_border_color, defColor);

075// 得到形狀的類(lèi)型

076shape_type = mArray.getString(R.styleable.shapeimageview_shape_type);

077

078mArray.recycle();// 回收mArray

079}

080

081@Override

082protectedvoidonDraw(Canvas canvas) {

083// TODO Auto-generated method stub

084// super.onDraw(canvas); 必須去掉該行或注釋掉,否則會(huì)出現(xiàn)兩張圖片

085// 得到傳入的圖片

086Drawable drawable = getDrawable();

087if(drawable ==null) {

088return;

089}

090if(getWidth() ==0|| getHeight() ==0) {

091return;

092}

093this.measure(0,0);

094if(drawable.getClass() == NinePatchDrawable.class) {// 如果該傳入圖片是.9格式的圖片

095return;

096}

097

098// 將圖片轉(zhuǎn)為位圖

099Bitmap mBitmap = ((BitmapDrawable) drawable).getBitmap();

100

101Bitmap cpBitmap = mBitmap.copy(Bitmap.Config.ARGB_8888,true);

102// 得到畫(huà)布寬高

103width = getWidth();

104height = getHeight();

105

106intradius =0;//

107// 判斷是否是圓形

108if("round".equals(shape_type)) {

109// 如果內(nèi)圓邊框和外圓邊框的顏色不等于默認(rèn)顏色,則說(shuō)明該圓有兩個(gè)邊框

110if(in_border_color != defColor && out_border_color != defColor) {

111// 計(jì)算出半徑

112radius = (width < height ? width : height) /2-2

113* border_size;

114// 畫(huà)內(nèi)圓邊框

115drawCircleBorder(canvas, radius + border_size /2,

116in_border_color);

117// 畫(huà)外圓邊框

118drawCircleBorder(canvas,

119radius + border_size + border_size /2,

120out_border_color);

121}// 如果內(nèi)圓邊框顏色不等于默認(rèn)顏色,則說(shuō)明該圓有一個(gè)邊框

122elseif(in_border_color != defColor

123&& out_border_color == defColor) {

124radius = (width < height ? width : height) /2- border_size;

125

126drawCircleBorder(canvas, radius + border_size /2,

127in_border_color);

128}// 如果外圓邊框顏色不等于默認(rèn)顏色,則說(shuō)明該圓有一個(gè)邊框

129elseif(in_border_color == defColor

130&& out_border_color != defColor) {

131radius = (width < height ? width : height) /2- border_size;

132

133drawCircleBorder(canvas, radius + border_size /2,

134out_border_color);

135}else{// 沒(méi)有邊框

136radius = (width < height ? width : height) /2;

137}

138}else{

139radius = (width < height ? width : height) /2;

140}

141

142Bitmap shapeBitmap = drawShapeBitmap(cpBitmap, radius);

143canvas.drawBitmap(shapeBitmap, width /2- radius, height /2- radius,

144null);

145}

146

147/**

148* 畫(huà)出指定形狀的圖片

149*

150* @param cpBitmap

151* @param radius

152* @return

153*/

154privateBitmap drawShapeBitmap(Bitmap bmp,intradius) {

155// TODO Auto-generated method stub

156Bitmap squareBitmap;// 根據(jù)傳入的位圖截取合適的正方形位圖

157Bitmap scaledBitmap;// 根據(jù)diameter對(duì)截取的正方形位圖進(jìn)行縮放

158intdiameter = radius *2;

159// 傳入位圖的寬高

160intw = bmp.getWidth();

161inth = bmp.getHeight();

162// 為了防止寬高不相等,造成圓形圖片變形,因此截取長(zhǎng)方形中處于中間位置最大的正方形圖片

163intsquarewidth =0, squareheight =0;// 矩形的寬高

164intx =0, y =0;

165if(h > w) {// 如果高>寬

166squarewidth = squareheight = w;

167x =0;

168y = (h - w) /2;

169// 截取正方形圖片

170squareBitmap = Bitmap.createBitmap(bmp, x, y, squarewidth,

171squareheight);

172}elseif(h < w) {// 如果寬>高

173squarewidth = squareheight = h;

174x = (w - h) /2;

175y =0;

176squareBitmap = Bitmap.createBitmap(bmp, x, y, squarewidth,

177squareheight);

178}else{

179squareBitmap = bmp;

180}

181// 對(duì)squareBitmap進(jìn)行縮放為diameter邊長(zhǎng)的正方形位圖

182if(squareBitmap.getWidth() != diameter

183|| squareBitmap.getHeight() != diameter) {

184scaledBitmap = Bitmap.createScaledBitmap(squareBitmap, diameter,

185diameter,true);

186}else{

187scaledBitmap = squareBitmap;

188}

189

190Bitmap outputbmp = Bitmap.createBitmap(scaledBitmap.getWidth(),

191scaledBitmap.getHeight(), Config.ARGB_8888);

192Canvas canvas =newCanvas(outputbmp);// 創(chuàng)建一個(gè)相同大小的畫(huà)布

193Paint paint =newPaint();// 定義畫(huà)筆

194paint.setAntiAlias(true);// 設(shè)置抗鋸齒

195paint.setFilterBitmap(true);

196paint.setDither(true);

197canvas.drawARGB(0,0,0,0);

198

199if("star".equals(shape_type)) {// 如果繪制的形狀為五角星形

200Path path =newPath();

201floatradian = degree2Radian(36);// 36為五角星的角度

202floatradius_in = (float) (radius * Math.sin(radian /2) / Math

203.cos(radian));// 中間五邊形的半徑

204

205path.moveTo((float) (radius * Math.cos(radian /2)),0);// 此點(diǎn)為多邊形的起點(diǎn)

206path.lineTo((float) (radius * Math.cos(radian /2) + radius_in

207* Math.sin(radian)),

208(float) (radius - radius * Math.sin(radian /2)));

209path.lineTo((float) (radius * Math.cos(radian /2) *2),

210(float) (radius - radius * Math.sin(radian /2)));

211path.lineTo((float) (radius * Math.cos(radian /2) + radius_in

212* Math.cos(radian /2)),

213(float) (radius + radius_in * Math.sin(radian /2)));

214path.lineTo(

215(float) (radius * Math.cos(radian /2) + radius

216* Math.sin(radian)), (float) (radius + radius

217* Math.cos(radian)));

218path.lineTo((float) (radius * Math.cos(radian /2)),

219(float) (radius + radius_in));

220path.lineTo(

221(float) (radius * Math.cos(radian /2) - radius

222* Math.sin(radian)), (float) (radius + radius

223* Math.cos(radian)));

224path.lineTo((float) (radius * Math.cos(radian /2) - radius_in

225* Math.cos(radian /2)),

226(float) (radius + radius_in * Math.sin(radian /2)));

227path.lineTo(0, (float) (radius - radius * Math.sin(radian /2)));

228path.lineTo((float) (radius * Math.cos(radian /2) - radius_in

229* Math.sin(radian)),

230(float) (radius - radius * Math.sin(radian /2)));

231

232path.close();// 使這些點(diǎn)構(gòu)成封閉的多邊形

233canvas.drawPath(path, paint);

234}elseif("triangle".equals(shape_type)) {// 如果繪制的形狀為三角形

235Path path =newPath();

236

237path.moveTo(0,0);

238path.lineTo(diameter /2, diameter);

239path.lineTo(diameter,0);

240

241path.close();

242canvas.drawPath(path, paint);

243}elseif("heart".equals(shape_type)) {// 如果繪制的形狀為心形

244Path path =newPath();

245

246path.moveTo(diameter /2, diameter /5);

247path.quadTo(diameter,0, diameter /2, diameter /1.0f);

248path.quadTo(0,0, diameter /2, diameter /5);

249

250path.close();

251canvas.drawPath(path, paint);

252}else{// 這是默認(rèn)形狀,圓形

253// 繪制圓形

254canvas.drawCircle(scaledBitmap.getWidth() /2,

255scaledBitmap.getHeight() /2, scaledBitmap.getWidth() /2,

256paint);

257}

258// 設(shè)置Xfermode的Mode

259paint.setXfermode(newPorterDuffXfermode(PorterDuff.Mode.SRC_IN));

260canvas.drawBitmap(scaledBitmap,0,0, paint);

261

262bmp =null;

263squareBitmap =null;

264scaledBitmap =null;

265returnoutputbmp;

266

267}

268

269/**

270* 角度轉(zhuǎn)弧度公式

271*

272* @param degree

273* @return

274*/

275privatefloatdegree2Radian(intdegree) {

276// TODO Auto-generated method stub

277return(float) (Math.PI * degree /180);

278}

279

280/**

281* 如果圖片為圓形,這該方法為畫(huà)出圓形圖片的有色邊框

282*

283* @param canvas

284* @param radius 邊框半徑

285* @param color 邊框顏色

286*/

287privatevoiddrawCircleBorder(Canvas canvas,intradius,intcolor) {

288// TODO Auto-generated method stub

289Paint paint =newPaint();

290

291paint.setAntiAlias(true);// 抗鋸齒

292paint.setFilterBitmap(true);

293paint.setDither(true);

294paint.setColor(color);// 設(shè)置畫(huà)筆顏色

295paint.setStyle(Paint.Style.STROKE);// 設(shè)置畫(huà)筆的style為STROKE:空心

296paint.setStrokeWidth(border_size);// 設(shè)置畫(huà)筆的寬度

297// 畫(huà)出空心圓,也就是邊框

298canvas.drawCircle(width /2, height /2, radius, paint);

299}

300

301}

好了,ShapeImageView.java寫(xiě)完了,有沒(méi)有發(fā)現(xiàn)其原理很簡(jiǎn)單呢?在此過(guò)程中有一點(diǎn)大家要注意一下,那就是我們重寫(xiě)Ondraw(Canvas canvas)方法時(shí)一定要把super.onDraw(canvas);注釋掉或去掉,否則會(huì)出現(xiàn)兩張圖片疊在一起。不要問(wèn)我為什么。

最后附上Activity_main.xml

01http://schemas.android.com/apk/res/android"

02xmlns:myview="http://schemas.android.com/apk/res-auto"

03xmlns:tools="http://schemas.android.com/tools"

04android:layout_width="match_parent"

05android:layout_height="match_parent"

06android:paddingBottom="@dimen/activity_vertical_margin"

07android:paddingLeft="@dimen/activity_horizontal_margin"

08android:paddingRight="@dimen/activity_horizontal_margin"

09android:paddingTop="@dimen/activity_vertical_margin"

10android:orientation="vertical"

11tools:context=".MainActivity">

12

13

14android:layout_width="60dip"

15android:layout_height="60dip"

16android:src="@drawable/girl"

17/>

18

19

20android:layout_width="80dip"

21android:layout_height="80dip"

22android:layout_marginTop="10dip"

23android:src="@drawable/girl"

24myview:shape_type="triangle"

25/>

26

27

28android:layout_width="100dip"

29android:layout_height="100dip"

30android:layout_marginTop="10dip"

31android:src="@drawable/girl"

32myview:shape_type="star"

33/>

34

35

36android:layout_width="100dip"

37android:layout_height="100dip"

38android:layout_marginTop="10dip"

39android:src="@drawable/girl"

40myview:border_size="2dip"

41myview:in_border_color="#EE0000"

42myview:out_border_color="#00EEEE"

43myview:shape_type="round"

44/>

45

46

47android:layout_width="100dip"

48android:layout_height="100dip"

49android:layout_marginTop="10dip"

50android:src="@drawable/girl"

51myview:shape_type="heart"

52/>

53

由于用到了自定義屬性,因此要在主layout里加上x(chóng)mlns:myview="http://schemas.android.com/apk/res-auto",否則會(huì)報(bào)錯(cuò)。

至此,如何將一張圖片弄成三角,五角,圓形或心形的圖片的全部技術(shù)就給大家講解了。希望對(duì)大家有所幫助,也希望大家能理解。當(dāng)然不僅僅是三角,五角,圓形或心形的形狀。只要你想的到的都能弄出來(lái)(特殊形狀的圖片除外),就看你敢不敢了。

由于本人是第一次寫(xiě)博客,如果文中有什么地方出現(xiàn)錯(cuò)誤或不理解的可以在下面回復(fù)中指出來(lái)。謝謝

下面是源碼下載地址:

ShapeImageViewDemo

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,663評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,125評(píng)論 3 414
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 175,506評(píng)論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 62,614評(píng)論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,402評(píng)論 6 404
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 54,934評(píng)論 1 321
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,021評(píng)論 3 440
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 42,168評(píng)論 0 287
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,690評(píng)論 1 333
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,596評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,784評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,288評(píng)論 5 357
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,027評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 34,404評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 35,662評(píng)論 1 280
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,398評(píng)論 3 390
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,743評(píng)論 2 370

推薦閱讀更多精彩內(nèi)容

  • 系列文章之 Android中自定義View(一)系列文章之 Android中自定義View(二)系列文章之 And...
    YoungerDev閱讀 2,191評(píng)論 0 4
  • 素材 圓角處理 public static Bitmap getRoundedCornerBitmap(Bitma...
    Don_閱讀 4,365評(píng)論 2 18
  • A 他不可能找回自己的兒子了,這個(gè)殘酷的答案在他心里很清楚,他就像是一個(gè)等待著隨時(shí)被拉到行刑場(chǎng)的戰(zhàn)俘,已經(jīng)接受了呼...
    常圣言閱讀 2,590評(píng)論 8 18
  • 小時(shí)候以為掉了一顆牙就是天大的事,中學(xué)的時(shí)候以為沒(méi)進(jìn)全年級(jí)前十就是不得了的事,高中的時(shí)候以為沒(méi)考上政法大學(xué)就是...
    傅北藥閱讀 548評(píng)論 6 1
  • 文/熠歆 從五月份以來(lái),我就很少吃油炸的東西了,因?yàn)槟樕掀鸲欢唬铱桃饪刂谱约海M量少吃油炸的,多吃清淡的,除了回...
    熠歆閱讀 360評(píng)論 0 2