转载请标明出处:http://blog.csdn.net/joker_ya/article/details/38589677

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

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

首先是原图:

接下来就是效果图了:

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

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

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

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


该Mode是设置两张图片相交时的模式。
在正常的情况下,在已有的图像上绘图将会在其上面添加一层新的形状。如果新的Paint是完全不透明的, 那么它将完全遮挡住下面的Paint;如果它是部分透明的,那么它将会被染上下面的颜色。
而setXfermode就可以来解决这个问题 .

Canvas canvas = new Canvas(dstBitmap);
paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
canvas.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文件用来定义自定义属性。

<?xml version="1.0" encoding="utf-8"?>
<resources><declare-styleable name="shapeimageview"><attr name="border_size" format="dimension" /><attr name="in_border_color" format="color" /><attr name="out_border_color" format="color"/><attr name="shape_type" format="string"/></declare-styleable>
</resources>

接下来新建一个ShapeImageView.java

package com.example.shapeimageviewdemo;import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Bitmap.Config;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.NinePatchDrawable;
import android.util.AttributeSet;
import android.widget.ImageView;/*** * @author Joker_Ya* */
public class ShapeImageView extends ImageView {private Context mContext;private int border_size = 0;// 边框厚度private int in_border_color = 0;// 内圆边框颜色private int out_border_color = 0;// 外圆边框颜色private int defColor = 0xFFFFFFFF;// 默认颜色private int width = 0;// 控件的宽度private int height = 0;// 控件的高度private String shape_type;// 形状的类型public ShapeImageView(Context context) {super(context);// TODO Auto-generated constructor stubthis.mContext = context;}public ShapeImageView(Context context, AttributeSet attrs) {super(context, attrs);// TODO Auto-generated constructor stubthis.mContext = context;setAttributes(attrs);}public ShapeImageView(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);// TODO Auto-generated constructor stubthis.mContext = context;setAttributes(attrs);}/*** 获得自定义属性* * @param attrs*/private void setAttributes(AttributeSet attrs) {// TODO Auto-generated method stubTypedArray mArray = mContext.obtainStyledAttributes(attrs,R.styleable.shapeimageview);// 得到边框厚度,否则返回0border_size = mArray.getDimensionPixelSize(R.styleable.shapeimageview_border_size, 0);// 得到内边框颜色,否则返回默认颜色in_border_color = mArray.getColor(R.styleable.shapeimageview_in_border_color, defColor);// 得到外边框颜色,否则返回默认颜色out_border_color = mArray.getColor(R.styleable.shapeimageview_out_border_color, defColor);// 得到形状的类型shape_type = mArray.getString(R.styleable.shapeimageview_shape_type);mArray.recycle();// 回收mArray}@Overrideprotected void onDraw(Canvas canvas) {// TODO Auto-generated method stub// super.onDraw(canvas); 必须去掉该行或注释掉,否则会出现两张图片// 得到传入的图片Drawable drawable = getDrawable();if (drawable == null) {return;}if (getWidth() == 0 || getHeight() == 0) {return;}this.measure(0, 0);if (drawable.getClass() == NinePatchDrawable.class) {// 如果该传入图片是.9格式的图片return;}// 将图片转为位图Bitmap mBitmap = ((BitmapDrawable) drawable).getBitmap();Bitmap cpBitmap = mBitmap.copy(Bitmap.Config.ARGB_8888, true);// 得到画布宽高width = getWidth();height = getHeight();int radius = 0;//// 判断是否是圆形if ("round".equals(shape_type)) {// 如果内圆边框和外圆边框的颜色不等于默认颜色,则说明该圆有两个边框if (in_border_color != defColor && out_border_color != defColor) {// 计算出半径radius = (width < height ? width : height) / 2 - 2* border_size;// 画内圆边框drawCircleBorder(canvas, radius + border_size / 2,in_border_color);// 画外圆边框drawCircleBorder(canvas,radius + border_size + border_size / 2,out_border_color);}// 如果内圆边框颜色不等于默认颜色,则说明该圆有一个边框else if (in_border_color != defColor&& out_border_color == defColor) {radius = (width < height ? width : height) / 2 - border_size;drawCircleBorder(canvas, radius + border_size / 2,in_border_color);}// 如果外圆边框颜色不等于默认颜色,则说明该圆有一个边框else if (in_border_color == defColor&& out_border_color != defColor) {radius = (width < height ? width : height) / 2 - border_size;drawCircleBorder(canvas, radius + border_size / 2,out_border_color);} else {// 没有边框radius = (width < height ? width : height) / 2;}} else {radius = (width < height ? width : height) / 2;}Bitmap shapeBitmap = drawShapeBitmap(cpBitmap, radius);canvas.drawBitmap(shapeBitmap, width / 2 - radius, height / 2 - radius,null);}/*** 画出指定形状的图片* * @param cpBitmap* @param radius* @return*/private Bitmap drawShapeBitmap(Bitmap bmp, int radius) {// TODO Auto-generated method stubBitmap squareBitmap;// 根据传入的位图截取合适的正方形位图Bitmap scaledBitmap;// 根据diameter对截取的正方形位图进行缩放int diameter = radius * 2;// 传入位图的宽高int w = bmp.getWidth();int h = bmp.getHeight();// 为了防止宽高不相等,造成圆形图片变形,因此截取长方形中处于中间位置最大的正方形图片int squarewidth = 0, squareheight = 0;// 矩形的宽高int x = 0, y = 0;if (h > w) {// 如果高>宽squarewidth = squareheight = w;x = 0;y = (h - w) / 2;// 截取正方形图片squareBitmap = Bitmap.createBitmap(bmp, x, y, squarewidth,squareheight);} else if (h < w) {// 如果宽>高squarewidth = squareheight = h;x = (w - h) / 2;y = 0;squareBitmap = Bitmap.createBitmap(bmp, x, y, squarewidth,squareheight);} else {squareBitmap = bmp;}// 对squareBitmap进行缩放为diameter边长的正方形位图if (squareBitmap.getWidth() != diameter|| squareBitmap.getHeight() != diameter) {scaledBitmap = Bitmap.createScaledBitmap(squareBitmap, diameter,diameter, true);} else {scaledBitmap = squareBitmap;}Bitmap outputbmp = Bitmap.createBitmap(scaledBitmap.getWidth(),scaledBitmap.getHeight(), Config.ARGB_8888);Canvas canvas = new Canvas(outputbmp);// 创建一个相同大小的画布Paint paint = new Paint();// 定义画笔paint.setAntiAlias(true);// 设置抗锯齿paint.setFilterBitmap(true);paint.setDither(true);canvas.drawARGB(0, 0, 0, 0);if ("star".equals(shape_type)) {// 如果绘制的形状为五角星形Path path = new Path();float radian = degree2Radian(36);// 36为五角星的角度float radius_in = (float) (radius * Math.sin(radian / 2) / Math.cos(radian)); // 中间五边形的半径path.moveTo((float) (radius * Math.cos(radian / 2)), 0);// 此点为多边形的起点path.lineTo((float) (radius * Math.cos(radian / 2) + radius_in* Math.sin(radian)),(float) (radius - radius * Math.sin(radian / 2)));path.lineTo((float) (radius * Math.cos(radian / 2) * 2),(float) (radius - radius * Math.sin(radian / 2)));path.lineTo((float) (radius * Math.cos(radian / 2) + radius_in* Math.cos(radian / 2)),(float) (radius + radius_in * Math.sin(radian / 2)));path.lineTo((float) (radius * Math.cos(radian / 2) + radius* Math.sin(radian)), (float) (radius + radius* Math.cos(radian)));path.lineTo((float) (radius * Math.cos(radian / 2)),(float) (radius + radius_in));path.lineTo((float) (radius * Math.cos(radian / 2) - radius* Math.sin(radian)), (float) (radius + radius* Math.cos(radian)));path.lineTo((float) (radius * Math.cos(radian / 2) - radius_in* Math.cos(radian / 2)),(float) (radius + radius_in * Math.sin(radian / 2)));path.lineTo(0, (float) (radius - radius * Math.sin(radian / 2)));path.lineTo((float) (radius * Math.cos(radian / 2) - radius_in* Math.sin(radian)),(float) (radius - radius * Math.sin(radian / 2)));path.close();// 使这些点构成封闭的多边形canvas.drawPath(path, paint);} else if ("triangle".equals(shape_type)) {// 如果绘制的形状为三角形Path path = new Path();path.moveTo(0, 0);path.lineTo(diameter / 2, diameter);path.lineTo(diameter, 0);path.close(); canvas.drawPath(path, paint);} else if ("heart".equals(shape_type)) {// 如果绘制的形状为心形Path path = new Path();path.moveTo(diameter / 2, diameter / 5);path.quadTo(diameter, 0, diameter / 2, diameter / 1.0f);path.quadTo(0, 0, diameter / 2, diameter / 5);path.close();canvas.drawPath(path, paint);} else {// 这是默认形状,圆形// 绘制圆形canvas.drawCircle(scaledBitmap.getWidth() / 2,scaledBitmap.getHeight() / 2, scaledBitmap.getWidth() / 2,paint);}// 设置Xfermode的Modepaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));canvas.drawBitmap(scaledBitmap, 0, 0, paint);bmp = null;squareBitmap = null;scaledBitmap = null;return outputbmp;}/*** 角度转弧度公式* * @param degree* @return*/private float degree2Radian(int degree) {// TODO Auto-generated method stubreturn (float) (Math.PI * degree / 180);}/*** 如果图片为圆形,这该方法为画出圆形图片的有色边框* * @param canvas* @param radius 边框半径* @param color 边框颜色*/private void drawCircleBorder(Canvas canvas, int radius, int color) {// TODO Auto-generated method stubPaint paint = new Paint();paint.setAntiAlias(true);// 抗锯齿paint.setFilterBitmap(true);paint.setDither(true);paint.setColor(color);// 设置画笔颜色paint.setStyle(Paint.Style.STROKE);// 设置画笔的style为STROKE:空心paint.setStrokeWidth(border_size);// 设置画笔的宽度// 画出空心圆,也就是边框canvas.drawCircle(width / 2, height / 2, radius, paint);}}

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

最后附上Activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:myview="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:paddingBottom="@dimen/activity_vertical_margin"android:paddingLeft="@dimen/activity_horizontal_margin"android:paddingRight="@dimen/activity_horizontal_margin"android:paddingTop="@dimen/activity_vertical_margin"android:orientation="vertical"tools:context=".MainActivity" ><!-- 没有设置shape_type,默认为无边框圆形 --><com.example.shapeimageviewdemo.ShapeImageView android:layout_width="60dip"android:layout_height="60dip"android:src="@drawable/girl"/><!-- 令 shape_type="triangle"为三角形--><com.example.shapeimageviewdemo.ShapeImageView android:layout_width="80dip"android:layout_height="80dip"android:layout_marginTop="10dip"android:src="@drawable/girl"myview:shape_type="triangle"/><!-- 令 shape_type="star"为五角星--><com.example.shapeimageviewdemo.ShapeImageView android:layout_width="100dip"android:layout_height="100dip"android:layout_marginTop="10dip"android:src="@drawable/girl"myview:shape_type="star"/><!-- 令 shape_type="round"为圆形,并设置边框厚度和内外边框颜色--><com.example.shapeimageviewdemo.ShapeImageView android:layout_width="100dip"android:layout_height="100dip"android:layout_marginTop="10dip"android:src="@drawable/girl"myview:border_size="2dip"myview:in_border_color="#EE0000"myview:out_border_color="#00EEEE"myview:shape_type="round"/><!-- 令 shape_type="heart"为心形 --><com.example.shapeimageviewdemo.ShapeImageView android:layout_width="100dip"android:layout_height="100dip"android:layout_marginTop="10dip"android:src="@drawable/girl"myview:shape_type="heart"/>
</LinearLayout>

由于用到了自定义属性,因此要在主layout里加上xmlns:myview="http://schemas.android.com/apk/res-auto",否则会报错。

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

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

下面是源码下载地址:

ShapeImageViewDemo

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

  1. 精通Android自定义View(十二)绘制圆形进度条

    1 绘图基础简析 1 精通Android自定义View(一)View的绘制流程简述 2 精通Android自定义View(二)View绘制三部曲 3 精通Android自定义View(三)View绘制 ...

  2. Android自定义View实现三角到八角的属性分布图-雷达图(蜘蛛网图)

    Android自定义View实现三角到八角的属性分布图-雷达图(蜘蛛网图) 前言 自定义View的关键点 绘制多边形 结尾 前言 刚开始学习自定义view,简单完成了一个属性分布器,可以实现三条到八条 ...

  3. android 自定义音乐圆形进度条,Android自定义View实现音频播放圆形进度条

    本篇文章介绍自定义View配合属性动画来实现如下的效果 实现思路如下: 根据播放按钮的图片大小计算出圆形进度条的大小 根据音频的时间长度计算出圆形进度条绘制的弧度 通过Handler刷新界面来更新圆形 ...

  4. 精通Android自定义View(十四)绘制水平向右加载的进度条

    1引言 1 精通Android自定义View(一)View的绘制流程简述 2 精通Android自定义View(二)View绘制三部曲 3 精通Android自定义View(三)View绘制三部曲综合 ...

  5. Android 自定义View 圆形圆角图片

    [Android 自定义View 圆形圆角图片] 基于Xfermode 实现 1.概述 在很久以前也写过一个利用Xfermode 实现圆形.圆角图片的(Android 完美实现图片圆角和圆形(对实现进 ...

  6. android view 渐变动画,Android自定义view渐变圆形动画

    本文实例为大家分享了Android自定义view渐变圆形动画的具体代码,供大家参考,具体内容如下 直接上效果图 自定义属性 attrs.xml文件 创建一个类 ProgressRing继承自 view ...

  7. Android自定义View之画圆环(进阶篇:圆形进度条)

    前言: 如果你想读懂或者更好的理解本篇文章关于自定义圆环或圆弧的内容.请你务必提前阅读下Android自定义View之画圆环(手把手教你如何一步步画圆环).在这篇文章中,详细描述了最基本的自定义圆环的 ...

  8. android自定义View之(六)------高仿华为荣耀3C的圆形刻度比例图(ShowPercentView)

    为什么写这篇文章: 显示当前的容量所占的比例,表现当前计划的进度,一般都会采用百分比的方式,而图形显示,以其一目了然的直观性和赏心悦目的美观形成为了我们的当然的首选. 在图形表示百分比的方法中,我们有 ...

  9. Android自定义view,圆形的TextView,并通过xml设置属性,AttributeSet中取值

    Android自定义view设置xml属性 一个圆形的自定义TextView,通过xml来设置背景颜色的属性 values/attrs <declare-styleable name=" ...

最新文章

  1. 三维点云语义分割总览
  2. 移动Web单页应用开发实践——页面结构化
  3. 将svn设置开机启动linux,ubuntu安装SVN并设置开机启动
  4. jszip压缩服务器文件,使用JSZip压缩驻留在服务器上的PDF
  5. 史上最简单的SpringCloud教程 | 第五篇: 路由网关(zuul)
  6. 操作系统--中断和异常
  7. 阅读笔记-你的灯还亮着吗?
  8. 收藏一下mybatis全局参数配置
  9. 互联网晚报 | 8月12日 星期四 | 苏宁易购零售云将迈入“万店时代”;理想汽车今日港股上市;好未来励步推素质教育新产品...
  10. lighttpd在proxy-core下path_info为空的修复
  11. 在JavaScript中解析查询字符串[重复]
  12. Linux系统磁盘阵列创建教程----------(better late than never. 只要开始,虽晚不迟。)...
  13. 浏览器图标及名称大全_估计真要卸载谷歌浏览器了!自带黑科技浏览器推荐(下)...
  14. win10投影无法正常使用:我们正在确认此功能 解决方法
  15. 机电一体化基础知识及实训QY-JDYT01
  16. r中gglot怎么组合多张图_R语言之可视化①⑧子图组合patchwork包
  17. ZOJ-1005-Jugs
  18. QQ2013协议分析(解密篇)
  19. 打印机打开扫描提示使用该设备需要WIA驱动程序。请从安装CD或从制造商的网站安装此程序,然后重试--------
  20. 近期刷题总结 [19 03 17]

热门文章

  1. 用ASP发送信使服务
  2. linux系统查看电脑设备型号,Linux系统查看硬件信息
  3. c#OleDbConnection 类和DbConnection
  4. CogLTX Applying BERT to Long Texts
  5. MySQL外键设置中的的nbsp;Cascad…
  6. Java内存的玄幻世界观
  7. Android Studio 中Failed to read key from keystore的解决办法
  8. 数字人民币与智能合约
  9. Cookie 与 Session
  10. 虚拟机VMWARE安装win7没有网卡声音等问题解决记录