## 前言git
最近項目須要接入環信客服 SDK
,我配合這同事完成,其中我負責文件下載這部分。github
由於時間比較緊張,8 天的時間完成 環信客服模塊 的接入,就直接用了環信提供的 UI
控件,可是一些細節的部分, UI
仍是會給出設計圖,按照設計圖完成最終效果。canvas
UI
那邊直接讓我參考 IOS
的實現效果:ide
源碼請看 DownloadLoadingViewthis
面對這樣的須要應該怎麼實現呢?其實實現的方式可能不止我想的這種,我就講述一下我是如何處理的。spa
首先,能夠分紅三部分:設計
那怎麼實現背景半透明,而圓環和弧又是全透明的。頓時有個想法,要是兩張圖片重疊的部分能被摳出掉,也就是變成全透明,那豈不是很是容易就實現了。code
圓環和弧既然是蓋在了背景上,理當直接變成透明。那 Android
有對應處理的 API
嗎?答案是確定的。 setXfermode() 用於設置圖像的過分模式,其中 PorterDuff.Mode.CLEAR 爲清除模式則能夠實現上述的效果。圖片
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);//關閉硬件加速 paint = new Paint(Paint.ANTI_ALIAS_FLAG); }
記得須要關閉硬件加速,否則會沒有效果。get
paint.setColor(mBackgroundColor); paint.setStyle(Paint.Style.FILL); RectF round = new RectF(0, 0, getWidth(), getHeight()); canvas.drawRoundRect(round, mRoundRadius, mRoundRadius, paint);
設置背景顏色,樣式爲填充,繪製圓角矩形
paint.setColor(Color.RED); paint.setStrokeWidth(mStrokeWidth); // 採用 clear 的方式 paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); paint.setStyle(Paint.Style.STROKE); canvas.drawCircle(getWidth() / 2, getHeight() / 2, mRadius, paint);
圓環的顏色能夠隨意設置,畢竟最後會被摳除掉,
設置
PorterDuff.Mode.CLEAR
類型模式繪製圓環
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); // 記得設置爲 null 否則會沒有效果 paint.setXfermode(null);
根據當前的進度繪製相對應的弧,而且結束的時候將
Xfermode
模式置爲null
。
這樣效果就結束了,賊簡單。完整的代碼請看 DownloadLoadingView
文中提到了 PorterDuff.Mode
,裏面存儲了大量的枚舉,當咱們須要處理圖像的時候就會用到,可是對每種類型並無特別的瞭解。每次使用的時候都須要查資料,而後肯定到底須要使用哪一種模式。
public Xfermode setXfermode(Xfermode xfermode) { long xfermodeNative = 0; if (xfermode != null) xfermodeNative = xfermode.native_instance; native_setXfermode(mNativePaint, xfermodeNative); mXfermode = xfermode; return xfermode;。 }
具體的模式:
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; }
註釋中已經說明了該模式到的透明度計算和顏色的計算方式,首先咱們要了解一下基本的概念:
Sa:全稱爲Source alpha,表示源圖的Alpha通道; Sc:全稱爲Source color,表示源圖的顏色; Da:全稱爲Destination alpha,表示目標圖的Alpha通道; Dc:全稱爲Destination color,表示目標圖的顏色.
來看一下權威的展現圖:
我以爲 各個擊破搞明白 PorterDuff.Mode 這篇文章寫的特別好,不是很懂的小夥伴能夠看一下,在這裏也表示一下感謝。