1500字范文,内容丰富有趣,写作好帮手!
1500字范文 > android 自定义 seekbar Android自定义控件 带文字提示的SeekBar

android 自定义 seekbar Android自定义控件 带文字提示的SeekBar

时间:2020-06-15 03:39:41

相关推荐

android 自定义 seekbar Android自定义控件 带文字提示的SeekBar

封面

1.写在前面

SeekBar控件在开发中还是比较常见的,比如音视频进度、音量调节等,但是原生控件有时还不能满足我们的需求,今天就来学习一下如何自定义SeekBar控件,本文主要实现了一个带文字指示器效果的SeekBar控件,看下最终效果:

IndicatorSeekBar

2.实现

IndicatorSeekBar

public class IndicatorSeekBar extends AppCompatSeekBar {

// 画笔

private Paint mPaint;

// 进度文字位置信息

private Rect mProgressTextRect = new Rect();

// 滑块按钮宽度

private int mThumbWidth = dp2px(50);

// 进度指示器宽度

private int mIndicatorWidth = dp2px(50);

// 进度监听

private OnIndicatorSeekBarChangeListener mIndicatorSeekBarChangeListener;

public IndicatorSeekBar(Context context) {

this(context, null);

}

public IndicatorSeekBar(Context context, AttributeSet attrs) {

this(context, attrs, R.attr.seekBarStyle);

}

public IndicatorSeekBar(Context context, AttributeSet attrs, int defStyleAttr) {

super(context, attrs, defStyleAttr);

init();

}

private void init() {

mPaint = new TextPaint();

mPaint.setAntiAlias(true);

mPaint.setColor(Color.parseColor("#00574B"));

mPaint.setTextSize(sp2px(16));

// 如果不设置padding,当滑动到最左边或最右边时,滑块会显示不全

setPadding(mThumbWidth / 2, 0, mThumbWidth / 2, 0);

// 设置滑动监听

this.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {

@Override

public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {

// NO OP

}

@Override

public void onStartTrackingTouch(SeekBar seekBar) {

if (mIndicatorSeekBarChangeListener != null) {

mIndicatorSeekBarChangeListener.onStartTrackingTouch(seekBar);

}

}

@Override

public void onStopTrackingTouch(SeekBar seekBar) {

if (mIndicatorSeekBarChangeListener != null) {

mIndicatorSeekBarChangeListener.onStopTrackingTouch(seekBar);

}

}

});

}

@Override

protected synchronized void onDraw(Canvas canvas) {

super.onDraw(canvas);

String progressText = getProgress() + "%";

mPaint.getTextBounds(progressText, 0, progressText.length(), mProgressTextRect);

// 进度百分比

float progressRatio = (float) getProgress() / getMax();

// thumb偏移量

float thumbOffset = (mThumbWidth - mProgressTextRect.width()) / 2 - mThumbWidth * progressRatio;

float thumbX = getWidth() * progressRatio + thumbOffset;

float thumbY = getHeight() / 2f + mProgressTextRect.height() / 2f;

canvas.drawText(progressText, thumbX, thumbY, mPaint);

if (mIndicatorSeekBarChangeListener != null) {

float indicatorOffset = getWidth() * progressRatio - (mIndicatorWidth - mThumbWidth) / 2 - mThumbWidth * progressRatio;

mIndicatorSeekBarChangeListener.onProgressChanged(this, getProgress(), indicatorOffset);

}

}

/**

* 设置进度监听

*

* @param listener OnIndicatorSeekBarChangeListener

*/

public void setOnSeekBarChangeListener(OnIndicatorSeekBarChangeListener listener) {

this.mIndicatorSeekBarChangeListener = listener;

}

/**

* 进度监听

*/

public interface OnIndicatorSeekBarChangeListener {

/**

* 进度监听回调

*

* @param seekBar SeekBar

* @param progress 进度

* @param indicatorOffset 指示器偏移量

*/

public void onProgressChanged(SeekBar seekBar, int progress, float indicatorOffset);

/**

* 开始拖动

*

* @param seekBar SeekBar

*/

public void onStartTrackingTouch(SeekBar seekBar);

/**

* 停止拖动

*

* @param seekBar SeekBar

*/

public void onStopTrackingTouch(SeekBar seekBar);

}

/**

* dp转px

*

* @param dp dp值

* @return px值

*/

public int dp2px(float dp) {

return (int) TypedValue.applyDimension(PLEX_UNIT_DIP, dp,

getResources().getDisplayMetrics());

}

/**

* sp转px

*

* @param sp sp值

* @return px值

*/

private int sp2px(float sp) {

return (int) TypedValue.applyDimension(PLEX_UNIT_SP, sp,

getResources().getDisplayMetrics());

}

}

重点看下onDraw方法:

@Override

protected synchronized void onDraw(Canvas canvas) {

super.onDraw(canvas);

String progressText = getProgress() + "%";

mPaint.getTextBounds(progressText, 0, progressText.length(), mProgressTextRect);

// 进度百分比

float progressRatio = (float) getProgress() / getMax();

// thumb偏移量

float thumbOffset = (mThumbWidth - mProgressTextRect.width()) / 2 - mThumbWidth * progressRatio;

float thumbX = getWidth() * progressRatio + thumbOffset;

float thumbY = getHeight() / 2f + mProgressTextRect.height() / 2f;

canvas.drawText(progressText, thumbX, thumbY, mPaint);

if (mIndicatorSeekBarChangeListener != null) {

float indicatorOffset = getWidth() * progressRatio - (mIndicatorWidth - mThumbWidth) / 2 - mThumbWidth * progressRatio;

mIndicatorSeekBarChangeListener.onProgressChanged(this, getProgress(), indicatorOffset);

}

}

再看一遍效果图:

IndicatorSeekBar

可以看到,进度百分比文字是跟着进度变化在平移的,所以X轴坐标根据进度动态计算就可以了【总宽度 * 进度百分比】(getWidth() * progressRatio),文字需要居中显示,所以需要向右平移【(滑块宽度 - 文字宽度)/ 2】((mThumbWidth - mProgressTextRect.width()) / 2)。

为了避免滑块滑动到终点时布局被隐藏,需要为SeekBar设置左右padding,距离分别为滑块宽度的一半,,所以【控件总长度 = 控件实际长度 + 滑块宽度】,向右平移的过程中就要动态减去滑块宽度【滑块宽度 * 进度百分比】(mThumbWidth * progressRatio),到这里文字的X轴坐标就计算完成了。

文字在平移的过程中始终是垂直居中的,所以Y轴坐标可以这样计算【控件高度 / 2 + 文字高度 / 2】(getHeight() / 2f + mProgressTextRect.height() / 2f),注意drawText方法默认是从左下角开始绘制文字的,如果对绘制文字还不太了解,可以看下这篇文章《Android 图解Canvas drawText文字居中的那些事》

指示器跟随滑块移动

在IndicatorSeekBar中,向外提供了一个setOnSeekBarChangeListener方法用来回调SeekBar的状态,其中onProgressChanged方法中的indicatorOffset参数就是指示器控件的X坐标,计算方式与上文中进度百分比文字的计算方式一致:

// 【总宽度 * 进度百分比 -(指示器宽度 - 滑块宽度)/ 2 - 滑块宽度 * 进度百分比】

float indicatorOffset = getWidth() * progressRatio - (mIndicatorWidth - mThumbWidth) / 2 - mThumbWidth * progressRatio;

mIndicatorSeekBarChangeListener.onProgressChanged(this, getProgress(), indicatorOffset);

看下如何使用:

public class MainActivity extends AppCompatActivity {

private TextView tvIndicator;

private IndicatorSeekBar indicatorSeekBar;

private Paint mPaint = new Paint();

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

tvIndicator = findViewById(R.id.tv_indicator);

indicatorSeekBar = findViewById(R.id.indicator_seek_bar);

initData();

}

private void initData() {

final LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) tvIndicator.getLayoutParams();

indicatorSeekBar.setOnSeekBarChangeListener(new IndicatorSeekBar.OnIndicatorSeekBarChangeListener() {

@Override

public void onProgressChanged(SeekBar seekBar, int progress, float indicatorOffset) {

String indicatorText = progress + "%";

tvIndicator.setText(indicatorText);

params.leftMargin = (int) indicatorOffset;

tvIndicator.setLayoutParams(params);

}

@Override

public void onStartTrackingTouch(SeekBar seekBar) {

tvIndicator.setVisibility(View.VISIBLE);

}

@Override

public void onStopTrackingTouch(SeekBar seekBar) {

tvIndicator.setVisibility(View.INVISIBLE);

}

});

}

}

布局文件:

android:layout_width="match_parent"

android:layout_height="match_parent">

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:layout_centerInParent="true"

android:layout_marginStart="20dp"

android:layout_marginEnd="20dp"

android:orientation="vertical">

android:id="@+id/tv_indicator"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:background="@drawable/bg_indicator"

android:gravity="center"

android:textColor="#FFFFFF"

android:textSize="16sp"

android:visibility="invisible" />

android:id="@+id/indicator_seek_bar"

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:layout_marginTop="5dp"

android:background="@null"

android:max="100"

android:maxHeight="2dp"

android:minHeight="2dp"

android:progress="50"

android:progressDrawable="@drawable/seekbar_progress_drawable"

android:thumb="@drawable/seekbar_thumb" />

3.写在最后

代码已上传至GitHub,欢迎Star、Fork!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。