Android自定义View之三角,五角星,圆形,心形图片实现

转载请标明出处:http://www.aiuxian.com/article/p-1982467.html

接下来就是如何实现的了。啥也不说了,先上图:

首先是原图:

接下来就是效果图了:

怎么样?是不是比什么都不弄直接src进去的要好呢?根据该方法大家可以实现最新版QQ的消息列表界面:

说了那么多了,还没给你们讲讲是怎么样的一个原理呢!接下来就给大家讲解一下实现该功能的原理:

其实主要是靠画笔paint中的一个方法:paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));来实现的。

下面简单的介绍下Xfermode和PorterDuffXfermode:

该Mode是设置两张图片相交时的模式。

在正常的情况下,在已有的图像上绘图将会在其上面添加一层新的形状。如果新的Paint是完全不透明的, 那么它将完全遮挡住下面的Paint;如果它是部分透明的,那么它将会被染上下面的颜色。

而setXfermode就可以来解决这个问题 .

1Canvas canvas =newCanvas(dstBitmap);

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

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

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

新画上去的图片可以理解为前景,就是src。

下图可以让大家更好的理解PorterDuffXfermode的Mode:

从上面我们可以看到PorterDuff.Mode为枚举类,一共有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

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

有没有心动了?好了。接下来就是看看如何实现的了。

新建一个名为ShapeViewDemo的项目。目录如下:

在res的文件夹下新建一个名为attrs.xml文件用来定义自定义属性。

1

2

3

4

5

6

7

8

9

接下来新建一个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;// 内圆边框颜色

029privateintout_border_color =0;// 外圆边框颜色

030privateintdefColor =0xFFFFFFFF;// 默认颜色

031

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

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

034

035privateString shape_type;// 形状的类型

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// 得到内边框颜色,否则返回默认颜色

070in_border_color = mArray.getColor(

071R.styleable.shapeimageview_in_border_color, defColor);

072// 得到外边框颜色,否则返回默认颜色

073out_border_color = mArray.getColor(

074R.styleable.shapeimageview_out_border_color, defColor);

075// 得到形状的类型

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); 必须去掉该行或注释掉,否则会出现两张图片

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// 将图片转为位图

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

100

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

102// 得到画布宽高

103width = getWidth();

104height = getHeight();

105

106intradius =0;//

107// 判断是否是圆形

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

109// 如果内圆边框和外圆边框的颜色不等于默认颜色,则说明该圆有两个边框

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

111// 计算出半径

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

113* border_size;

114// 画内圆边框

115drawCircleBorder(canvas, radius + border_size /2,

116in_border_color);

117// 画外圆边框

118drawCircleBorder(canvas,

119radius + border_size + border_size /2,

120out_border_color);

121}// 如果内圆边框颜色不等于默认颜色,则说明该圆有一个边框

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}// 如果外圆边框颜色不等于默认颜色,则说明该圆有一个边框

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{// 没有边框

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* 画出指定形状的图片

149*

150* @param cpBitmap

151* @param radius

152* @return

153*/

154privateBitmap drawShapeBitmap(Bitmap bmp,intradius) {

155// TODO Auto-generated method stub

156Bitmap squareBitmap;// 根据传入的位图截取合适的正方形位图

157Bitmap scaledBitmap;// 根据diameter对截取的正方形位图进行缩放

158intdiameter = radius *2;

159// 传入位图的宽高

160intw = bmp.getWidth();

161inth = bmp.getHeight();

162// 为了防止宽高不相等,造成圆形图片变形,因此截取长方形中处于中间位置最大的正方形图片

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// 对squareBitmap进行缩放为diameter边长的正方形位图

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);// 创建一个相同大小的画布

193Paint paint =newPaint();// 定义画笔

194paint.setAntiAlias(true);// 设置抗锯齿

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);// 此点为多边形的起点

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();// 使这些点构成封闭的多边形

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{// 这是默认形状,圆形

253// 绘制圆形

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

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

256paint);

257}

258// 设置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* 角度转弧度公式

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* 如果图片为圆形,这该方法为画出圆形图片的有色边框

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);// 设置画笔颜色

295paint.setStyle(Paint.Style.STROKE);// 设置画笔的style为STROKE:空心

296paint.setStrokeWidth(border_size);// 设置画笔的宽度

297// 画出空心圆,也就是边框

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

299}

300

301}

好了,ShapeImageView.java写完了,有没有发现其原理很简单呢?在此过程中有一点大家要注意一下,那就是我们重写Ondraw(Canvas canvas)方法时一定要把super.onDraw(canvas);注释掉或去掉,否则会出现两张图片叠在一起。不要问我为什么。

最后附上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里加上xmlns:myview="http://schemas.android.com/apk/res-auto",否则会报错。

至此,如何将一张图片弄成三角,五角,圆形或心形的图片的全部技术就给大家讲解了。希望对大家有所帮助,也希望大家能理解。当然不仅仅是三角,五角,圆形或心形的形状。只要你想的到的都能弄出来(特殊形状的图片除外),就看你敢不敢了。

由于本人是第一次写博客,如果文中有什么地方出现错误或不理解的可以在下面回复中指出来。谢谢

下面是源码下载地址:

ShapeImageViewDemo转载请标明出处:http://www.aiuxian.com/article/p-1982467.html

好吧,写之前扯扯。如果是大神的话,可以忽略此文档。有兴趣的话也可以看看。这是本人的第一篇技术博客吧(话说也谈不上是什么特别的技术

)!因为之前在写一个项目需要用到图片。但是把一张图片原封不动的src入ImageView里面去,看起来怪别扭的。因此不想走平民路线,于是就冒出来想把图片弄成三角形的,五角星或圆形的想法。说干就干,所以赶紧上网查了查怎么实现该想法。在此过程中也发现了很多问题,所以今天写出来和大家分享一下。本文是根据大牛鸿洋和alan_biao的博客编写粗来的。原理都和他们的一样,只是在图形上改了改,改成能画出三角形,五角星,心形的形状。大家可以去看看他们得博客,写的都很不错的。鸿洋的博客:链接地址alan_biao的博客:链接地址好吧就扯到这里吧!!写这篇博客的目的一个是为了和大家分享,另一个就是记录自己的收获和成长。

接下来就是如何实现的了。啥也不说了,先上图:

首先是原图:

接下来就是效果图了:

怎么样?是不是比什么都不弄直接src进去的要好呢?根据该方法大家可以实现最新版QQ的消息列表界面:

说了那么多了,还没给你们讲讲是怎么样的一个原理呢!接下来就给大家讲解一下实现该功能的原理:

其实主要是靠画笔paint中的一个方法:paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));来实现的。

下面简单的介绍下Xfermode和PorterDuffXfermode:

该Mode是设置两张图片相交时的模式。

在正常的情况下,在已有的图像上绘图将会在其上面添加一层新的形状。如果新的Paint是完全不透明的, 那么它将完全遮挡住下面的Paint;如果它是部分透明的,那么它将会被染上下面的颜色。

而setXfermode就可以来解决这个问题 .

1Canvas canvas =newCanvas(dstBitmap);

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

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

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

新画上去的图片可以理解为前景,就是src。

下图可以让大家更好的理解PorterDuffXfermode的Mode:

从上面我们可以看到PorterDuff.Mode为枚举类,一共有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

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

有没有心动了?好了。接下来就是看看如何实现的了。

新建一个名为ShapeViewDemo的项目。目录如下:

在res的文件夹下新建一个名为attrs.xml文件用来定义自定义属性。

1

2

3

4

5

6

7

8

9

接下来新建一个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;// 内圆边框颜色

029privateintout_border_color =0;// 外圆边框颜色

030privateintdefColor =0xFFFFFFFF;// 默认颜色

031

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

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

034

035privateString shape_type;// 形状的类型

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// 得到内边框颜色,否则返回默认颜色

070in_border_color = mArray.getColor(

071R.styleable.shapeimageview_in_border_color, defColor);

072// 得到外边框颜色,否则返回默认颜色

073out_border_color = mArray.getColor(

074R.styleable.shapeimageview_out_border_color, defColor);

075// 得到形状的类型

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); 必须去掉该行或注释掉,否则会出现两张图片

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// 将图片转为位图

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

100

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

102// 得到画布宽高

103width = getWidth();

104height = getHeight();

105

106intradius =0;//

107// 判断是否是圆形

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

109// 如果内圆边框和外圆边框的颜色不等于默认颜色,则说明该圆有两个边框

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

111// 计算出半径

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

113* border_size;

114// 画内圆边框

115drawCircleBorder(canvas, radius + border_size /2,

116in_border_color);

117// 画外圆边框

118drawCircleBorder(canvas,

119radius + border_size + border_size /2,

120out_border_color);

121}// 如果内圆边框颜色不等于默认颜色,则说明该圆有一个边框

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}// 如果外圆边框颜色不等于默认颜色,则说明该圆有一个边框

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{// 没有边框

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* 画出指定形状的图片

149*

150* @param cpBitmap

151* @param radius

152* @return

153*/

154privateBitmap drawShapeBitmap(Bitmap bmp,intradius) {

155// TODO Auto-generated method stub

156Bitmap squareBitmap;// 根据传入的位图截取合适的正方形位图

157Bitmap scaledBitmap;// 根据diameter对截取的正方形位图进行缩放

158intdiameter = radius *2;

159// 传入位图的宽高

160intw = bmp.getWidth();

161inth = bmp.getHeight();

162// 为了防止宽高不相等,造成圆形图片变形,因此截取长方形中处于中间位置最大的正方形图片

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// 对squareBitmap进行缩放为diameter边长的正方形位图

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);// 创建一个相同大小的画布

193Paint paint =newPaint();// 定义画笔

194paint.setAntiAlias(true);// 设置抗锯齿

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);// 此点为多边形的起点

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();// 使这些点构成封闭的多边形

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{// 这是默认形状,圆形

253// 绘制圆形

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

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

256paint);

257}

258// 设置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* 角度转弧度公式

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* 如果图片为圆形,这该方法为画出圆形图片的有色边框

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);// 设置画笔颜色

295paint.setStyle(Paint.Style.STROKE);// 设置画笔的style为STROKE:空心

296paint.setStrokeWidth(border_size);// 设置画笔的宽度

297// 画出空心圆,也就是边框

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

299}

300

301}

好了,ShapeImageView.java写完了,有没有发现其原理很简单呢?在此过程中有一点大家要注意一下,那就是我们重写Ondraw(Canvas canvas)方法时一定要把super.onDraw(canvas);注释掉或去掉,否则会出现两张图片叠在一起。不要问我为什么。

最后附上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里加上xmlns:myview="http://schemas.android.com/apk/res-auto",否则会报错。

至此,如何将一张图片弄成三角,五角,圆形或心形的图片的全部技术就给大家讲解了。希望对大家有所帮助,也希望大家能理解。当然不仅仅是三角,五角,圆形或心形的形状。只要你想的到的都能弄出来(特殊形状的图片除外),就看你敢不敢了。

由于本人是第一次写博客,如果文中有什么地方出现错误或不理解的可以在下面回复中指出来。谢谢

下面是源码下载地址:

ShapeImageViewDemo

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容

  • 系列文章之 Android中自定义View(一)系列文章之 Android中自定义View(二)系列文章之 And...
    YoungerDev阅读 2,156评论 0 4
  • 素材 圆角处理 public static Bitmap getRoundedCornerBitmap(Bitma...
    Don_阅读 4,332评论 2 18
  • A 他不可能找回自己的儿子了,这个残酷的答案在他心里很清楚,他就像是一个等待着随时被拉到行刑场的战俘,已经接受了呼...
    常圣言阅读 2,542评论 8 18
  • 小时候以为掉了一颗牙就是天大的事,中学的时候以为没进全年级前十就是不得了的事,高中的时候以为没考上政法大学就是...
    傅北药阅读 533评论 6 1
  • 文/熠歆 从五月份以来,我就很少吃油炸的东西了,因为脸上起痘痘,我刻意控制自己,尽量少吃油炸的,多吃清淡的,除了回...
    熠歆阅读 347评论 0 2