preface
Recently, the project needs to access the SDK of Huanxin customer service. I cooperated with this colleague to complete the project, and I was responsible for the file download part.
Due to the limited time, it took 8 days to complete the access of the customer service module of Huanxin, so WE directly used the UI control provided by Huanxin. However, for some details, the UI still provided the design drawing, and the final effect was completed according to the design drawing.
The UI side directly referred me to the implementation of IOS:
The final result
See DownloadLoadingView for the source code
Functional analysis
How should such a need be realized? There’s probably more to it than I think, but I’m going to tell you how I did it.
First, it can be divided into three parts:
- Translucent background
- Fully transparent ring
- Solid fully transparent arc
How do you make the background translucent, while the rings and arcs are completely transparent? It suddenly occurred to me that if the overlap between the two images could be cut out, that is, become fully transparent, it would be very easy to achieve.
Since the rings and arcs are overlaid on the background, they should become transparent. Does Android have an API for that? The answer is yes. SetXfermode () is used to set the transition Mode of the image, where porterduff.mode. CLEAR is the CLEAR Mode to achieve the above effect.
The specific implementation
A series of initializations
public DownloadLoadingView(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.DownloadLoadingView);
mRadius = typedArray.getDimension(R.styleable.DownloadLoadingView_radius, RADIUS_DEFAULT);
mStrokeWidth = typedArray.getDimension(R.styleable.DownloadLoadingView_strokeWidth, STROKE_WIDTH_DEFAULT);
mMaxProgress = typedArray.getInteger(R.styleable.DownloadLoadingView_maxProgress, MAX_PROGRESS_DEFAULT);
mRoundRadius = typedArray.getDimension(R.styleable.DownloadLoadingView_roundRadius, ROUND_RADIUS_DEFAULT);
mBackgroundColor = typedArray.getColor(R.styleable.DownloadLoadingView_backgroundColor, getResources().getColor(R.color.bg_default));
Log.i(TAG, "radius:" + mRadius);
typedArray.recycle();
setLayerType(View.LAYER_TYPE_SOFTWARE, null);// Turn off hardware acceleration
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
}
Copy the code
Remember to turn off hardware acceleration, otherwise it won’t work.
Draw the background
paint.setColor(mBackgroundColor);
paint.setStyle(Paint.Style.FILL);
RectF round = new RectF(0.0, getWidth(), getHeight());
canvas.drawRoundRect(round, mRoundRadius, mRoundRadius, paint);
Copy the code
Set the background color to Fill and draw a rounded rectangle
Draw the circle
paint.setColor(Color.RED);
paint.setStrokeWidth(mStrokeWidth);
// Use the clear method
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
paint.setStyle(Paint.Style.STROKE);
canvas.drawCircle(getWidth() / 2, getHeight() / 2, mRadius, paint);
Copy the code
The color of the ring can be set arbitrarily, because eventually it will be cut out,
Set the porterduff.mode. CLEAR Mode to draw a ring
Draw the circle
paint.setStyle(Paint.Style.FILL);
float sweepAngle = 360 * mProgress / mMaxProgress;
RectF rectF = new RectF(getWidth() / 2 - mRadius, getHeight() / 2 - mRadius, getWidth() / 2 + mRadius, getHeight() / 2 + mRadius);
canvas.drawArc(rectF, -90, sweepAngle, true, paint);
// Remember to set it to null otherwise it will have no effect
paint.setXfermode(null);
Copy the code
Draw the corresponding arcs based on the current progress and set the Xfermode mode to NULL at the end.
That’s the end of the effect. Easy. See DownloadLoadingView for the complete code
expand
Porterduff.mode is mentioned, which stores a large number of enumerations that we use when we need to process images, but we don’t have a specific understanding of each type. Each time you use it, you need to look it up and decide which mode you want to use.
public Xfermode setXfermode(Xfermode xfermode) {
long xfermodeNative = 0;
if(xfermode ! =null)
xfermodeNative = xfermode.native_instance;
native_setXfermode(mNativePaint, xfermodeNative);
mXfermode = xfermode;
returnxfermode; . }Copy the code
Specific patterns:
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 (12),
/** [Sa + Da - Sa*Da, Sc*(1 - Da) + Dc*(1 - Sa) + max(Sc, Dc)] */
LIGHTEN (13),
/** [Sa * Da, Sc * Dc] */
MULTIPLY (14),
/** [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] */
SCREEN (15),
/** Saturate(S + D) */
ADD (16),
OVERLAY (17);
Mode(int nativeInt) {
this.nativeInt = nativeInt;
}
/ * * *@hide* /
public final int nativeInt;
}
Copy the code
The transparency and color calculations are explained in the comments, but first we need to understand the basic concepts:
Sa: Full name Source alpha, indicating the alpha channel of the Source graph; Sc: full name Source color, denoting the color of the Source image; Da: Full name Destination alpha, represents the alpha channel of the target graph; Dc: Destination color, the color of the target graph.Copy the code
Take a look at the definitive illustration:
I think the article porterduff. Mode is written very well. If you don’t understand it very well, you can have a look.
The original address