手把手教你實現一個絲滑般的控件Q彈效果(滑動+Q彈+漸變)

前言

很多人覺得Android的用戶體驗沒有iOS好,但是近年來,隨着衆多Android開發者的努力,現在的Android已經有着不輸於iOS的實力,用戶體驗也在直線上升。

今天要和大家分享的是一個絲滑般的控件Q彈效果,希望對大家的學習和工作有所啓發和幫助。

廢話就不多說了,直接上效果圖,讓你感受一下到底有多絲滑,又有多Q彈。

這Q彈的動畫,妖豔的色彩轉換,着實和市面上的普通Switch控件不太一樣。下面對它逐一拆解,從0到1實現它。

設計思路

Android系統提供的SwitchButon很好地體現了metiarial design設計風格,但不夠妖豔。我希望用兩個比較衝突的對比色進行控件的顏色設計,這樣擺在淺色背景頁面的時,控件可以達到一種直刺眼睛的效果。抓人、妖豔、騷氣外放。只要整體頁面搭配得當,該控件必定可以“外騷內純”。微微模糊的光暈可以使得本就亮麗的顏色變得更加妖豔。

爲增加設計的可擴展性,可以通過改變顏色從而達到體現不同風格的目的。

實現方式

總體而言,雖然控件的外表非常妖豔,但交互邏輯是比較簡單的。不考慮繼承自系統Switch,因爲並不需要再去了解如何擴展Switch。因此直接繼承自View即可。整個邏輯控制在onTouchEvent中實現,聲明好各個可配置的屬性,如顏色、大小等等。

UI繪製

可以看到,圖形由兩大部分構成,一個是前面藍色的圓角矩形(指示器),一個是後面紅色的長條矩形(背景條),分別對這兩個圖形進行繪製即可。onDraw部分的代碼如下:

  • 畫背景條:
    其中bkgRect代表待繪製矩形區域範圍,height、width爲控件長寬,bkgBarW、bkgBarH爲背景條長寬,由於是圓角矩形,調用canvas的drawRoundRect方法進行繪製,bkgBarPaint控制背景條顏色
//畫背景條
RectF bkgRect = new RectF((width - bkgBarW) / 2f, height / 2 - (bkgBarH / 2), (width - bkgBarW) / 2f + bkgBarW, height / 2 + (bkgBarH / 2));
canvas.drawRoundRect(bkgRect, bkgBarH / 4, bkgBarH / 4, bkgBarPaint);
  • 畫前景圓角矩形:
    同樣的,indicatorRect爲待繪製圓角矩形的區域範圍,indicatorW、indicatorH爲指示器寬高,indicatorX、indicatorY爲指示器的中心座標點,該座標之後會結合animator進行動態計算,最後調用canvas的drawRoundRect進行繪製
//畫指示器
RectF indicatorRect = new RectF(
        indicatorX,
        indicatorY,
        indicatorW + indicatorX,
        (height - indicatorH) / 2 + indicatorH
);
canvas.drawRoundRect(indicatorRect, indicatorH / 6, indicatorH / 6, indicatorPaint);
  • 畫圖標或文字:
    這部分計算好文字或圖標的座標進行繪製即可,需要注意的是,如果要繪製文字,需要計算出文字的基線位置,方便與指示器在視覺上居中
//畫圖標
int baseLineY = (int) (indicatorRect.centerY() - textTop / 2 - textBottom / 2);//基線中間點的y軸計算公式
if (status == false) {
    canvas.drawText("♂", indicatorRect.centerX(), baseLineY, textPaint);
} else {
    canvas.drawText("♀", indicatorRect.centerX(), baseLineY, textPaint);
}
  • 動畫實現
    爲方便控制,定義兩個AnimatorSet,分別爲animOnSet、animOffSet,animOnSet代表開關選擇器打開時的動畫合集,animOffSet代表開關選擇器關閉時的動畫合集。設置爲BounceInterpolator即可配置彈性效果。
animatorOn = ValueAnimator.ofFloat(indicatorStartX, indicatorEndX);
animatorOn.setDuration(500);
animatorOn.setInterpolator(new BounceInterpolator());
animatorOn.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        indicatorX = (float) animation.getAnimatedValue();
        postInvalidate();
    }
});
animOnSet = new AnimatorSet();
animOnSet.playTogether(animatorOn, animatorColorOn);


animatorOff = ValueAnimator.ofFloat(indicatorEndX, indicatorStartX);
animatorOff.setDuration(500);
animatorOff.setInterpolator(new BounceInterpolator());
animatorOff.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        indicatorX = (float) animation.getAnimatedValue();
        postInvalidate();
    }
});
animOffSet = new AnimatorSet();
animOffSet.playTogether(animatorColorOff, animatorOff);
  • 加億點細節
    仔細觀察可以注意到,指示器是帶有同顏色的陰影的,淡淡的陰影向四周暈開,如同光霧一般。這裏使用設計好的陰影背景圖切圖即可,但爲了增加控件通用性,採取了討巧的辦法。通過對Bitmap的處理從而在指示器顏色變化時,繪製的陰影顏色也隨之變化。代碼如下,bmShdow爲待繪製的陰影圖片
bmShadow = BitmapFactory.decodeResource(getResources(), R.drawable.img_shadow_rect_blue);
//sex_blue爲配置的指示器爲“開”狀態時的顏色,int值
bmShadow = BitmapUtils.replacePixelColor(bmShadow, sex_blue);

再仔細觀察,會發現在指示器動畫執行的過程中,指示器顏色也完成了一個漸變過渡。這裏有個變換顏色動態計算的操作,依然採用animator進行計算,其中indicatorPaint控制指示器顏色

animatorColorOn = new ValueAnimator();
animatorColorOn.setIntValues(sex_blue, sex_red);
animatorColorOn.setEvaluator(new ArgbEvaluator());
animatorColorOn.setDuration(500);
animatorColorOn.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        int color = (int) animation.getAnimatedValue();
        indicatorPaint.setColor(color);
    }
});

animatorColorOff = new ValueAnimator();
animatorColorOff.setIntValues(sex_red, sex_blue);
animatorColorOff.setEvaluator(new ArgbEvaluator());
animatorColorOff.setDuration(500);
animatorColorOff.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        int color = (int) animation.getAnimatedValue();
        indicatorPaint.setColor(color);
    }
});

最後

Android的學習是一條漫長的道路,每個地方都是值得學習的內容。

其中,控件設計或開發這個點就無處不體現着“自頂向下”的思想。在實際工作中,我們會遇到各種各樣的需求,但是不要慌!只要我們靜下心來,弄清需求,理清邏輯,打磨細節,做到這三點,絕大部分控件設計或開發的難題都可以迎刃而解。

我把自己這段時間整理的Android最重要最熱門的學習方向資料放在了我的GitHub:https://github.com/xieyuliang/Android-P7-share/blob/master/Android,裏面還有不同方向的自學編程路線、面試題集合/面經、及系列技術文章等。

資源持續更新中,歡迎大家一起學習和探討

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章