因为公司业务需要,最近用到了可拖动进度条,第一反应是使用SeekBar,用完调试才发现无法满足公司业务需求和UI的设计效果,于是大致浏览了一下自定义可拖动进度条,便用自定义view手写了一个可拖动进度条。
功能:
- 1、可控制拖动与禁止拖动进度条;
- 2、可自定义进度条高度、进度指示器圆点半径;
- 3、可自定义进度条背景颜色、进度条进度颜色、进度指示器圆点颜色;
- 4、可自定义禁止拖动状态下 进度条进度颜色、进度指示器圆点颜色。
可拖动状态下示例:
禁止拖动状态下示例:
话不多说,上代码。
1、自定义view部分代码:
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import androidx.annotation.Nullable;
/**
* 自定义可拖动的进度条
*/
public class DragProgressView extends View {
// 灰色背景线段的画笔
private Paint bgPaint;
// 实际进度绿色线段的画笔
private Paint progressPaint;
// 圆点指示器的画笔
private Paint circlePaint;
// 进度条的最大宽度
private float maxProgress;
// 进度条当前的宽度
private float currentProgress;
// 当前View的宽度
private int width;
// 当前View的高度
private int height;
// 距离左边的内边距
private int paddingLeft;
// 距离右边的内边距
private int paddingRight;
// 是否可拖动
private boolean isDrag = true;
// 圆点指示器的半径 默认10dp
private int mCircleRadius;
// 进度条高度 默认10dp
private int progressHeight;
// 进度条背景颜色
private int progressBackgroundColor;
// 进度条颜色
private int progressColor;
// 圆形指示器颜色
private int circleColor;
// 圆形指示器阴影颜色
private int circleShadowColor;
// 进度条不可拖动时颜色
private int progressColorN;
// 圆形指示器不可拖动时颜色
private int circleColorN;
// 圆形指示器阴影不可拖动时颜色
private int circleShadowColorN;
private Context mContext;
private String TAG = getClass().getSimpleName();
public DragProgressView(Context context) {
super(context);
}
public DragProgressView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public DragProgressView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
getAttrs(context, attrs);
initPaint();
}
/**
* 初始化画笔
*/
private void initPaint() {
// 进度条背景画笔
bgPaint = new Paint();
bgPaint.setColor(progressBackgroundColor); // 进度条背景颜色
bgPaint.setStyle(Paint.Style.FILL_AND_STROKE); // 填充且描边
bgPaint.setAntiAlias(true); // 抗锯齿
bgPaint.setStrokeCap(Paint.Cap.ROUND); // 线冒的头是圆的
bgPaint.setStrokeWidth(progressHeight); // 进度条背景高度 dp转px
// 设置进度画笔
progressPaint = new Paint();
progressPaint.setColor(progressColor); // 进度条进度颜色
progressPaint.setStyle(Paint.Style.FILL_AND_STROKE); // 填充且描边
progressPaint.setAntiAlias(true); // 抗锯齿
progressPaint.setStrokeCap(Paint.Cap.ROUND); // 线冒的头圆的
progressPaint.setStrokeWidth(progressHeight); // 进度条进度高度 3dp转px
// 圆点指示器
circlePaint = new Paint();
circlePaint.setAntiAlias(true); // 设置抗锯齿
circlePaint.setColor(circleColor); // 圆点指示器颜色
circlePaint.setShadowLayer(dip2px(mContext, 2), 0, 0, circleShadowColor); // 圆点指示器阴影颜色
circlePaint.setStyle(Paint.Style.FILL); // 填充
}
/**
* 初始化高度、颜色等可自定义参数
*/
private void getAttrs(Context context, AttributeSet attrs) {
mContext = context;
Resources resources = context.getResources();
progressBackgroundColor = resources.getColor(R.color.text_colorc);
progressColor = resources.getColor(R.color.toolbar_background);
circleColor = resources.getColor(R.color.toolbar_background);
circleShadowColor = resources.getColor(R.color.white);
if (attrs != null) {
TypedArray mTypedArray = context.obtainStyledAttributes(attrs, R.styleable.dragProgressView);
isDrag = mTypedArray.getBoolean(R.styleable.dragProgressView_isDrag, true); // 默认可拖动
mCircleRadius = mTypedArray.getLayoutDimension(R.styleable.dragProgressView_circleRadius, dip2px(context, 10));
progressHeight = mTypedArray.getLayoutDimension(R.styleable.dragProgressView_progressHeight, dip2px(context, 10));
progressBackgroundColor = mTypedArray.getColor(R.styleable.dragProgressView_progressBackgroundColor, progressBackgroundColor);
progressColor = mTypedArray.getColor(R.styleable.dragProgressView_progressColor, progressColor);
circleColor = mTypedArray.getColor(R.styleable.dragProgressView_circleColor, circleColor);
circleShadowColor = mTypedArray.getColor(R.styleable.dragProgressView_circleShadowColor, circleShadowColor);
progressColorN = mTypedArray.getColor(R.styleable.dragProgressView_progressColorN, progressColor);
circleColorN = mTypedArray.getColor(R.styleable.dragProgressView_circleColorN, circleColor);
circleShadowColorN = mTypedArray.getColor(R.styleable.dragProgressView_circleShadowColorN, circleShadowColor);
mTypedArray.recycle();
} else {
isDrag = true;
mCircleRadius = dip2px(context, 10);
progressHeight = dip2px(context, 10);
progressColorN = progressColor;
circleColorN = circleColor;
circleShadowColorN = circleShadowColor;
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
int minHeight = mCircleRadius * 2 + (dip2px(mContext, 2) * 2);
int height = resolveSize(minHeight, heightMeasureSpec);
setMeasuredDimension(width, height);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
// 初始化几个距离参数
width = getWidth(); // view的宽度
height = getHeight(); // view的高度
// 让左边距至少为半个圆点指示器的距离
paddingLeft = getPaddingLeft(); // 距离左边的距离
if (getPaddingLeft() < mCircleRadius) {
paddingLeft = mCircleRadius;
}
// 让右边距至少为半个圆点指示器的距离
paddingRight = getPaddingRight(); // 距离右边的距离
if (getPaddingRight() < mCircleRadius) {
paddingRight = mCircleRadius;
}
// 如果当前进度小于左边距
setCurrentProgress();
// 最大进度长度等于View的宽度-(左边的内边距+右边的内边距)
maxProgress = width - paddingLeft - paddingRight;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (isDrag) {
progressPaint.setColor(progressColor); // 进度条颜色
circlePaint.setColor(circleColor); // 指示器颜色
} else {
progressPaint.setColor(progressColorN); // 进度条颜色
circlePaint.setColor(circleColorN); // 指示器颜色
}
// 绘制进度条背景
// 从(左边距,View高度的一半)开始,到(View宽度-右边距,View高度的一半)还将绘制灰色背景线段
canvas.drawLine(paddingLeft, height / 2, width - paddingRight, height / 2, bgPaint);
// 绘制进度条进度
// 从(左边距,View高度的一半)开始,到(现在的触摸到的进度宽度,View高度的一半)还将绘制灰色背景线段
canvas.drawLine(paddingLeft, height / 2, currentProgress, height / 2, progressPaint);
// 要支持阴影下过必须关闭硬件加速
setLayerType(LAYER_TYPE_SOFTWARE, null);// 发光效果不支持硬件加速
// 绘制圆点指示器
canvas.drawCircle(currentProgress, getHeight() / 2, mCircleRadius, circlePaint);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: // 按下
// 设置进度值
setMotionProgress(event);
return true;
case MotionEvent.ACTION_MOVE: // 移动
// 获取当前触摸点,赋值给当前进度
setMotionProgress(event);
return true;
}
return super.onTouchEvent(event);
}
/**
* 根据用户手势计算进度值
* @param event 用户手势操作事件
*/
private void setMotionProgress(MotionEvent event) {
if (!isDrag) {
return;
}
// 获取当前触摸点,赋值给当前进度
currentProgress = (int) event.getX();
// 如果当前进度小于左边距
setCurrentProgress();
// 实际百分比进度数值
float result = ((currentProgress - paddingLeft) * 100) / maxProgress;
// 进行空值判断
if (onProgressListener != null) {
onProgressListener.onProgressChanged((int) result);
}
invalidate();
}
// 设置当前进度条进度,从1到100
public void setProgress(int progress) {
if (progress > 100 || progress < 0) {
return;
}
setCurrentProgress();
//设置当前进度的宽度
currentProgress = ((progress * maxProgress) / 100) + paddingLeft;
if (onProgressListener != null) {
onProgressListener.onProgressChanged(progress);
}
invalidate();
}
// 如果当前进度超出边界,将当前进度赋值为边界极值
private void setCurrentProgress() {
if (currentProgress < paddingLeft) { // 如果当前进度小于左边距
currentProgress = paddingLeft;
} else if (currentProgress > width - paddingRight) { // 如果当前进度大于 宽度 - 右边距
currentProgress = width - paddingRight;
}
}
/**
* 获取当前是否可拖动状态
* @return isDrag true 可拖动; false 不可拖动。
*/
public boolean isDrag() {
return isDrag;
}
/**
* 设置当前是否可拖动,立即重绘
* @param drag true 可拖动; false 不可拖动。
*/
public void setDrag(boolean drag) {
isDrag = drag;
invalidate();
}
private OnProgressListener onProgressListener;
public interface OnProgressListener {
void onProgressChanged(int progress);
}
// 设置拖动进度监听
public void setOnProgressListener(OnProgressListener onProgressListener) {
this.onProgressListener = onProgressListener;
}
/**
* 根据手机的分辨率从 dp 的单位 转成为 px(像素)
*/
public static int dip2px(Context context, float dpValue) {
float scale = 1;
scale = context.getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
/**
* 根据手机的分辨率从 px(像素) 的单位 转成为 dp
*/
public static int px2dip(Context context, float pxValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (pxValue / scale + 0.5f);
}
/**
* 根据手机的分辨率从 px(像素) 的单位 转成为 sp
*/
public static int px2sp(Context context, float pxValue) {
final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
return (int) (pxValue / fontScale + 0.5f);
}
}
2、资源文件values目录下attrs文件中(如果没有attrs就创建一个)添加一下代码:
<!-- DragProgressView可拖动进度条样式 -->
<declare-styleable name="dragProgressView">
<attr name="isDrag" format="boolean" />
<attr name="circleRadius" format="dimension" />
<attr name="progressHeight" format="dimension" />
<attr name="progressBackgroundColor" format="color" />
<attr name="progressColor" format="color" />
<attr name="circleColor" format="color" />
<attr name="circleShadowColor" format="color" />
<attr name="progressColorN" format="color" />
<attr name="circleColorN" format="color" />
<attr name="circleShadowColorN" format="color" />
</declare-styleable>
3、布局xml中使用
<com.example.view.DragProgressView
android:id="@+id/db_progress"
android:layout_marginTop="40dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="15dp"
android:layout_marginRight="15dp"
apps:isDrag="true"
apps:circleRadius="10dp"
apps:progressHeight="10dp"
apps:progressBackgroundColor="@color/text_colorc"
apps:progressColor="@color/toolbar_background"
apps:circleColor="@color/toolbar_background"
apps:circleShadowColor="@color/settings_chart_progress_circle_shadow"
apps:progressColorN="@color/text_color8"
apps:circleColorN="@color/text_color8"
apps:circleShadowColorN="@color/white" />
4、在Activity或Fragment中使用
DragProgressView dbProgress = findViewById(R.id.db_progress);
dbProgress.setOnProgressListener(new DragProgressView.OnProgressListener() {
@Override
public void onProgressChanged(int progress) {
Log.v("progress", "...dbProgress拖动进度:" + progress);
}
});
// chartProgress 从服务器获取或者自定义的显示默认值,默认进度为0,没有此种需求的可忽略此行
dbProgress.setProgress(chartProgress);
boolean isDrag = true;
// 设置是否可拖动,默认可拖动,没有禁止拖动需求的可忽略此行
dbProgress.setDrag(isDrag);
以上便是自定义view实现可拖动横向进度条的全部代码了,代码中有详细解释,如果不满足业务需求,可在此基础上继续修改。欢迎各位有相关业务需求的参考学习,转载请注明该地址。