仿“得到”App播放进度条效果

作者: 我叫陆大旭 | 来源:发表于2018-07-20 10:29 被阅读73次

最近由于要做音频播放的功能,设计图中有一个类似“得到”App播放进度条的效果,所以就自己做了一个。

废话不多说先看一下效果

效果图

源代码

BubbleSeekBar源代码

使用


首先在布局中增加BubbleSeekBar组件

<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"
    app: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" />

然后在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);
}

注意点


destory()方法是否必须调用
  • 是的。在销毁布局前需要调用此方法,否则会抛"has leaked window android.widget.framelayout"的错误

当布局layout_width为WRAP_CONTENT的时候大小是多少
  • 取Thumb的宽度

当布局layout_height为WRAP_CONTENT的时候大小是多少
  • 取Thumb和Track的的高度中比较高

trackMarginLeft和trackMarginRight的作用
  • 当Thumb有阴影的时候,左右两边就会没办法到头和尾,所以需要这两个属性使track的头和尾向里缩进


我叫陆大旭。

一个懂点心理学的无聊程序员大叔。
看完文章无论有没有收获,记得打赏、关注和点赞!

相关文章

  • 仿“得到”App播放进度条效果

    最近由于要做音频播放的功能,设计图中有一个类似“得到”App播放进度条的效果,所以就自己做了一个。 废话不多说先看...

  • Axure-实现可拖动进度条

    可拖动进度条的应用场景,例如调节APP屏幕亮度,音乐播放器的音量等。 效果如下图(●ˇ∀ˇ●) 准备4个元件,如下...

  • 小程序音乐播放器

    一、实现效果 点击播放按钮歌曲进行播放,点击暂停停止播放 显示歌曲当前播放时间和总时间 随着歌曲播放进度条变红,点...

  • 小程序背景音频

    1.功能需求: 1)小程序后台可以继续播放音频 2)音频播放,暂停,播放进度时时更新 3)进度条拖拽 2.效果图:...

  • Flutter仿Apple Music播放界面上滑抽屉

    Flutter仿Apple Music播放界面上滑抽屉 先看Apple Music的效果,底部播放控制滑动打开或关...

  • XMLYLearn-仿喜马拉雅应用

    简单仿喜马拉雅App,利用喜马拉雅的SDK,加载节目列表,实现音频播放功能 完整项目地址 效果Gif图 实现的功能...

  • iOS LearnNavigationBar

    LearnNavigationBar 得到App的首页下拉隐藏导航栏效果、得到App的我的下拉效果、QQ空间App...

  • iOS仿抖音—评论视图滑动消失

    iOS仿抖音短视频 iOS仿抖音—左右滑动切换效果iOS仿抖音—上下滑动播放视频iOS仿抖音—评论视图滑动消失 前...

  • android自定义View实现弧形进度条

    效果和蓝舞者app播放界面调节播放速率类似效果如下: 主要代码如下: @Override protected v...

  • ProgressBar的使用实例

    以下内容有三方式设置进度条 设置一个简单的进度条 布局 效果图 设置一个进度条,类似于歌曲播放一样,显示不同颜色的...

网友评论

本文标题:仿“得到”App播放进度条效果

本文链接:https://www.haomeiwen.com/subject/pfutmftx.html