上一节咱们学会了使用Canvas和Paint实现自定义View的绘制,咱们已经知道如何简单实现自定义View的方法。这一节咱们主要来讲说Paint的setXfermode方法,对于Android开发中碰到最可能是实现圆角图片或是圆形图片显示,只要掌握了setXfermode的使用就可以实现咱们想要的图片显示效果。html
想了解Xfermode是什么,固然先去翻翻Android官方开发文档啦。文档中Xfermode提供了三个Subclasses:AvoidXfermode,PixelXorXfermode,PorterDuffXfermode而这节咱们先介绍PorterDuffXfermode,这也是和本节要讲的内容相关。其余的放在下次再讲吧。android
既然要说PorterDuffXfermode,它是实现图形混合模式,当要实现复杂显示多个图形重叠时呈现特别的显示方式PorterDuffXfermode就派上用处。PorterDuffXfermode的构造方法须要传递一个PorterDuff.Mode 参数。再查查文档吧,一看吓一跳,Enum Values很多,一共有18种mode。具体每一种模式的实现效果是什么样的?把代码敲起来就知道结果了啊。canvas
public class PorterDuffView extends View { Paint mPaint; Context mContext; int BlueColor; int PinkColor; int mWith; int mHeight; public PorterDuffView(Context context) { super(context); init(context); } public PorterDuffView(Context context, AttributeSet attrs) { super(context, attrs); init(context); } public PorterDuffView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); mHeight = getMeasuredHeight(); mWith = getMeasuredWidth(); } private void init(Context context) { mContext = context; BlueColor = ContextCompat.getColor(mContext, R.color.colorPrimary); PinkColor = ContextCompat.getColor(mContext, R.color.colorAccent); mPaint = new Paint(); mPaint.setStyle(Paint.Style.FILL); mPaint.setAntiAlias(true); } private Bitmap drawRectBm(){ Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); paint.setColor(BlueColor); paint.setStyle(Paint.Style.FILL); paint.setAntiAlias(true); Bitmap bm = Bitmap.createBitmap(200,200, Bitmap.Config.ARGB_8888); Canvas cavas = new Canvas(bm); cavas.drawRect(new RectF(0,0,70,70),paint); return bm; } private Bitmap drawCircleBm(){ Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); paint.setColor(PinkColor); paint.setStyle(Paint.Style.FILL); paint.setAntiAlias(true); Bitmap bm = Bitmap.createBitmap(200,200, Bitmap.Config.ARGB_8888); Canvas cavas = new Canvas(bm); cavas.drawCircle(70,70,35,paint); return bm; } @Override protected void onDraw(Canvas canvas) { mPaint.setFilterBitmap(false); mPaint.setStyle(Paint.Style.FILL); mPaint.setTextSize(20); RectF recf = new RectF(20,20,60,60); mPaint.setColor(BlueColor); canvas.drawRect(recf,mPaint); mPaint.setColor(PinkColor); canvas.drawCircle(100,40,20,mPaint); int sc = canvas.saveLayer(0, 0,mWith,mHeight, 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); int y = 180; int x = 50; for(PorterDuff.Mode mode : PorterDuff.Mode.values()){ if(y >= 900){ y = 180; x += 200; } mPaint.setXfermode(null); canvas.drawText(mode.name(),x + 100,y,mPaint); canvas.drawBitmap(drawRectBm(),x,y,mPaint); mPaint.setXfermode(new PorterDuffXfermode(mode)); canvas.drawBitmap(drawCircleBm(),x,y,mPaint); y += 120; } mPaint.setXfermode(null); // 还原画布 canvas.restoreToCount(sc); } }
实现效果图,每一个模式的效果一目了然。微信
知道PorterXfermode的使用模式下面咱们实践到实际开发中,咱们来绘制圆形图片和微信聊天图片。在设计自定义ImageView以前先简单回顾一下Canvas的绘制,对于Canvas的onDraw每执行一次就是在屏幕上增长一个Bitmap覆盖在原来的图层上。绘制自定义形状图片是经过将自定义图形图层和图片资源图层的内容使用PorterDuffXfermode绘制合并而成,那在图层之间PorterDuffXfermode绘制以前。须要使用 canvas.saveLayerAlpha将原图层的内容保存,使得图形图层是和图片资源图层进行绘制操做。在图层绘制结束以后再使用canvas.restoreToCount恢复保存的原图层内容。若不使用原图层保存和恢复的方法的状况确定达不到原来所想要的效果,但也能够试试了解canvas的绘制原理,看看结果会是怎样。ide
// public class CircleImgView extends ImageView { private int mWidth; private int mHeight; private Paint mPaint; private Bitmap CircleBitmap; public CircleImgView(Context context) { super(context); } public CircleImgView(Context context, AttributeSet attrs) { super(context, attrs); } public CircleImgView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } private void ImgCircle(){ Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); paint.setAntiAlias(true); paint.setFilterBitmap(true); paint.setColor(Color.GRAY); paint.setStrokeWidth(5); paint.setStyle(Paint.Style.FILL); CircleBitmap = Bitmap.createBitmap(mWidth,mHeight, Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(CircleBitmap); canvas.drawCircle(mWidth/2,mHeight/2,mWidth/2,paint); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); mWidth = getMeasuredWidth(); mHeight = getMeasuredHeight(); mPaint = new Paint(); mPaint.setAntiAlias(true); mPaint.setFilterBitmap(true); ImgCircle(); } @Override protected void onDraw(Canvas canvas) { int count = canvas.saveLayerAlpha(0, 0, mWidth, mHeight, 250, Canvas.HAS_ALPHA_LAYER_SAVE_FLAG); super.onDraw(canvas); mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN)); canvas.drawBitmap(CircleBitmap,0,0, mPaint); canvas.restoreToCount(count); } }
public class ImgCustView extends ImageView{ private int mWidth; private int mHeight; private Paint mPaint; private Bitmap canvasBitmap; public ImgCustView(Context context) { super(context); } public ImgCustView(Context context, AttributeSet attrs) { super(context, attrs); } public ImgCustView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } private void ImgShape(){ Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); paint.setAntiAlias(true); paint.setFilterBitmap(true); paint.setColor(Color.GRAY); paint.setStyle(Paint.Style.FILL); canvasBitmap = Bitmap.createBitmap(mWidth,mHeight, Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(canvasBitmap); Path path = new Path(); path.moveTo(mWidth - 25,50); path.lineTo(mWidth - 25,80); path.lineTo(mWidth,65); path.close(); RectF rectF = new RectF(0,0,mWidth - 25,mHeight); canvas.drawRoundRect(rectF,20,20,paint); canvas.drawPath(path,paint); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); mWidth = getMeasuredWidth(); mHeight = getMeasuredHeight(); mPaint = new Paint(); mPaint.setAntiAlias(true); mPaint.setFilterBitmap(true); ImgShape(); } @Override protected void onDraw(Canvas canvas) { int count = canvas.saveLayerAlpha(0, 0, mWidth, mHeight, 250, Canvas.HAS_ALPHA_LAYER_SAVE_FLAG); super.onDraw(canvas); mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN)); canvas.drawBitmap(canvasBitmap,0,0, mPaint); canvas.restoreToCount(count); } }
public class ImgCustActivity extends BaseActivity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(500,500); layoutParams.setMargins(20,20,20,20); //实例化ImageView并加载图片资源 ImageView imageView = new ImageView(this); imageView.setScaleType(ImageView.ScaleType.FIT_XY); imageView.setImageResource(R.drawable.background); imageView.setLayoutParams(layoutParams); //实例化圆形ImageView并加载图片资源 ImgCustView imgCustView = new ImgCustView(this); imgCustView.setScaleType(ImageView.ScaleType.FIT_XY); imgCustView.setImageResource(R.drawable.background); imgCustView.setLayoutParams(layoutParams); //实例化聊天ImageView并加载图片资源 CircleImgView circleView = new CircleImgView(this); circleView.setScaleType(ImageView.ScaleType.FIT_XY); circleView.setImageResource(R.drawable.background); circleView.setLayoutParams(layoutParams); llContent.setBackgroundColor(Color.LTGRAY); llContent.addView(imageView); llContent.addView(imgCustView); llContent.addView(circleView); } }
最后的效果呈现学习
这节主要学习PorterDuffXfermode模式并经过几个简单例子实践了一下。结合简单例子咱们还能够深刻学习绘制更为复杂有趣的自定义图形。本节只作到了抛砖迎玉的做用,之后我还会结合其余内容绘制更有意思和创造性的图形。以后还会讲讲关于View的动画,下次见 see you!动画