Android开发经验谈Android自定义ViewAndroid开发

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

2018-07-20  本文已影响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中自定义的属性

代码分析


测量组件的大小。同时计算出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()方法是否必须调用
当布局layout_width为WRAP_CONTENT的时候大小是多少
当布局layout_height为WRAP_CONTENT的时候大小是多少
trackMarginLeft和trackMarginRight的作用

我叫陆大旭。

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

上一篇 下一篇

猜你喜欢

热点阅读