『自定義View實戰』—— 仿ios圖示下載view

下位子發表於2019-02-20

前言

最近專案需要接入環信客服 SDK ,我配合這同事完成,其中我負責檔案下載這部分。

因為時間比較緊張,8 天的時間完成 環信客服模組 的接入,就直接用了環信提供的 UI 控制元件,但是一些細節的部分, UI 還是會給出設計圖,按照設計圖完成最終效果。

UI那邊直接讓我參考 IOS的實現效果:

UI效果

最終效果

最終效果

原始碼請看 DownloadLoadingView

功能分析

面對這樣的需要應該怎麼實現呢?其實實現的方式可能不止我想的這種,我就講述一下我是如何處理的。

預覽圖

首先,可以分成三部分:

  1. 半透明的背景
  2. 全透明的環
  3. 實心全透明的弧

那怎麼實現背景半透明,而圓環和弧又是全透明的。頓時有個想法,要是兩張圖片重疊的部分能被摳出掉,也就是變成全透明,那豈不是非常容易就實現了。

圓環和弧既然是蓋在了背景上,理當直接變成透明。那 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);
}
複製程式碼

記得需要關閉硬體加速,不然會沒有效果。

繪製背景

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,表示目標圖的顏色.
複製程式碼

來看一下權威的展示圖:

mode預覽圖

我覺得 各個擊破搞明白 PorterDuff.Mode 這篇文章寫的特別好,不是很懂的小夥伴可以看一下,在這裡也表示一下感謝。

原文地址

相關文章