Orient-Ui | Canvas.clipRect打造的炫酷Switch

上週同事問有沒有多個Item的Switch控件,我想這也不是什麼難事,這麼多第三方庫,直接挑一個就行。找了半天,雖然說大部分Switch都很炫,不過都只支持兩個Item,惟一找到的支持多Item的,可是是用在ios的,如圖: java

ios demo
自定義Switch倒也不是什麼難事,這個圖裏面惟一的難點也就是滑塊懸浮在文字上方的時候文字會變色,不過一點難度都沒有確實也沒什麼意思,這是我實現的版本:
MultiSwitch

能夠從圖中看出,特色是:android

  • 支持多個Item
  • 支持文字和圖標
  • 形狀能夠多選擇
  • 滑塊滑動的時候覆蓋的文字和圖標會變色

Github地址:github.com/mCyp/Orient…ios

1、思路

製做這個Switch挺簡單,過程是:git

  1. 繪製底層的背景
  2. 繪製底層的文字或者Icon
  3. 繪製滑塊
  4. 滑塊若是覆蓋到了文字或者Icon,就繪製滑塊覆蓋到的文字或者Icon

這也就是本Switch的難點了,如何繪製部分文字和圖標呢,答案就見標題了,使用畫布裁剪的方法Canvas.clipRect(Rect rect),我若是將畫布Canvas裁剪成一個滑塊大小,這個時候,我再繪製滑塊覆蓋到的文字到原有的位置,超出滑塊的部分就不會顯示了。github

聰明的同窗這個時候可能會有這樣的疑問?你如今將畫布裁剪了,那豈不是隻能顯示滑塊了,道理是這樣的,但是咱們還有Canvas.save()Canvas.restore()方法,它們對應的做用分別是將當前的畫布保存到對應的畫布棧中取出畫布棧中頂層的畫布,並進行恢復canvas

2、核心代碼

在這裏,我假設你們已經對基本的自定View已經很熟悉了,直接展現繪製滑塊的代碼:數組

/** * 繪製Switch滑動塊 */
    private void drawThumb(Canvas canvas) {
        // 滑塊的左右邊界
        int left = mItemCoordinate[mThumbState.pos] + mThumbState.offset;
        int right = mItemCoordinate[mThumbState.pos + 1] + mThumbState.offset;
        // 1. 保存當前圖層
        canvas.save();
        Rect rect = new Rect(left + mThumbMargin, top + mThumbMargin, right - mThumbMargin, bottom - mThumbMargin);
        // 2. 根據滑塊的設定大小裁剪畫布
        canvas.clipRect(rect);
        // 3. 繪製滑塊
        int padding = mThumbMargin + mThumbBorderWidth;
        if (mShape == SwitchShape.RECT) {
            drawRoundRect(canvas, left + padding, top + padding
                    , right - padding, bottom - padding, CORNER_RADIUS, mThumbColorPaint);
            if (mThumbBorderWidth != 0)
                drawRoundRect(canvas, left + mThumbMargin, top + mThumbMargin
                        , right - mThumbMargin, bottom - mThumbMargin, CORNER_RADIUS, mThumbBorderPaint);

        } else {
            drawRoundRect(canvas, left + padding, top + padding
                    , right - padding, bottom - padding, (bottom - top) / 2 - padding, mThumbColorPaint);
            if (mThumbBorderWidth != 0)
                drawRoundRect(canvas, left + mThumbMargin, top + mThumbMargin
                        , right - mThumbMargin, bottom - mThumbMargin, (bottom - top) / 2 - mThumbMargin, mThumbBorderPaint);
        }

        int first, second;
        //... 省略 獲取位置

        // 4. 繪製文字orIcon
        if (mType == SwitchType.TEXT) {
            drawText(canvas, mItems[first], mItemCoordinate[first], top, mItemCoordinate[first + 1], bottom, mThumbTextPaint);
            if (second != -1 && second <= mItemCount - 1) {
                drawText(canvas, mItems[second], mItemCoordinate[second], top, mItemCoordinate[second + 1], bottom, mThumbTextPaint);
            }
        } else {
            drawIcon(canvas, mIconRes[first], mItemCoordinate[first], top, mItemCoordinate[first + 1], bottom, mThumbTextPaint);
            if (second >= 0) {
                drawIcon(canvas, mIconRes[second], mItemCoordinate[second], top, mItemCoordinate[second + 1], bottom, mThumbTextPaint);
            }
        }
        
        // 5. 底層的畫布恢復
        canvas.restore();
    }
複製代碼

註釋也都在上面了,對源碼感興趣的同窗能夠直接看Github,這個控件的代碼也就600行,處理好觸摸事件和使用好屬性動畫便可。bash

3、使用

可能有的同窗不想關注原理,只想知道如何使用。微信

開始

implementation 'com.orient:Orient-Ui:2.1.1'
複製代碼

第一步 添加進xml佈局文件

<com.orient.me.widget.sw.MultiSwitch android:id="@+id/ms_weak" android:layout_width="match_parent" android:layout_marginStart="@dimen/len_10" android:layout_marginEnd="@dimen/len_10" android:layout_height="60dp" android:layout_gravity="center" android:layout_marginTop="@dimen/len_20" app:msBackgroundColor="@color/teal_300" app:msTextSize="@dimen/font_18" app:msNormalTextColor="@color/white_alpha_192" app:msShape="rect" app:msThumbColor="@color/white" app:msThumbMargin="@dimen/len_6" app:msThumbTextColor="@color/teal_300" app:msType="text" />
複製代碼

解釋一下各個屬性的用法:app

屬性 說明 類型
msBackgroundColor 背景顏色 reference|color
msNormalTextColor 非選中狀態文本或者Icon顏色 reference|color
msThumbTextColor 滑塊中文本或者Icon顏色 reference|color
msTextSize 文本大小 reference|dimension
msIconSize 圖標大小 reference|dimension
msThumbMargin 滑塊的外邊距 reference|dimension
msShape 選擇的形狀 rect or oval
msType 選擇的類型 text or icon
msThumbColor 滑塊背景色 reference

第二步 獲取MultiSwitch

使用findViewById獲取MultiSwitch對象

第三步 設置選項內容

設置字符串數組或者Icon數組

mHead.setItemsArray(new String[]{"Dark","Light"});
// or
mIconSwitch.setIconArray(new int[]{R.drawable.grid_ic_play,R.drawable.ic_camera,R.drawable.common_ic_back});
複製代碼

第四步 設置監聽器

提供了位置選擇的回調以及滑塊移動百分比的回調,如個人效果圖,設置背景的黑夜模式和白天模式的時候,利用百分比回調能夠用來設置背景色的漸變效果。

mHead.setMultiSwitchListener(new MultiSwitchListener() {
    @Override
    public void onPositionSelected(int pos) {
        // when pos selected, it will call back
    }

    @Override
    public void onPositionOffsetPercent(int pos, float percent) {
        // current page move offset percent when drag
    }
});
複製代碼

除此之外,你還能夠設置默認位置:

// 設置默認位置
mHead.setCurrentItem(2);
複製代碼

4、總結

進行自定View的時候,你們大可沒必要聞自定View色變,有的時候,某個自定義View的難點可能就是你們不經常使用到的那一到兩個Api,這個時候,就須要你們熟悉官方提供的Api了。

我的說明

本人最近打算換工做了,在上海或者無錫,若是有好的公司推薦或者內推,能夠聯繫我哈,微信:Jw_19951030,感激涕零~

若是你們對Oreint-Ui系列的其餘控件感興趣,能夠查看:

表格: 《Orient-Ui | 單RecyclerView實現花式表格》
時間軸:《花式實現時間軸,樣式由你來定!》

相關文章
相關標籤/搜索