最近由于要做音频播放的功能,设计图中有一个类似“得到”App播放进度条的效果,所以就自己做了一个。
废话不多说先看一下效果
源代码
BubbleSeekBar源代码
使用
首先在布局中增加BubbleSeekBar组件
然后在Java代码中增加气泡内容。这里增加的是一个TextView,使用者可以根据自己的需求增加View内容。
final TextView textView = new TextView(this);
textView.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
textView.setBackgroundResource(R.drawable.bubble_bg);
textView.setTextColor(0xffffffff);
textView.setGravity(Gravity.CENTER);
bubbleSeekBar.addBubbleFL(textView);
最后在代码中增加监听。onProgressChanged()方法是在进度条改变的时候都会调用;onStartTrackingTouch()方法是在按下时会调用;onStopTrackingTouch()方法是在按下移动结束后调用。
bubbleSeekBar.setOnProgressChangedListener(new BubbleSeekBar.OnProgressChangedListener() {
@Override
public void onProgressChanged(BubbleSeekBar bubbleSeekBar, float progress, boolean fromUser) {
String str = (int) progress + "/" + (int) bubbleSeekBar.getMax();
bubbleSeekBar.updateThumbText(str);
textView.setText(str);
}
@Override
public void onStartTrackingTouch(BubbleSeekBar bubbleSeekBar) {
}
@Override
public void onStopTrackingTouch(BubbleSeekBar bubbleSeekBar) {
}
});
Style
这里列举了可以在xml中自定义的属性
- min:进度条的最小值
- max:进度条的最大值
- thumb:Thumb的样式
- thumbHeight:Thumb的高度
- thumbWidth:Thumb的宽度
- thumbTextColor:Thumb的文字颜色
- thumbTextSize:Thumb的文字大小
- barHeight:进度条的高度
- track:Track的样式
- secondTrack:上层Track的样式
- trackMarginLeft:Track的左旁白
- trackMarginRight:Track的右旁白
- bubbleBackgroud:气泡的背景
- bubbleWidth:气泡的宽度
- bubbleHeight:气泡的高度
- bubbleOffset:气泡离Thumb的距离
代码分析
测量组件的大小。同时计算出Track的Y轴中心点和Track可移动的长度。
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int measureWidth = getLayoutParams().width;
int measureHeight = getLayoutParams().height;
if (measureHeight == ViewGroup.LayoutParams.WRAP_CONTENT) {
measureHeight = (int) (mThumbHeight > mTrackHeight ? mThumbHeight : mTrackHeight);
}
if (measureWidth == ViewGroup.LayoutParams.WRAP_CONTENT) {
measureWidth = (int) mThumbWidth;
}
measureWidth = resolveSize(measureWidth, widthMeasureSpec);
measureHeight = resolveSize(measureHeight, heightMeasureSpec);
setMeasuredDimension(measureWidth, measureHeight);
mTrackCenterY = measureHeight >> 1;
mTrackLength = measureWidth - mThumbWidth;
}
绘制Track。当mTrack和mSecondTrack为null时不进行绘制。
/**
* 绘制轨道
*
* @param canvas
*/
private void drawTrack(Canvas canvas) {
int save = canvas.save();
canvas.translate(0, mTrackCenterY - mTrackHeight / 2.0f);
if (mTrack != null) {
mTrack.setBounds(Math.round(mTrackMarginLeft), 0, getMeasuredWidth() - Math.round(mTrackMarginLeft) - Math.round(mTrackMarginRight), Math.round(mTrackHeight));
mTrack.draw(canvas);
}
if (mSecondTrack != null) {
mSecondTrack.setBounds(Math.round(mTrackMarginLeft), 0, Math.round(mThumbOffset + mThumbWidth / 2.0f) - Math.round(mTrackMarginLeft) - Math.round(mTrackMarginRight), Math.round(mTrackHeight));
mSecondTrack.draw(canvas);
}
canvas.restoreToCount(save);
}
绘制Thumb。当mThumbText为null时不会进行绘制。
/**
* 绘制拇指
*
* @param canvas
*/
private void drawThumb(Canvas canvas) {
int save = canvas.save();
if (mThumb != null) {
canvas.translate(mThumbOffset, mTrackCenterY - mThumbHeight / 2.0f);
mThumb.setBounds(0, 0, Math.round(mThumbWidth), Math.round(mThumbHeight));
mThumb.draw(canvas);
if (mThumbText != null) {
/**
* 绘制文字
*/
Rect rect = new Rect();
mPaint.getTextBounds(mThumbText, 0, mThumbText.length(), rect);
float x = (mThumbWidth - rect.width()) / 2.0f + rect.width() / 2.0f;
float y = (mThumbHeight - rect.height()) / 2.0f + rect.height();
canvas.drawText(mThumbText, x, y, mPaint);
}
}
canvas.restoreToCount(save);
}
自定义点击事件。当按下时会调用showBubble()方法,显示BubbleFL;当移动的时候会调用calculateBubble()方法计算BubbleFL的位置;当最后释放时调用hideBubble()方法,隐藏BubbleFL。
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
performClick();
isThumbOnDragging = true;
if (mOnProgressChangedListener != null) {
mOnProgressChangedListener.onStartTrackingTouch(this);
}
showBubble();
}
break;
case MotionEvent.ACTION_MOVE: {
mThumbOffset = event.getX() - mThumbWidth / 2;
if (mThumbOffset < 0)
mThumbOffset = 0;
else if (mThumbOffset > mTrackLength)
mThumbOffset = mTrackLength;
if (mTrackLength != 0)
mProgress = mMin + (mMax - mMin) * (mThumbOffset) / mTrackLength;
else
mProgress = mMin;
calculateBubble();
postInvalidate();
if (mOnProgressChangedListener != null) {
mOnProgressChangedListener.onProgressChanged(this, getProgress(), true);
}
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL: {
if (mOnProgressChangedListener != null) {
mOnProgressChangedListener.onStopTrackingTouch(this);
}
isThumbOnDragging = false;
hideBubble();
}
break;
}
return isThumbOnDragging | super.onTouchEvent(event);
}
注意点
当布局layout_width为WRAP_CONTENT的时候大小是多少
- 取Thumb的宽度
当布局layout_height为WRAP_CONTENT的时候大小是多少
- 取Thumb和Track的的高度中比较高
trackMarginLeft和trackMarginRight的作用
- 当Thumb有阴影的时候,左右两边就会没办法到头和尾,所以需要这两个属性使track的头和尾向里缩进
我叫陆大旭。
一个懂点心理学的无聊程序员大叔。
看完文章无论有没有收获,记得打赏、关注和点赞!