Written in Jane

Recently, due to the audio playback function, there was an effect similar to “get” App playback progress bar in the design drawing, so I made one myself.


Without further ado let’s see the effect

The source code

BubbleSeekBar source code

use


Start by adding the BubbleSeekBar component to the layout

<ren.daxu.ui.seekbar.BubbleSeekBar android:id="@+id/bubbleSeekBar" android:layout_width="match_parent" android:layout_height="wrap_content" app:barHeight="4dp" app:bubbleBackgroud="@mipmap/ic_launcher" BubbleHeight =" 30DP "app:bubbleOffset=" 20DP" app:bubbleWidth=" 100DP "app: Max ="100.0" app:min="50.0" app:secondTrack="@drawable/track_s" app:thumb="@drawable/thumb" app:thumbHeight="20dp" app:thumbTextColor="#FFFFFFFF" app:thumbTextSize="10dp" app:thumbWidth="60dp" app:track="@drawable/track" />Copy the code

Then add the bubble content to your Java code. What is added here is a TextView, and users can add content to the View according to their own needs.

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);
Copy the code

Finally, add a listener to your code. The onProgressChanged() method is called whenever the progress bar changes; The onStartTrackingTouch() method is called when pressed; The onStopTrackingTouch() method is called after the move has been pressed.

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) {
    }
});
Copy the code


Style


The attributes that can be customized in XML are listed here

  • Min: indicates the minimum value of the progress bar
  • Max: indicates the maximum value of the progress bar
  • Thumb: Style of thumb
  • ThumbHeight: The height of the Thumb
  • ThumbWidth: The width of the Thumb
  • ThumbTextColor: The Thumb’s text color
  • ThumbTextSize: The text size of the Thumb
  • BarHeight: Height of the progress bar
  • Track: The style of track
  • SecondTrack: The style of top Track
  • TrackMarginLeft: Left voice-over of Track
  • TrackMarginRight: The right voice-over of Track
  • BubbleBackgroud: Bubble background
  • BubbleWidth: bubbleWidth
  • BubbleHeight: bubbleHeight
  • BubbleOffset: The distance of the bubble from the Thumb


The code analysis


Measure the size of the component. Meanwhile, the Y axis center point of Track and the movable length of Track are calculated.

@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;
}
Copy the code

Drawing Track. No drawing takes place when mTrack and mSecondTrack are null.

Private void drawTrack(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); }Copy the code

Draw the Thumb. No drawing is done when the mThumbText is null.

Private void drawThumb(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 ! Rect Rect = new Rect(); mPaint.getTextBounds(mThumbText, 0, mThumbText.length(), rect); Float x = (mthumbwidth-rect.width ()) / 2.0f + rect.width() / 2.0f; float x = (mthumbwidth-rect.width ()) / 2.0f + rect.width() / 2.0f; Float y = (mthumbheight-rect.height ()) / 2.0f + rect.height(); float y = (mthumbheight-rect.height ()) / 2.0f + rect.height(); canvas.drawText(mThumbText, x, y, mPaint); } } canvas.restoreToCount(save); }Copy the code

Custom click events. The showBubble() method is called when pressed, showing BubbleFL; When moving, the calculateBubble() method is called to calculate the BubbleFL position; Hide BubbleFL by calling the hideBubble() method when finally released.

@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); }Copy the code


Pay attention to the point


The size of the layout when layout_width is WRAP_CONTENT

  • Take the width of Thumb









My name is Lu Daxu.

A boring programmer who knows something about psychology. Tip, follow and like the article whether or not you get anything from it!