我们的设计要求聊天软件发图片做成微信那样,把图切成泡泡的形状,之前只用过circleimage,也就是切四个圆角,但是类似于气泡这种东西的那个小尾巴确实用函数画起来很困难,只好作罢,今天看到一开源代码有这个,于是着急做了个demo,分享下。
前段时间有人问我关于怎么绘制不规则图形的问题。比如,如何像whatsApp那样绘制的聊天气泡图形。在这个系列文章中我们主要来关注一下如何实现不规则图形效果。
在开始之前,我必须承认我忽略了问我这个问题的这个人。如果您正在阅读这篇文章,那么请与我联系,我会为你记上一功。感谢您激发我写这一系列文章的灵感。
我们先从一个简单的不规则图形开始:一个带有圆角的矩形。有必要指出的是,圆角可以使用RoundRectShape Drawable实现(在API2.0里引入的)。但是,它不能实现之后我们要制作的所有效果,所以我们还是坚持使用自己的方法。
这里要使用的第一种技术就是我们在Styling Android介绍过的Dynamic Icons——使用Alpha遮罩层实现。
概念很简单,这里需要两张图片:第一张是我们需要显示在矩形中的图片,第二章是需要定义的矩形。然后我们使用一个Porter-Duff Alpha模式来进行图片结合。这个模式可以让我们从遮罩层中使用到透明像素的信息。
这是我们需要使用到的图片:
第一张是Betty,这篇文章的模特。第二张是定义圆角的遮罩层,这图片用绿色的原因是要保证遮罩层中的颜色不会被使用到(我们可不希望在转换的过程中让Betty变成绿色)。
代码很简单:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
public
Bitmap combineImages(Bitmap bgd, Bitmap fg) {
Bitmap bmp;
int
width = bgd.getWidth() > fg.getWidth() ?
bgd.getWidth() : fg.getWidth();
int
height = bgd.getHeight() > fg.getHeight() ?
bgd.getHeight() : fg.getHeight();
bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Paint paint =
new
Paint();
paint.setXfermode(
new
PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP));
Canvas canvas =
new
Canvas(bmp);
canvas.drawBitmap(bgd,
0
,
0
,
null
);
canvas.drawBitmap(fg,
0
,
0
, paint);
return
bmp;
}
|
现在我们就创建了一张匹配了原图和遮罩层的新图片。首先就先绘制遮罩层图片,然后就把可爱的Betty绘制上去,使用一个带有SRC_ATOP的PorterDuffXFerMode模式的paint对象。这样就可以使用背景遮罩层的透明像素,进而就可以准确绘制原图了。结果就像这样:
看起来非常不错,我们可以实现预期的结果了,但是这个方法还有一些问题。
首先,遮罩层的密度必须和原图一样,这点我们其实也可以对遮罩层进行缩放处理,但是缩放过度的话也会出现问题的。还有就是,如果原图和遮罩层的图比例不一样的话圆角也会出现变形。
还有一个大问题是,这样不够高效。为了实现这个效果,我们需要载入两张图片到内存中,然后结合成一张新的图片。如果图片很大的话,这样我们就有出现OutOfMemoryError的危险。
虽然这种技术还存在一些问题(比如不规则图形太复杂的话,用这种技术就有些困难了),但是还有会有解决方案的。这就是我们后面要说的,一种更高效的方式。我们在下一篇文章再见。
这篇文章的所有代码在这里。
上面的博客是照抄别人的,自己又写了demo,把思路整理了下,用到了一个关键的BubbleImageHelper类,大家可以参考下
1 package com.example.myimage; 2 3 import android.content.Context; 4 import android.graphics.Bitmap; 5 import android.graphics.BitmapFactory; 6 import android.graphics.Canvas; 7 import android.graphics.Matrix; 8 import android.graphics.Paint; 9 import android.graphics.PorterDuff; 10 import android.graphics.PorterDuffXfermode; 11 import android.graphics.Rect; 12 import android.graphics.Bitmap.Config; 13 14 public class BubbleImageHelper { 15 private Context context = null; 16 private static BubbleImageHelper instance = null; 17 18 public static synchronized BubbleImageHelper getInstance(Context c) { 19 if (null == instance) { 20 instance = new BubbleImageHelper(c); 21 } 22 return instance; 23 } 24 25 private BubbleImageHelper(Context c) { 26 context = c; 27 } 28 29 private Bitmap getScaleImage(Bitmap bitmap, float width, float height) { 30 if (null == bitmap || width < 0.0f || height < 0.0f) { 31 return null; 32 } 33 Matrix matrix = new Matrix(); 34 float scaleWidth = width / bitmap.getWidth(); 35 float scaleHeight = height / bitmap.getHeight(); 36 matrix.postScale(scaleWidth, scaleHeight); 37 Bitmap resizeBmp = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), 38 bitmap.getHeight(), matrix, true); 39 return resizeBmp; 40 } 41 42 public Bitmap getBubbleImageBitmap(Bitmap srcBitmap, 43 int backgroundResourceID) { 44 if (null == srcBitmap) { 45 return null; 46 } 47 Bitmap background = null; 48 background = BitmapFactory.decodeResource(context.getResources(), 49 backgroundResourceID); 50 if (null == background) { 51 return null; 52 } 53 54 Bitmap mask = null; 55 Bitmap newBitmap = null; 56 mask = srcBitmap; 57 58 float srcWidth = (float) srcBitmap.getWidth(); 59 float srcHeight = (float) srcBitmap.getHeight(); 60 if (srcWidth < 300f && srcHeight < 500f) { 61 srcWidth = 300f; 62 srcHeight = (float) 500f; 63 Bitmap tmp = getScaleImage(background, srcWidth, srcHeight); 64 if (null != tmp) { 65 background = tmp; 66 } else { 67 tmp = getScaleImage(srcBitmap, (float) 100f, (float) 100f); 68 if (null != tmp) { 69 mask = tmp; 70 } 71 } 72 } 73 74 Config config = background.getConfig(); 75 if (null == config) { 76 config = Bitmap.Config.ARGB_8888; 77 } 78 79 newBitmap = Bitmap.createBitmap(background.getWidth(), 80 background.getHeight(), config); 81 Canvas newCanvas = new Canvas(newBitmap); 82 83 newCanvas.drawBitmap(background, 0, 0, null); 84 85 Paint paint = new Paint(); 86 87 paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP)); 88 89 int left = 0; 90 int top = 0; 91 int right = mask.getWidth(); 92 int bottom = mask.getHeight(); 93 if (mask.getWidth() > background.getWidth()) { 94 left = (mask.getWidth() - background.getWidth()) / 2; 95 right = mask.getWidth() - left; 96 } 97 98 if (mask.getHeight() > background.getHeight()) { 99 top = (mask.getHeight() - background.getHeight()) / 2; 100 bottom = mask.getHeight() - top; 101 } 102 103 newCanvas.drawBitmap(mask, new Rect(left, top, right, bottom), 104 new Rect(0, 0, background.getWidth(), background.getHeight()), 105 paint); 106 107 return newBitmap; 108 } 109 }
封装好了,使用起来就很简单,如下
package com.example.myimage; import android.app.Activity; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.Bundle; import android.view.Menu; import android.view.MenuItem; import android.widget.ImageView; public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Bitmap bitmap = null; bitmap = BubbleImageHelper.getInstance(getApplicationContext()) .getBubbleImageBitmap( BitmapFactory.decodeResource(getApplicationContext() .getResources(), R.drawable.mask), R.drawable.bg); if (null != bitmap) { ((ImageView) findViewById(R.id.imageView1)).setImageBitmap(bitmap); } } }
源码下载地址 http://download.csdn.net/detail/hongheqq/8198099