廢話不多說,來人,上圖
這Q彈的動畫,妖豔的色彩轉換,著實和市面上的普通Switch控制項不太一樣。下面對它逐一拆解,從0到1實現它。
一、設計思路Android系統提供的SwitchButon很好地體現了metiarial design設計風格,但不夠妖豔。我希望用兩個比較衝突的對比色進行控制項的顏色設計,這樣擺在淺色背景頁面的時,控制項可以達到一種直刺眼睛的效果。抓人、妖豔、騷氣外放。只要整體頁面搭配得當,該控制項必定可以「外騷內純」。微微模糊的光暈可以使得本就亮麗的顏色變得更加妖豔。
為增加設計的可擴展性,可以改變顏色從而體現不同的風格。
二、實現方式總體而言,雖然控制項的外表非常妖豔,但交互邏輯是比較簡單的。不考慮繼承自系統Switch,因為並不需要再去了解如何擴展Switch。因此直接繼承自View即可。整個邏輯控制在onTouchEvent中實現,聲明好各個可配置的屬性,如顏色、大小等等。
可以看到,圖形由兩大部分構成,一個是前面藍色的圓角矩形(指示器),一個是後面紅色的長條矩形(背景條),分別對這兩個圖形進行繪製即可。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);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); }});
三、後記控制項設計或開發,無處不體現著「自頂向下」的思想。弄清需求,理清邏輯,打磨細節,做到這三點,絕大部分控制項設計或開發的難題一定可以迎刃而解。《隔壁產品都饞哭了》系列文章的初衷就是要設計出一些和市面上不太一樣的控制項,給人眼前一亮的感覺。不僅把它設計出來,還包括了控制項各個細節的實現,最後做到好看也好用。