大家好,欢迎来到IT知识分享网。
文章目录
前言
SeekBar是Android的常用控件之一,简单总结下几种用法。
一、SeekBar基础用法
SeekBar是ProgressBar的扩展,是一个可以拖动的进度条,这样用户就可以通过拖动控制条来改变进度。系统默认只有水平样式。
1、水平样式
2、常用属性
- 设置进度条范围最大值
android:max=“” - 设置进度条范围最小值
android:min=“” - 设置当前进度值
android:progress=“” - 设置第二进度值(类似缓冲进度)
android:secondaryProgress =“” - 设置进度条的图片
android:progressDrawable = “” - 设置进度条的滑块图片
android:thumb = “”
2、布局写法
<SeekBar android:id="@+id/sb_number" android:layout_marginStart="@dimen/x38" android:layout_marginEnd="@dimen/x42" android:background="@null" android:splitTrack="false" android:layout_width="@dimen/x540" android:layout_height="wrap_content" android:maxHeight="@dimen/x10" android:progressDrawable="@drawable/seekbar_volume" android:thumb="@drawable/icon_seekbar_thumb"/>
3、代码调用
SeekBar seekBar = contentView.findViewById(R.id.sb_number); seekBar.setProgress(100); seekBar.setMin(0); seekBar.setMax(finalMaxTypeVolume); seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
// 进度变化时的回调 } @Override public void onStartTrackingTouch(SeekBar seekBar) {
// 开始拖动时的回调 } @Override public void onStopTrackingTouch(SeekBar seekBar) {
// 停止拖动时的回调 } });
二、垂直seekBar
默认的SeekBar没有设置方向的功能,垂直方向需要重新SeekBar.
1.关键代码,重新onDraw方法,将控件旋转90度
protected void onDraw(Canvas c) {
c.rotate(-90); c.translate(-getHeight(), 0); super.onDraw(c); }
三、圆弧seekBar
圆弧seekBar可以用来在复杂的界面使用,相对代码更加复杂,本文方法自定义view绘制。
1. 自定义圆弧SeekBar
/ * 圆弧 SeekBar */ public class ArcSeekBarPro extends View {
/ * 画笔 */ private Paint mPaint; / * 笔画描边的宽度 */ private float mStrokeWidth; private Paint.Cap mStrokeCap = Paint.Cap.ROUND; / * 默认的控件宽度,也是圆弧直径 */ private int defaultArcDiameter; / * 开始角度 */ private int mStartAngle = 0; / * 路径角度 */ private int mSweepAngle = 30; / * 圆心坐标x */ private float mCircleCenterX; / * 圆心坐标y */ private float mCircleCenterY; / * 弧形 正常颜色 */ private int mNormalColor = 0x80D0D4D9; / * 进度颜色 */ private int mProgressColor = 0x99FFFFFF; / * 半径 */ private float mRadius; / * 最大进度 */ private int mMax = 10; / * 当前进度 */ private int mProgress = 0; / * 进度百分比 */ private int mProgressPercent; private Bitmap mThumbBitmap; private Matrix mMatrix; / * 拖动按钮的画笔宽度 */ private float mThumbStrokeWidth; / * 拖动按钮的颜色 */ private int mThumbColor = 0xFFD5BDAD; / * 拖动按钮的半径 */ private float mThumbRadius; / * 拖动按钮的中心点X坐标 */ private float mThumbCenterX; / * 拖动按钮的中心点Y坐标 */ private float mThumbCenterY; / * 触摸时可偏移距离 */ private float mAllowableOffsets; / * 触摸时按钮半径放大量 */ private float mThumbRadiusEnlarges; / * 是否显示拖动按钮 */ private boolean isShowThumb = true; / * 手势,用来处理点击事件 */ private GestureDetector mDetector; / * 是否可以拖拽 */ private boolean isCanDrag = false; / * 是否启用拖拽改变进度 */ private boolean isEnabledDrag = true; / * 是否启用点击改变进度 */ private boolean isEnabledSingle = true; private OnChangeListener mOnChangeListener; public ArcSeekBarPro(Context context) {
this(context, null); } public ArcSeekBarPro(Context context, AttributeSet attrs) {
this(context, attrs, 0); } public ArcSeekBarPro(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr); init(context, attrs); } private void init(Context context, AttributeSet attrs) {
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ArcSeekBarPro); mThumbBitmap = Utils.loadBitmap(R.drawable.icon_seekbar_pro_thumb, context); mMatrix = new Matrix(); DisplayMetrics displayMetrics = getDisplayMetrics(); defaultArcDiameter = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, context.getResources().getDimension(R.dimen.arc_diameter), getDisplayMetrics()); mStrokeWidth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, context.getResources().getDimension(R.dimen.eac_control_thumb), displayMetrics); mThumbRadius = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, context.getResources().getDimension(R.dimen.thumb_drawable_radius), displayMetrics); mThumbStrokeWidth = mThumbRadius; mAllowableOffsets = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10, displayMetrics); mThumbRadiusEnlarges = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 2, displayMetrics); int size = a.getIndexCount(); for (int i = 0; i < size; i++) {
int attr = a.getIndex(i); if (attr == R.styleable.ArcSeekBarPro_arcStrokeWidth) {
mStrokeWidth = a.getDimension(attr, mStrokeWidth); } else if (attr == R.styleable.ArcSeekBarPro_arcStrokeCap) {
mStrokeCap = getStrokeCap(a.getInt(attr, 3)); } else if (attr == R.styleable.ArcSeekBarPro_arcNormalColor) {
mNormalColor = a.getColor(attr, mNormalColor); } else if (attr == R.styleable.ArcSeekBarPro_arcProgressColor) {
mProgressColor = a.getColor(attr, mProgressColor); } else if (attr == R.styleable.ArcSeekBarPro_arcStartAngle) {
mStartAngle = a.getInt(attr, mStartAngle); } else if (attr == R.styleable.ArcSeekBarPro_arcSweepAngle) {
mSweepAngle = a.getInt(attr, mSweepAngle); } else if (attr == R.styleable.ArcSeekBarPro_arcMax) {
int max = a.getInt(attr, mMax); if (max > 0) {
mMax = max; } } else if (attr == R.styleable.ArcSeekBarPro_arcProgress) {
mProgress = a.getInt(attr, mProgress); } else if (attr == R.styleable.ArcSeekBarPro_arcThumbStrokeWidth) {
mThumbStrokeWidth = a.getDimension(attr, mThumbStrokeWidth); } else if (attr == R.styleable.ArcSeekBarPro_arcThumbColor) {
mThumbColor = a.getColor(attr, mThumbColor); } else if (attr == R.styleable.ArcSeekBarPro_arcThumbRadius) {
mThumbRadius = a.getDimension(attr, mThumbRadius); } else if (attr == R.styleable.ArcSeekBarPro_arcThumbRadiusEnlarges) {
mThumbRadiusEnlarges = a.getDimension(attr, mThumbRadiusEnlarges); } else if (attr == R.styleable.ArcSeekBarPro_arcShowThumb) {
isShowThumb = a.getBoolean(attr, isShowThumb); } else if (attr == R.styleable.ArcSeekBarPro_arcAllowableOffsets) {
mAllowableOffsets = a.getDimension(attr, mAllowableOffsets); } else if (attr == R.styleable.ArcSeekBarPro_arcEnabledDrag) {
isEnabledDrag = a.getBoolean(attr, true); } else if (attr == R.styleable.ArcSeekBarPro_arcEnabledSingle) {
isEnabledSingle = a.getBoolean(attr, true); } } a.recycle(); mProgressPercent = (int) (mProgress * 100.0f / mMax); mPaint = new Paint(); mDetector = new GestureDetector(getContext(), new GestureDetector.SimpleOnGestureListener() {
@Override public boolean onSingleTapUp(MotionEvent event) {
if (isInArc(event.getX(), event.getY())) {
updateDragThumb(event.getX(), event.getY(), true); if (mOnChangeListener != null) {
mOnChangeListener.onSingleTapUp(); } return true; } return super.onSingleTapUp(event); } }); } private Paint.Cap getStrokeCap(int value) {
switch (value) {
case 1: return Paint.Cap.BUTT; case 2: return Paint.Cap.SQUARE; default: return Paint.Cap.ROUND; } } private DisplayMetrics getDisplayMetrics() {
return getResources().getDisplayMetrics(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec); int width = measureHandler(widthMeasureSpec, defaultArcDiameter); int height = measureHandler(heightMeasureSpec, defaultArcDiameter); //圆心坐标 mCircleCenterX = (width + getPaddingLeft() - getPaddingRight()) / 2.0f; mCircleCenterY = (height + getPaddingTop() - getPaddingBottom()) / 2.0f; //计算间距 int padding = Math.max(getPaddingLeft() + getPaddingRight(), getPaddingTop() + getPaddingBottom()); //半径=视图宽度-横向或纵向内间距值 - 画笔宽度 mRadius = (width - padding - Math.max(mStrokeWidth, mThumbStrokeWidth)) / 2.0f - mThumbRadius; setMeasuredDimension(width, height); } / * 测量 * * @param measureSpec * @param defaultSize * @return */ private int measureHandler(int measureSpec, int defaultSize) {
int result = defaultSize; int measureMode = MeasureSpec.getMode(measureSpec); int measureSize = MeasureSpec.getSize(measureSpec); if (measureMode == MeasureSpec.EXACTLY) {
result = measureSize; } else if (measureMode == MeasureSpec.AT_MOST) {
result = Math.min(defaultSize, measureSize); } return result; } @Override protected void onDraw(Canvas canvas) {
super.onDraw(canvas); drawArc(canvas); drawThumb(canvas); } / * 绘制弧形 * * @param canvas */ private void drawArc(Canvas canvas) {
mPaint.reset(); mPaint.setAntiAlias(true); mPaint.setStyle(Paint.Style.STROKE); mPaint.setStrokeWidth(mStrokeWidth); mPaint.setShader(null); mPaint.setStrokeCap(mStrokeCap); //进度圆半径 float diameter = mRadius * 2; float startX = mCircleCenterX - mRadius; float startY = mCircleCenterY - mRadius; RectF rectF1 = new RectF(startX, startY, startX + diameter, startY + diameter); if (mNormalColor != 0) {
mPaint.setColor(mNormalColor); //绘制底层弧形 canvas.drawArc(rectF1, mStartAngle, mSweepAngle, false, mPaint); } mPaint.setColor(mProgressColor); float ratio = getRatio(); if (ratio != 0) {
//绘制当前进度弧形 canvas.drawArc(rectF1, mStartAngle, mSweepAngle * ratio, false, mPaint); } } private void drawThumb(Canvas canvas) {
if (isShowThumb) {
mPaint.reset(); // mPaint.setAntiAlias(true); // mPaint.setStyle(Paint.Style.FILL_AND_STROKE); // mPaint.setStrokeWidth(mThumbStrokeWidth); // mPaint.setColor(mThumbColor); float thumbAngle = mStartAngle + mSweepAngle * getRatio(); //已知圆心,半径,角度,求圆上的点坐标 mThumbCenterX = (float) (mCircleCenterX + mRadius * Math.cos(Math.toRadians(thumbAngle))); mThumbCenterY = (float) (mCircleCenterY + mRadius * Math.sin(Math.toRadians(thumbAngle))); // if (isCanDrag) {
// canvas.drawCircle(mThumbCenterX, mThumbCenterY, mThumbRadius + mThumbRadiusEnlarges, mPaint); // } else {
// canvas.drawCircle(mThumbCenterX, mThumbCenterY, mThumbRadius, mPaint); // } if (null != mThumbBitmap) {
mMatrix.setTranslate(mThumbCenterX - mThumbRadius, mThumbCenterY - mThumbRadius); mMatrix.preScale(1, 1, 0, 0); mPaint.setAlpha(255); canvas.drawBitmap(mThumbBitmap, mMatrix, mPaint); } } } @Override public boolean onTouchEvent(MotionEvent event) {
if (isEnabledDrag) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: checkCanDrag(event.getX(), event.getY()); break; case MotionEvent.ACTION_MOVE: if (isCanDrag) {
updateDragThumb(event.getX(), event.getY(), false); } break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: getParent().requestDisallowInterceptTouchEvent(false); if (mOnChangeListener != null) {
mOnChangeListener.onStopTrackingTouch(isCanDrag); } isCanDrag = false; invalidate(); break; } } if (isEnabledSingle) {
mDetector.onTouchEvent(event); } return isEnabledSingle || isEnabledDrag || super.onTouchEvent(event); } / * 判断坐标点是否在弧形上 * * @param x * @param y * @return */ private boolean isInArc(float x, float y) {
float distance = getDistance(mCircleCenterX, mCircleCenterY, x, y); if (Math.abs(distance - mRadius) <= mStrokeWidth / 2f + mAllowableOffsets) {
if (mSweepAngle < 360) {
float angle = (getTouchDegrees(x, y) + mStartAngle) % 360; if (mSweepAngle < 0) {
if (mStartAngle + mSweepAngle + 360 <= 360) {
return angle <= mStartAngle || angle >= (mStartAngle + mSweepAngle + 360) % 360; } else {
return angle <= mStartAngle && angle >= mStartAngle + mSweepAngle + 360; } } if (mStartAngle + mSweepAngle <= 360) {
return angle >= mStartAngle && angle <= mStartAngle + mSweepAngle; } else {
return angle >= mStartAngle || angle <= (mStartAngle + mSweepAngle) % 360; } } return true; } return false; } / * 获取两点间距离 * * @param x1 * @param y1 * @param x2 * @param y2 * @return */ private float getDistance(float x1, float y1, float x2, float y2) {
return (float) Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2)); } / * 更新多拽进度 * * @param x * @param y * @param isSingle */ private void updateDragThumb(float x, float y, boolean isSingle) {
int progress = getProgressForAngle(getTouchDegrees(x, y)); if (!isSingle) {
int tempProgressPercent = (int) (progress * 100.0f / mMax); //当滑动至至边界值时,增加进度校准机制 if (mProgressPercent < 10 && tempProgressPercent > 90) {
progress = 0; } else if (mProgressPercent > 90 && tempProgressPercent < 10) {
progress = mMax; } int progressPercent = (int) (progress * 100.0f / mMax); //拖动进度突变不允许超过30% if (Math.abs(progressPercent - mProgressPercent) > 30) {
return; } } setProgress(progress, true); } / * 通过弧度换算得到当前精度 * * @param angle * @return */ private int getProgressForAngle(float angle) {
int touchProgress = 0; if (mSweepAngle < 0) {
touchProgress = Math.round(1.0f * mMax / mSweepAngle * (angle - 360)); } else {
touchProgress = Math.round(1.0f * mMax / mSweepAngle * angle); } return touchProgress; } / * 获取触摸坐标的夹角度数 * * @param x * @param y * @return */ private float getTouchDegrees(float x, float y) {
float x1 = x - mCircleCenterX; float y1 = y - mCircleCenterY; //求触摸点弧形的夹角度数 float angle = (float) (Math.atan2(y1, x1) * 180 / Math.PI); angle -= mStartAngle; while (angle < 0) {
angle += 360; } return angle; } / * 检测是否可拖拽 * * @param x * @param y */ private void checkCanDrag(float x, float y) {
float distance = getDistance(mThumbCenterX, mThumbCenterY, x, y); isCanDrag = distance <= mThumbRadius + mAllowableOffsets; if (mOnChangeListener != null) {
mOnChangeListener.onStartTrackingTouch(isCanDrag); } invalidate(); } / * 进度比例 * * @return */ private float getRatio() {
return mProgress * 1.0f / mMax; } / * 设置最大进度 * * @param max */ public void setMax(int max) {
if (max > 0) {
this.mMax = max; invalidate(); } } / * 设置当前进度 * * @param progress */ public void setProgress(int progress) {
setProgress(progress, false); } private void setProgress(int progress, boolean fromUser) {
if (progress < 0) {
progress = 0; } else if (progress > mMax) {
progress = mMax; } this.mProgress = progress; mProgressPercent = (int) (mProgress * 100.0f / mMax); invalidate(); if (mOnChangeListener != null) {
mOnChangeListener.onProgressChanged(mProgress, mMax, fromUser); } } / * 设置正常颜色 * * @param color */ public void setNormalColor(int color) {
this.mNormalColor = color; invalidate(); } / * 设置进度颜色(纯色) * * @param color */ public void setProgressColor(int color) {
this.mProgressColor = color; invalidate(); } / * 设置进度颜色 * * @param resId */ public void setProgressColorResource(int resId) {
int color = getResources().getColor(resId); setProgressColor(color); } public int getStartAngle() {
return mStartAngle; } public int getSweepAngle() {
return mSweepAngle; } public float getCircleCenterX() {
return mCircleCenterX; } public float getCircleCenterY() {
return mCircleCenterY; } public float getRadius() {
return mRadius; } public int getMax() {
return mMax; } public int getProgress() {
return mProgress; } public float getThumbRadius() {
return mThumbRadius; } public float getThumbCenterX() {
return mThumbCenterX; } public float getThumbCenterY() {
return mThumbCenterY; } public float getAllowableOffsets() {
return mAllowableOffsets; } public boolean isEnabledDrag() {
return isEnabledDrag; } public boolean isEnabledSingle() {
return isEnabledSingle; } public boolean isShowThumb() {
return isShowThumb; } public float getThumbRadiusEnlarges() {
return mThumbRadiusEnlarges; } / * 触摸时按钮半径放大量 * * @param thumbRadiusEnlarges */ public void setThumbRadiusEnlarges(float thumbRadiusEnlarges) {
this.mThumbRadiusEnlarges = thumbRadiusEnlarges; } / * 是否显示拖动按钮 * * @param showThumb */ public void setShowThumb(boolean showThumb) {
isShowThumb = showThumb; invalidate(); } / * 触摸时可偏移距离:偏移量越大,触摸精度越小 * * @param allowableOffsets */ public void setAllowableOffsets(float allowableOffsets) {
this.mAllowableOffsets = allowableOffsets; } / * 是否启用拖拽 * * @param enabledDrag 默认为 true,为 false 时 相当于{@link android.widget.ProgressBar} */ public void setEnabledDrag(boolean enabledDrag) {
isEnabledDrag = enabledDrag; } / * 设置是否启用点击改变进度 * * @param enabledSingle */ public void setEnabledSingle(boolean enabledSingle) {
isEnabledSingle = enabledSingle; } / * 进度百分比 * * @return */ public int getProgressPercent() {
return mProgressPercent; } / * 设置进度改变监听 * * @param onChangeListener */ public void setOnChangeListener(OnChangeListener onChangeListener) {
this.mOnChangeListener = onChangeListener; } public interface OnChangeListener {
/ * 跟踪触摸事件开始时回调此方法 {@link MotionEvent#ACTION_DOWN} * * @param isCanDrag */ void onStartTrackingTouch(boolean isCanDrag); / * 进度改变时回调此方法 * * @param progress * @param max * @param fromUser */ void onProgressChanged(int progress, int max, boolean fromUser); / * 跟踪触摸事件停止时回调此方法 {@link MotionEvent#ACTION_UP} */ void onStopTrackingTouch(boolean isCanDrag); / * 通过点击事件改变进度后回调此方法 {@link GestureDetector#GestureDetector#onSingleTapUp()} */ void onSingleTapUp(); } }
三种SeekBar效果如图所示
三、常见问题
- 自定义图标滑到最小或者最大时,图标异常
android:thumbOffset="0px"
- 滑块间隔是否覆盖滑轨,系统默认是间隔
android:splitTrack=“false” //默认为true
- 解决 seekBar 宽度不撑满布局
android:paddingStart="0dp" android:paddingEnd="0dp"
四、参考链接
- https://github.com/jenly1314/ArcSeekBar
- https://blog.csdn.net/liosen/article/details/
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/129217.html