Paint API - Detailed explanation of Xfermode and PorterDuff (3)
Introduction to this section:
In the previous section, we learned about the third son in Xfermode: PorterDuff. There is one parameter in the Xfermode construction method: PorterDuff.Mode , after watching 16 image mixing modes, we wrote our own code to verify the document 18 different mixing modes, 18 of which are newly added ADD and OVERLAY modes! Of course, it is not enough to just verify that you know; In this section, we will write an example to help us familiarize ourselves with how to use PorterDuff.Mode to provide us with it in practice. These shuffle modes! The example brought in this section is: the implementation of circular & rounded corner graphics!
In2.3.4 ImageView(Image View)We finally explained the simplest The implementation of drawing a circular ImageView is to call clipPath on the picture to cut out a circle!
This section is implemented using the DST_IN mode in PorterDuff.Mode. Without further ado, let’s start this section! PS: The examples in this section are taken from Hongyang’s work - Android
#1. Effect diagram to be realized and analysis of the implementation process:
Rendering diagram after operation
:
Well, the above is an effect we want to achieve, through this
PorterDuff.Mode.DST_IN mode! Let’s analyze the implementation process:
- Step 1: Xfermode is nothing more than two layers of graphs. The one drawn first is called the DST graph (target graph), and the one drawn later is called the SRC graph (original graph). We want to achieve For circles or rounded corners, we can first draw the image to be displayed (DST), and here we set it through the src attribute; Then draw the circle and fillet (SRC). The part we want to display is where they intersect, and it is the content of the picture part. So choose: DST_IN mode!
- Step 2: Well, now that we understand the principle, next we have to consider issues related to customizing ImageView:
- If the View we want to draw is rounded or circular, we need to add an attribute to judge, and rounded corners You also need a corner radius parameters, so we can use custom attributes (attrs.xml), and then customize the View's construction method to Take out these parameters!
- Then comes the calculation of the image size: First of allIf we set a circle, we need to make the width and height consistent, subject to the minimum value, we can use the onMesure() method Call getMeasuredXxx() to get the width and height, see which one is smaller, call setMeasuredDimension(x, x); set the width and height! Then, we obtain the image width and height in the onDraw() method, and then calculate the scaling ratio according to the image width and height, and the View width and height, If the width and height of the picture does not match the width and height of the View, the width and height of the resulting picture must be greater than the width and height of the View, so, take the larger value!
- Then comes to the drawing of pictures. Define a method for drawing graphics. Then after initializing the brush, set setXfermode to PorterDuff.Mode.DST_IN, draw the picture first, then draw the graphics
- The last is some things about the image cache. WeakReference is used here to cache the image to avoid allocating memory every time onDraw And redraw, finally clear the cache in invalidate!
The general implementation process is as above. It will be much simpler to look at the code after knowing the process!
2. Code implementation:
Custom control attributes: res/attrs.xml:
<?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="CircleImageView"> <attr name="Radius" format="dimension"/> <attr name="type"> <enum name="circle" value="0"/> <enum name="round" value="1"/> </attr> </declare-styleable> </resources>
Then comes the custom ImageView: CircleImageView.java:
/** * Created by Jay on 2015/10/25 0025. */ public class CircleImageView extends ImageView { private Paint mPaint; private Xfermode mXfermode = new PorterDuffXfermode(PorterDuff.Mode.DST_IN); private Bitmap mMaskBitmap; private WeakReference<Bitmap> mWeakBitmap; //图片相关的属性 private int type; //类型,圆形或者圆角 public static final int TYPE_CIRCLE = 0; public static final int TYPE_ROUND = 1; private static final int BODER_RADIUS_DEFAULT = 10; //圆角默认大小值 private int mBorderRadius; //圆角大小 public CircleImageView(Context context) { this(context, null); } public CircleImageView(Context context, AttributeSet attrs) { super(context, attrs); mPaint = new Paint(); mPaint.setAntiAlias(true); //取出attrs中我们为View设置的相关值 TypedArray tArray = context.obtainStyledAttributes(attrs, R.styleable.CircleImageView); mBorderRadius = tArray.getDimensionPixelSize(R.styleable.CircleImageView_Radius, BODER_RADIUS_DEFAULT); type = tArray.getInt(R.styleable.CircleImageView_type, TYPE_CIRCLE); tArray.recycle(); } public CircleImageView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); if (type == TYPE_CIRCLE) { int width = Math.min(getMeasuredWidth(), getMeasuredHeight()); setMeasuredDimension(width, width); //设置当前View的大小 } } @Override protected void onDraw(Canvas canvas) { //在缓存中取出bitmap Bitmap bitmap = mWeakBitmap == null ? null : mWeakBitmap.get(); if (bitmap == null || bitmap.isRecycled()) { //获取图片宽高 Drawable drawable = getDrawable(); int width = drawable.getIntrinsicWidth(); int height = drawable.getIntrinsicHeight(); if (drawable != null) { bitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888); Canvas drawCanvas = new Canvas(bitmap); float scale = 1.0f; if (type == TYPE_ROUND) { scale = Math.max(getWidth() * 1.0f / width, getHeight() * 1.0f / height); } else { scale = getWidth() * 1.0F / Math.min(width, height); } //根据缩放比例,设置bounds,相当于缩放图片了 drawable.setBounds(0, 0, (int) (scale * width), (int) (scale * height)); drawable.draw(drawCanvas); if (mMaskBitmap == null || mMaskBitmap.isRecycled()) { mMaskBitmap = getBitmap(); } mPaint.reset(); mPaint.setFilterBitmap(false); mPaint.setXfermode(mXfermode); //绘制形状 drawCanvas.drawBitmap(mMaskBitmap, 0, 0, mPaint); //bitmap缓存起来,避免每次调用onDraw,分配内存 mWeakBitmap = new WeakReference<Bitmap>(bitmap); //绘制图片 canvas.drawBitmap(bitmap, 0, 0, null); mPaint.setXfermode(null); } } if (bitmap != null) { mPaint.setXfermode(null); canvas.drawBitmap(bitmap, 0.0f, 0.0f, mPaint); return; } } //缓存Bitmap,避免每次OnDraw都重新分配内存与绘图 @Override public void invalidate() { mWeakBitmap = null; if (mWeakBitmap != null) { mMaskBitmap.recycle(); mMaskBitmap = null; } super.invalidate(); } //定义一个绘制形状的方法 private Bitmap getBitmap() { Bitmap bitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(bitmap); Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); //抗锯齿 paint.setColor(Color.BLACK); if (type == TYPE_ROUND) { canvas.drawRoundRect(new RectF(0, 0, getWidth(), getHeight()), mBorderRadius, mBorderRadius, paint); } else { canvas.drawCircle(getWidth() / 2, getWidth() / 2, getWidth() / 2, paint); } return bitmap; } }
Finally call it in the layout file:activity_main.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <com.jay.xfermodedemo1.CircleImageView android:layout_width="160dp" android:layout_height="240dp" android:layout_margin="10dp" android:src="@mipmap/ic_bg_meizi2" app:type="circle" /> <com.jay.xfermodedemo1.CircleImageView android:layout_width="160dp" android:layout_height="280dp" android:layout_margin="10dp" android:src="@mipmap/ic_bg_meizi1" app:Radius="30dp" app:type="round" /> </LinearLayout>
Okay, I can’t understand the code at once , you will understand after reading it twice~
3. Download the code example of this section:
Summary of this section:
In this section we explained the first application example of Xfermode and PorterDuff, setting the DST_IN mode to achieve Customization of round and rounded image ImageView, I believe everyone is already familiar with the simple application of PorterDuff. Strike while the iron is hot. In the next section, we will write an example to practice our skills~ Okay, that’s all, thank you~