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

## 前言git

最近項目須要接入環信客服 SDK ,我配合這同事完成,其中我負責文件下載這部分。github

由於時間比較緊張,8 天的時間完成 環信客服模塊 的接入,就直接用了環信提供的 UI 控件,可是一些細節的部分, UI 仍是會給出設計圖,按照設計圖完成最終效果。canvas

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

UI效果

最終效果

最終效果

源碼請看 DownloadLoadingViewthis

功能分析

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

預覽圖

首先,能夠分紅三部分:設計

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

那怎麼實現背景半透明,而圓環和弧又是全透明的。頓時有個想法,要是兩張圖片重疊的部分能被摳出掉,也就是變成全透明,那豈不是很是容易就實現了。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);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

記得須要關閉硬件加速,否則會沒有效果。get

繪製背景

paint.setColor(mBackgroundColor);
paint.setStyle(Paint.Style.FILL);
RectF round = new RectF(0, 0, getWidth(), getHeight());
canvas.drawRoundRect(round, mRoundRadius, mRoundRadius, paint);
  • 1
  • 2
  • 3
  • 4

設置背景顏色,樣式爲填充,繪製圓角矩形

繪製圓環

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);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

圓環的顏色能夠隨意設置,畢竟最後會被摳除掉,

設置 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);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

根據當前的進度繪製相對應的弧,而且結束的時候將 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;。
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

具體的模式:

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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44

註釋中已經說明了該模式到的透明度計算和顏色的計算方式,首先咱們要了解一下基本的概念:

Sa:全稱爲Source alpha,表示源圖的Alpha通道;
Sc:全稱爲Source color,表示源圖的顏色;
Da:全稱爲Destination alpha,表示目標圖的Alpha通道;
Dc:全稱爲Destination color,表示目標圖的顏色.
  • 1
  • 2
  • 3
  • 4

來看一下權威的展現圖:

mode預覽圖

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

原文地址

相關文章
相關標籤/搜索