自定義View實戰之漸變,可撥動,帶動畫圓環控制元件實現
阿新 • • 發佈:2019-02-11
效果圖預覽
1. 分析
1. 繪製中間數字
2. 繪製帶刻度的圓環 考慮分成若干等份
3. 繪製漸變圓環 需要用到漸變相關屬性
4. 動畫處理的同時需要考慮時時計算角度
5. 圓環開關控制按鈕的波動範圍處理
2. 實現原理
1. 繪製文字這個簡單
2. 繪製刻度圓環 分等份 用canvas.drawArc畫若干圓環
3. 繪製漸變圓環,需要用到Paint的setShader方法設定漸變顏色
4. 屬性動畫處理
3. 初始化一些東西 初始化一般我放在onSizeChanged方法中
@Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); mCenterX = w / 2; mCenterY = h / 2; int[] colors = {Color.RED,Color.GREEN,Color.YELLOW}; mSweepGradient = new SweepGradient(mCenterX,mCenterY, colors,null); mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ring_dot); mBitmap = conversionBitmap(mBitmap,dp2px(30),dp2px(30)); mMinValidateTouchArcRadius = (int) (mCircleRadius - mBitmap.getWidth() / 2 * 1.5); mMaxValidateTouchArcRadius = (int) mCircleRadius; }
4. 繪製中間數字和帶刻度線的圓環
/** * 畫帶刻度線的圓環 */ private void drawScaleRing(Canvas canvas) { float margin = mRingRadius - dp20; float left = mCenterX - margin; float top = mCenterY - margin; float right = left + 2 * margin; float bottom = top + 2 * margin; mScaleReacF.set(left,top,right,bottom); // 等分成360 / 6 = 60份 保證間距一樣的 for (int i = 0; i < sweepAngle / 6; i++) { canvas.drawArc(mScaleReacF, startAngle + i * 6, 1f, false, mScaleRingPaint); } } /** * 畫中間的數字 */ private void drawMidNum(Canvas canvas) { String value = String.valueOf(mStepNum); //測量文字的寬高 mTextPaint.getTextBounds(value, 0, value.length(), mTextRect); int width = mTextRect.width();//文字寬 int height = mTextRect.height();//文字高 canvas.drawText(value, mCenterX - width / 2, mCenterY + height / 2, mTextPaint); }
5. 繪製帶漸變色的進度圓環
/** * 畫進度圓環 */ private void drawRingProgress(Canvas canvas) { canvas.save(); //canvas.rotate(-90); float left = mCenterX - mRingRadius; float top = mCenterY - mRingRadius; float right = left + 2 * mRingRadius; float bottom = top + 2 * mRingRadius; mProgressReacF.set(left,top,right,bottom); //設定漸變顏色 mProgressPaint.setShader(mSweepGradient); float sweepAngle = mCurrentAngle; //繪製圓環 startAngle開始角度 sweepAngle掃瞄的角度 canvas.drawArc(mProgressReacF, startAngle, sweepAngle, false, mProgressPaint); canvas.restore(); }
6. 進度條動畫和數字處理
//載入進度條動畫
public void setProgressAnimation(float last, float currentProgress, long duration) {
mCurrentAngle = (currentProgress / mMaxProgress) * sweepAngle;
ValueAnimator progressAnimator = ValueAnimator.ofFloat(last, mCurrentAngle);
progressAnimator.setDuration(duration);
progressAnimator.setTarget(mCurrentAngle);
progressAnimator.setInterpolator(new AccelerateInterpolator());
progressAnimator.start();
progressAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
/**每次要繪製的圓弧角度**/
mCurrentAngle = (float) animation.getAnimatedValue();
//根據當前進度和總進度計算當前步數
mStepNum = (int) (mCurrentAngle / sweepAngle * mMaxStepNum);
postInvalidate();
}
});
}
7. 觸控事件處理
@Override
public boolean onTouchEvent(MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();
if (mIsShowControlIcon && (event.getAction() == MotionEvent.ACTION_MOVE || isTouchArc(x, y))) {
// 通過當前觸控點搞到cos角度值
float cos = computeCos(x, y);
// 通過反三角函式獲得角度值
double angle;
if (x < mCenterX) { // 滑動超過180度
angle = Math.PI * RADIAN + Math.acos(cos) * RADIAN;
} else { // 沒有超過180度
angle = Math.PI * RADIAN - Math.acos(cos) * RADIAN;
}
if (mCurrentAngle > 270 && angle < 90) {
mCurrentAngle = 360;
cos = -1;
} else if (mCurrentAngle < 90 && angle > 270) {
mCurrentAngle = 0;
cos = -1;
} else {
mCurrentAngle = (float) angle;
}
mCurrentProgress = getSelectedValue();
invalidate();
return true;
} else {
return super.onTouchEvent(event);
}
}
private int getSelectedValue() {
return Math.round(mMaxProgress * (mCurrentAngle / sweepAngle));
}
/**
* 按下時判斷按下的點是否按在圓邊範圍內
* @param x
* @param y
*/
private boolean isTouchArc(int x, int y) {
double d = getTouchRadius(x, y);
return d >= mMinValidateTouchArcRadius && d <= mMaxValidateTouchArcRadius;
}
/**
* 計算某點到圓點的距離
* @param x
* @param y
*/
private double getTouchRadius(int x, int y) {
int cx = x - mCenterX;
int cy = y - mCenterY;
return Math.hypot(cx, cy);
}
/**
* 拿到傾斜的cos值
*/
private float computeCos(float x, float y) {
float width = x - mCenterX;
float height = y - mCenterY;
float slope = (float) Math.sqrt(width * width + height * height);
return height / slope;
}
7. 專案原始碼下載
後面統一提供原始碼下載連結
8. 聯絡方式
QQ:1509815887