之前做項(xiàng)目的時(shí)候,有個(gè)圖片編輯頁面,用到拖動(dòng)條添加編輯效果。老板很喜歡Instagram那種拖動(dòng)條的效果,0點(diǎn)從中間開始衰絮,左減右增,也要做成這樣咆畏。Android原生的SeekBar燃异,是從左邊開始,一直往右邊增加的夯膀,不能將起點(diǎn)設(shè)在中間诗充。然后在GitHub上找了幾遍,也沒有找到符合要求的诱建,只能自己實(shí)現(xiàn)了蝴蜓。
先看一下效果圖:
ccc.gif
思路:
1、一根線俺猿,一個(gè)活動(dòng)圓
2茎匠、線分三段,活動(dòng)圓未到達(dá)的前后兩段押袍,活動(dòng)圓掃過的中間段
3诵冒、監(jiān)聽觸摸事件,刷新活動(dòng)圓的位置谊惭,和三段線
具體實(shí)現(xiàn)
1汽馋、資源初始化
private void init(Context context, AttributeSet attrs, int defStyleAttr) {
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MiddleSeekBar, defStyleAttr, R.style.MiddleSeekBar);
mThumbRadius = a.getDimensionPixelSize(R.styleable.MiddleSeekBar_MB_ThumbRadius, 0);
mThumbColor = a.getColor(R.styleable.MiddleSeekBar_MB_ThumbColor, 0);
mTextColor = a.getColor(R.styleable.MiddleSeekBar_MB_TextColor, 0);
mProgressColor = a.getColor(R.styleable.MiddleSeekBar_MB_ProgressColor, 0);
mProgressBackgroundColor = a.getColor(R.styleable.MiddleSeekBar_MB_ProgressBackgroundColor, 0);
mTextSize = a.getDimensionPixelSize(R.styleable.MiddleSeekBar_MB_TextSize, 0);
mProgressWidth = a.getDimensionPixelOffset(R.styleable.MiddleSeekBar_MB_ProgressWidth, 0);
a.recycle();
//初始化提示文字畫筆
mTextPaint.setTextSize(mTextSize);
mTextPaint.setColor(mTextColor);
//初始化進(jìn)度條畫筆
mProgressPaint.setStrokeCap(Paint.Cap.ROUND);
mProgressPaint.setStyle(Paint.Style.STROKE);
mProgressPaint.setStrokeWidth(mProgressWidth);
//初始化活動(dòng)圓畫筆
mThumbShadowRadius = (int) (getResources().getDisplayMetrics().density * THUMB_SHADOW);
mYOffset = (int) (getResources().getDisplayMetrics().density * Y_OFFSET);
if(!isInEditMode()) {
ViewCompat.setLayerType(this, ViewCompat.LAYER_TYPE_SOFTWARE, mThumbPaint);
mThumbPaint.setShadowLayer(mThumbShadowRadius, 0, mYOffset, KEY_SHADOW_COLOR);
}
mThumbPaint.setColor(mThumbColor);
}
2、測(cè)量
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Paint.FontMetrics fontMetrics = mTextPaint.getFontMetrics();
//suggestHeight算出拖動(dòng)條的高度
int suggestHeight = (int) (3 * mThumbRadius + mThumbShadowRadius - fontMetrics.top + fontMetrics.bottom + getPaddingTop() + getPaddingBottom());
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
resolveSizeAndState(suggestHeight, heightMeasureSpec, 0));
}
3圈盔、繪制
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
//設(shè)置最左邊的起始點(diǎn)
mStartPoint.set(getPaddingLeft() + mThumbRadius, h - getPaddingBottom() - mThumbRadius - mThumbShadowRadius);
mProgressLength = w - getPaddingLeft() - getPaddingRight() - 2 * mThumbRadius;
//設(shè)置活動(dòng)圓作用區(qū)域的長方形
mThumbRect.set(mStartPoint.x + mProgressLength * mThumbRatio - HIT_SCOPE_RATIO * mThumbRadius, mStartPoint.y - HIT_SCOPE_RATIO * mThumbRadius,
mStartPoint.x + mProgressLength * mThumbRatio + HIT_SCOPE_RATIO * mThumbRadius, mStartPoint.y + HIT_SCOPE_RATIO * mThumbRadius);
}
@Override
protected void onDraw(Canvas canvas) {
int y = mStartPoint.y;
int firstX = mStartPoint.x;//最左邊點(diǎn)
int secondX = mStartPoint.x + mProgressLength/2;//中間點(diǎn)
int thirdX = (int) mThumbRect.centerX();//活動(dòng)圓所在點(diǎn)
//draw progress將進(jìn)度條分成三段豹芯,前段沒有到達(dá)時(shí)顯示背景色,中段顯示進(jìn)度顏色驱敲,后端沒有到達(dá)時(shí)顯示背景色
mProgressPaint.setColor(mProgressBackgroundColor);
canvas.drawLine(firstX, y, secondX<thirdX?secondX:thirdX, y, mProgressPaint);
mProgressPaint.setColor(mProgressColor);
if (secondX < thirdX){
canvas.drawLine(secondX, y, thirdX, y, mProgressPaint);
}else {
canvas.drawLine(thirdX, y, secondX, y, mProgressPaint);
}
mProgressPaint.setColor(mProgressBackgroundColor);
canvas.drawLine(thirdX>secondX?thirdX:secondX, y, firstX + mProgressLength, y, mProgressPaint);
//畫活動(dòng)圓
canvas.drawCircle(thirdX, y, mThumbRadius, mThumbPaint);
//畫提示文字
mPromptString = ratio2String(mThumbRatio);
mTextPaint.setTextAlign(Paint.Align.CENTER);
canvas.drawText(mPromptString, mThumbRect.centerX(), mThumbRect.top - mThumbRadius, mTextPaint);
}
4铁蹈、觸摸事件
@Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
//判斷觸摸點(diǎn)在活動(dòng)圓方形作用域內(nèi)
if (mThumbRect.contains(x, y)) {
selectInfo.pointerId = event.getPointerId(0);
selectInfo.isCaptured = true;
}
break;
case MotionEvent.ACTION_MOVE:
if (selectInfo.isCaptured) {
if (moveThumb(x)) return false;
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
selectInfo.invalid();
break;
}
if (selectInfo.isCaptured) {
if(mOnSeekBarChangeListener!=null){
mOnSeekBarChangeListener.onProgressChanged(this, mThumbRatio);
}
invalidate();
}
//讓父View的 onInterceptTouchEvent(MotionEvent)失效
if(ViewCompat.isAttachedToWindow(this)){
getParent().requestDisallowInterceptTouchEvent(selectInfo.isCaptured);
}
return selectInfo.isCaptured;
}
private boolean moveThumb(float x) {
if (x > mStartPoint.x) {
mThumbRatio = (x - mStartPoint.x) / mProgressLength;
if (mThumbRatio > 1) {
mThumbRatio = 1;
return true;
}
float offsetX = x - mThumbRect.centerX();
//活動(dòng)圓位置進(jìn)行偏移調(diào)整
mThumbRect.offset(offsetX, 0);
}
return false;
}
總結(jié)
就這樣,一個(gè)中間為起點(diǎn)可以兩邊拖動(dòng)的拖動(dòng)條實(shí)現(xiàn)了众眨。
完整代碼GitHub