Android圆形头像的绘制(一)之绘制的几种方法
2016-06-28 10:42
417 查看
现在很多应用都有涉及到自定义图像,现在我们看下圆形图像是怎么绘制的。在绘制之前,我们需要对PorterDuff.Mode进行初步的了解,后面圆形图像的绘制会用到该方法,看下PorterDuff.Mode定义的枚举参数
其中 Sa 代表source alpha ,即源 alpha 值 ,Da 代表 Destination alpha ,即 目标alpha值 ,Sc 代表 source color ,即源色值 ,Dc 代表 Destination color ,即目标色值,并且这所有的计算都以像素为单位,在某一种混合模式下,对每一个像素的alpha
和 color 通过对应算法进行运算,得出新的像素值。
ProterDuff.Mode16个枚举值代表的意思如下:
PorterDuff.Mode.CLEAR 所绘制不会提交到画布上。
PorterDuff.Mode.SRC 显示上层绘制图片
PorterDuff.Mode.DST 显示下层绘制图片
PorterDuff.Mode.SRC_OVER 正常绘制显示,上下层绘制叠盖。
PorterDuff.Mode.DST_OVER 上下层都显示。下层居上显示。
PorterDuff.Mode.SRC_IN 取两层绘制交集。显示上层。
PorterDuff.Mode.DST_IN 取两层绘制交集。显示下层。
PorterDuff.Mode.SRC_OUT 取上层绘制非交集部分。
PorterDuff.Mode.DST_OUT 取下层绘制非交集部分。
PorterDuff.Mode.SRC_ATOP 取下层非交集部分与上层交集部分
PorterDuff.Mode.DST_ATOP 取上层非交集部分与下层交集部分
PorterDuff.Mode.XOR 异或:去除两图层交集部分
PorterDuff.Mode.DARKEN 取两图层全部区域,交集部分颜色加深
PorterDuff.Mode.LIGHTEN 取两图层全部,点亮交集部分颜色
PorterDuff.Mode.MULTIPLY 取两图层交集部分叠加后颜色
PorterDuff.Mode.SCREEN 取两图层全部区域,交集部分变为透明色
看下16个枚举值展示的效果图:
实现的代码如下(代码来自ApiDemos/Graphics/XferModes):
从上面的效果图可以知道,假设创建一个矩形的bitmap(这里用A表示)和一个圆形的bitmap(这里用B表示),并且让B在A里居中展示,在调用PorterDuff.Mode.SRC_IN或者PorterDuff.Mode.DST_IN,合并后的图像就是我们想要的圆形图像,下面我们按照这样的思路,用代码进行实现,我们先创建一个Activity,在自定义一个View,并将
View设置在setContentView里,代码如下:
除上面两张方法实现圆形图像外,还有一种方法,记得上篇文章Android绘制之Canvas中的clipPath方法么?裁剪出一个圆形的canvas,在把bitmap画在canvas上,这样也能到达我们想要的效果,代码如下:
运行代码,效果图如下:
源码链接
public enum Mode { /** [0, 0] */ CLEAR (0), /** [Sa, Sc] */ SRC (1), /** [Da, Dc] */ DST (2), /** [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] */ SRC_OVER (3), /** [Sa + (1 - Sa)*Da, Rc = Dc + (1 - Da)*Sc] */ DST_OVER (4), /** [Sa * Da, Sc * Da] */ SRC_IN (5), /** [Sa * Da, Sa * Dc] */ DST_IN (6), /** [Sa * (1 - Da), Sc * (1 - Da)] */ SRC_OUT (7), /** [Da * (1 - Sa), Dc * (1 - Sa)] */ DST_OUT (8), /** [Da, Sc * Da + (1 - Sa) * Dc] */ SRC_ATOP (9), /** [Sa, Sa * Dc + Sc * (1 - Da)] */ DST_ATOP (10), /** [Sa + Da - 2 * Sa * Da, Sc * (1 - Da) + (1 - Sa) * Dc] */ XOR (11), /** [Sa + Da - Sa*Da, Sc*(1 - Da) + Dc*(1 - Sa) + min(Sc, Dc)] */ DARKEN (16), /** [Sa + Da - Sa*Da, Sc*(1 - Da) + Dc*(1 - Sa) + max(Sc, Dc)] */ LIGHTEN (17), /** [Sa * Da, Sc * Dc] */ MULTIPLY (13), /** [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] */ SCREEN (14), /** Saturate(S + D) */ ADD (12), OVERLAY (15); }
其中 Sa 代表source alpha ,即源 alpha 值 ,Da 代表 Destination alpha ,即 目标alpha值 ,Sc 代表 source color ,即源色值 ,Dc 代表 Destination color ,即目标色值,并且这所有的计算都以像素为单位,在某一种混合模式下,对每一个像素的alpha
和 color 通过对应算法进行运算,得出新的像素值。
ProterDuff.Mode16个枚举值代表的意思如下:
PorterDuff.Mode.CLEAR 所绘制不会提交到画布上。
PorterDuff.Mode.SRC 显示上层绘制图片
PorterDuff.Mode.DST 显示下层绘制图片
PorterDuff.Mode.SRC_OVER 正常绘制显示,上下层绘制叠盖。
PorterDuff.Mode.DST_OVER 上下层都显示。下层居上显示。
PorterDuff.Mode.SRC_IN 取两层绘制交集。显示上层。
PorterDuff.Mode.DST_IN 取两层绘制交集。显示下层。
PorterDuff.Mode.SRC_OUT 取上层绘制非交集部分。
PorterDuff.Mode.DST_OUT 取下层绘制非交集部分。
PorterDuff.Mode.SRC_ATOP 取下层非交集部分与上层交集部分
PorterDuff.Mode.DST_ATOP 取上层非交集部分与下层交集部分
PorterDuff.Mode.XOR 异或:去除两图层交集部分
PorterDuff.Mode.DARKEN 取两图层全部区域,交集部分颜色加深
PorterDuff.Mode.LIGHTEN 取两图层全部,点亮交集部分颜色
PorterDuff.Mode.MULTIPLY 取两图层交集部分叠加后颜色
PorterDuff.Mode.SCREEN 取两图层全部区域,交集部分变为透明色
看下16个枚举值展示的效果图:
实现的代码如下(代码来自ApiDemos/Graphics/XferModes):
package com.dylan.porterduffdemo; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.BitmapShader; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.graphics.RectF; import android.graphics.Shader; import android.graphics.Xfermode; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.view.View; public class MainActivity extends AppCompatActivity { static Bitmap makeDst(int w, int h) { Bitmap bm = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888); Canvas c = new Canvas(bm); Paint p = new Paint(Paint.ANTI_ALIAS_FLAG); p.setColor(0xFFFFCC44); c.drawOval(new RectF(0, 0, w * 3 / 4, h * 3 / 4), p); return bm; } // create a bitmap with a rect, used for the "src" image static Bitmap makeSrc(int w, int h) { Bitmap bm = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888); Canvas c = new Canvas(bm); Paint p = new Paint(Paint.ANTI_ALIAS_FLAG); p.setColor(0xFF66AAFF); c.drawRect(w / 3, h / 3, w * 19 / 20, h * 19 / 20, p); return bm; } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(new SampleView(this)); } private static class SampleView extends View { private static final int W = 192; private static final int H = 192; private static final int ROW_MAX = 4; // number of samples per row private Bitmap mSrcB; private Bitmap mDstB; private Shader mBG; // background checker-board pattern private static final Xfermode[] sModes = { new PorterDuffXfermode(PorterDuff.Mode.CLEAR), new PorterDuffXfermode(PorterDuff.Mode.SRC), new PorterDuffXfermode(PorterDuff.Mode.DST), new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER), new PorterDuffXfermode(PorterDuff.Mode.DST_OVER), new PorterDuffXfermode(PorterDuff.Mode.SRC_IN), new PorterDuffXfermode(PorterDuff.Mode.DST_IN), new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT), new PorterDuffXfermode(PorterDuff.Mode.DST_OUT), new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP), new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP), new PorterDuffXfermode(PorterDuff.Mode.XOR), new PorterDuffXfermode(PorterDuff.Mode.DARKEN), new PorterDuffXfermode(PorterDuff.Mode.LIGHTEN), new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY), new PorterDuffXfermode(PorterDuff.Mode.SCREEN) }; private static final String[] sLabels = { "Clear", "Src", "Dst", "SrcOver", "DstOver", "SrcIn", "DstIn", "SrcOut", "DstOut", "SrcATop", "DstATop", "Xor", "Darken", "Lighten", "Multiply", "Screen" }; public SampleView(Context context) { super(context); mSrcB = makeSrc(W, H); mDstB = makeDst(W, H); // make a ckeckerboard pattern Bitmap bm = Bitmap.createBitmap(new int[]{0xFFFFFFFF, 0xFFCCCCCC, 0xFFCCCCCC, 0xFFFFFFFF}, 2, 2, Bitmap.Config.RGB_565); mBG = new BitmapShader(bm, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT); Matrix m = new Matrix(); m.setScale(6, 6); mBG.setLocalMatrix(m); } @Override protected void onDraw(Canvas canvas) { canvas.drawColor(Color.WHITE); Paint labelP = new Paint(Paint.ANTI_ALIAS_FLAG); labelP.setTextAlign(Paint.Align.CENTER); Paint paint = new Paint(); paint.setFilterBitmap(false); canvas.translate(15, 35); int x = 0; int y = 0; for (int i = 0; i < sModes.length; i++) { // draw the border paint.setStyle(Paint.Style.STROKE); paint.setShader(null); canvas.drawRect(x - 0.5f, y - 0.5f, x + W + 0.5f, y + H + 0.5f, paint); // draw the checker-board pattern paint.setStyle(Paint.Style.FILL); paint.setShader(mBG); canvas.drawRect(x, y, x + W, y + H, paint); // draw the src/dst example into our offscreen bitmap int sc = canvas.saveLayer(x, y, x + W, y + H, null, Canvas.MATRIX_SAVE_FLAG | Canvas.CLIP_SAVE_FLAG | Canvas.HAS_ALPHA_LAYER_SAVE_FLAG | Canvas.FULL_COLOR_LAYER_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG); canvas.translate(x, y); canvas.drawBitmap(mDstB, 0, 0, paint); paint.setXfermode(sModes[i]); canvas.drawBitmap(mSrcB, 0, 0, paint); paint.setXfermode(null); canvas.restoreToCount(sc); // draw the label canvas.drawText(sLabels[i], x + W / 2, y - labelP.getTextSize() / 2, labelP); x += W + 10; // wrap around when we've drawn enough for one row if ((i % ROW_MAX) == ROW_MAX - 1) { x = 0; y += H + 30; } } } } }
从上面的效果图可以知道,假设创建一个矩形的bitmap(这里用A表示)和一个圆形的bitmap(这里用B表示),并且让B在A里居中展示,在调用PorterDuff.Mode.SRC_IN或者PorterDuff.Mode.DST_IN,合并后的图像就是我们想要的圆形图像,下面我们按照这样的思路,用代码进行实现,我们先创建一个Activity,在自定义一个View,并将
View设置在setContentView里,代码如下:
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); CircleImageView circleImageView = new CircleImageView(this); setContentView(circleImageView); } private class CircleImageView extends ImageView { public CircleImageView(Context context) { this(context, null); } public CircleImageView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public CircleImageView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } } }运行代码,出来的界面是空白的,因为没有实现CircleImageView里的onDraw方法,下面我们实现onDraw方法,在实现onDraw方法之前,需要在CircleImageView的构造函数里进行初始化操作,获取将要绘制图片的bitmap,代码如下:
private void init() { mSrcBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.test); }用PorterDuff.Mode.SRC_IN实现onDraw方法,代码如下:
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); Bitmap bitmap = createCirclrBitmapForSRC_IN(); if (bitmap != null) { canvas.drawBitmap(bitmap, 0, 0, new Paint()); } }
private Bitmap createCirclrBitmapForSRC_IN() { if (mSrcBitmap != null) { int width = mSrcBitmap.getWidth(); int height = mSrcBitmap.getHeight(); if (width > 0 && height > 0) { //创建一个和图片大小差不多的正方形矩阵 int size = Math.min(width, height); Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(bitmap); Paint paint = new Paint(); int center = size / 2; //在矩阵中心画圆,与矩阵的四边相切 canvas.drawCircle(center, center, center, paint); //设置Xfermode为SRC_IN paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); //绘制图片 canvas.drawBitmap(mSrcBitmap, 0, 0, paint); return bitmap; } } return null; }用PorterDuff.Mode.DST_IN实现onDraw方法,代码如下:
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); Bitmap bitmap = createCirclrBitmapForDST_IN(); if (bitmap != null) { canvas.drawBitmap(bitmap, 0, 0, new Paint()); } }
<span style="white-space:pre"> </span>private Bitmap createCirclrBitmapForDST_IN() { if (mSrcBitmap != null) { int width = mSrcBitmap.getWidth(); int height = mSrcBitmap.getHeight(); if (width > 0 && height > 0) { //创建一个和图片大小差不多的正方形矩阵 int size = Math.min(width, height); Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(bitmap); Paint paint = new Paint(); //绘制图片 canvas.drawBitmap(mSrcBitmap, 0, 0, paint); //设置Xfermode为SRC_IN paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN)); //创建圆形bitmap, b222 并画在canvas上 canvas.drawBitmap(createDstBitmap(size), 0, 0, paint); return bitmap; } } return null; }</span>
private Bitmap createDstBitmap(int size) { Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(bitmap); int center = size / 2; Paint paint = new Paint(); paint.setColor(Color.WHITE); canvas.drawCircle(center, center, center, paint); return bitmap; }</span>运行代码,两个方法的效果图如下:
除上面两张方法实现圆形图像外,还有一种方法,记得上篇文章Android绘制之Canvas中的clipPath方法么?裁剪出一个圆形的canvas,在把bitmap画在canvas上,这样也能到达我们想要的效果,代码如下:
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); Bitmap bitmap = createCirclrBitmapForClip(); if (bitmap != null) { canvas.drawBitmap(bitmap, 0, 0, new Paint()); } }
<span style="white-space:pre"> </span>private Bitmap createCirclrBitmapForClip() { if (mSrcBitmap != null) { int width = mSrcBitmap.getWidth(); int height = mSrcBitmap.getHeight(); if (width > 0 && height > 0) { int size = Math.min(width, height); Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(bitmap); clipCirclePath(canvas, size); Paint paint = new Paint(); canvas.drawBitmap(mSrcBitmap, 0, 0, paint); return bitmap; } } return null; }</span>
private void clipCirclePath(Canvas canvas, int size) { int center = size / 2; Path path = new Path(); path.addCircle(center, center, center, Path.Direction.CW); canvas.clipPath(path); }
运行代码,效果图如下:
源码链接
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- android:installLocation简析
- android searchView的关闭事件
- SourceProvider.getJniDirectories