阅读 63

Android技术分享| 自定义View实现使用更方便的SeekBar

前言

Android中自带的SeekBar个人感觉用起来很麻烦,调整一些颜色之类的需要单独写一个XML文件,内容感觉也很啰嗦。刚好我们的白板Demo开发中需要用到SeekBar,所以干脆实现了一个满足基本功能的SeekBar,支持在xml布局中指定各种颜色属性,也支持代码动态设置颜色。用起来更顺心一些。

SeekBar的代码请查看Github地址:白板Demo,Demo地址请:点击这里 即拿即用,非常方便;p(记得将res/values/styles.xml中的属性一并复制走)

效果

在这里插入图片描述

实现

由于不涉及到动画,这种自定义View做起来还是相当简单的。首先在res/values/styles.xml文件中定义好自己需要的属性,我这里贴上我定义的属性:

<declare-styleable name="SeekBarWidget">     <attr name="seek_maxProgress" format="integer" /><!-- 最大progress -->     <attr name="seek_minProgress" format="integer" /><!-- 最小progress -->     <attr name="seek_progress" format="integer" /><!-- 当前progress(默认值) -->     <attr name="seek_circleRadius" format="dimension" /><!-- seekBar中间圆形的半径 -->     <attr name="seek_circleStrokeWidth" format="dimension" /><!-- seekBar中间圆形外的border -->     <attr name="seek_lineHeight" format="dimension" /><!-- lineHeight有点词不达意,其实是进度条的高度 -->     <attr name="seek_backgroundColor" format="color" /><!-- 进度条的背景色 -->     <attr name="seek_circleStrokeColor" format="color" /><!-- border的颜色 -->     <attr name="seek_maxColor" format="color" /><!-- 进度条的前景色 -->     <attr name="seek_startColor" format="color" /><!-- 如果需要渐变色则设置此属性,否则这条属性置空即可 --> </declare-styleable> 复制代码

属性定义好以后,在View的构造中解析一下:

public SeekBarWidget(Context context) {     this(context, null); }                                                                                                                             public SeekBarWidget(Context context, @Nullable AttributeSet attrs) {     this(context, attrs, 0); }                                                                                                                             public SeekBarWidget(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {     super(context, attrs, defStyleAttr);                                                                                                                                if (null != attrs) {         TypedArray typedArray = getResources().obtainAttributes(attrs, R.styleable.SeekBarWidget);         minProgress = typedArray.getInt(R.styleable.SeekBarWidget_seek_minProgress, 0);         maxProgress = typedArray.getInt(R.styleable.SeekBarWidget_seek_maxProgress, 100) - minProgress;         progress = typedArray.getInt(R.styleable.SeekBarWidget_seek_progress, 0) - minProgress;         if (progress < 0) progress = minProgress;         circleRadius = typedArray.getDimension(R.styleable.SeekBarWidget_seek_circleRadius, 20f);         circleStrokeWidth = typedArray.getDimension(R.styleable.SeekBarWidget_seek_circleStrokeWidth, 5f);         lineHeight = typedArray.getDimension(R.styleable.SeekBarWidget_seek_lineHeight, 5f);         backgroundColor = typedArray.getColor(R.styleable.SeekBarWidget_seek_backgroundColor, Color.parseColor("#F0F0F0"));         circleStrokeColor = typedArray.getColor(R.styleable.SeekBarWidget_seek_circleStrokeColor, Color.WHITE);         int maxColor = typedArray.getColor(R.styleable.SeekBarWidget_seek_maxColor, Color.RED);         int startColor = typedArray.getColor(R.styleable.SeekBarWidget_seek_startColor, maxColor);         colorTransition = new ColorTransition(startColor, maxColor);         typedArray.recycle();         percentage = progress * 1.0f / maxProgress;         horizontalPadding = circleRadius * 2 + circleStrokeWidth * 2 + getPaddingStart() + getPaddingEnd();         mPaddingLeft = horizontalPadding - getPaddingEnd() - circleRadius - circleStrokeWidth;         return;     }     maxProgress = 100;     minProgress = 0;     circleRadius = 20;     circleStrokeWidth = 5;     lineHeight = 5;     backgroundColor = Color.parseColor("#F0F0F0");     colorTransition = new ColorTransition(Color.WHITE, Color.RED);     circleStrokeColor = Color.WHITE;     horizontalPadding = circleRadius * 2 + circleStrokeWidth * 2 + getPaddingStart() + getPaddingEnd();     mPaddingLeft = horizontalPadding - getPaddingEnd() - circleRadius - circleStrokeWidth; } 复制代码

接下来是onMeasureonDraw,由于我的应用场景宽高已经固定好了,所以没有写onMeasure,有需求的朋友可以自己实现一下~ 而绘制的思路很简单:

  1. 先绘制一条背景色的线,线的高度由外部指定。(设置StrokeCap为ROUND)

  2. 再绘制一条前景色的线,线高度同样由外部指定,这里我读取的是同一个属性lineHeight,也可以根据需求不同设置不一样的高度;p

  3. 接下来绘制seekBar的圆,这里border也直接使用drawCircle来实现,先绘制border。(circleRadius + circleStrokeWidth / 2f)

  4. 最后绘制中间的圆

onDraw:

@Override protected void onDraw(Canvas canvas) {     final int width = (int) (getMeasuredWidth() - horizontalPadding);     final int height = getMeasuredHeight();     paint.setColor(backgroundColor);     paint.setStrokeWidth(lineHeight);     paint.setStyle(Paint.Style.FILL_AND_STROKE);     paint.setStrokeCap(Paint.Cap.ROUND);     canvas.drawLine(mPaddingLeft, height >> 1, mPaddingLeft + width, height >> 1, paint);     //float percentage = progress / maxProgress;     int currColor = colorTransition.getValue(percentage);     paint.setColor(currColor);     canvas.drawLine(mPaddingLeft, height >> 1, mPaddingLeft + width * percentage, height >> 1, paint);     // draw circle border     paint.setColor(circleStrokeColor);     canvas.drawCircle(mPaddingLeft + width * percentage, height >> 1, circleRadius + (circleStrokeWidth / 2f), paint);     // draw circle inside color     paint.setColor(currColor);     canvas.drawCircle(mPaddingLeft + width * percentage, height >> 1, circleRadius, paint); } 复制代码

最后,处理一下onTouch事件。思路也很简单,从down、move、up事件中判断一下是否为横向滚动,是的话计算一下距离,更改progress进度并通知View重新绘制即可:

private float downX; private float downY; @Override public boolean onTouchEvent(MotionEvent event) {     boolean intercept = false;     switch (event.getAction()) {         case MotionEvent.ACTION_DOWN:             downX = event.getX();             downY = event.getY();             intercept = true;             break;         case MotionEvent.ACTION_MOVE:             float moveX = event.getX();             float moveY = event.getY();             float xMove = Math.abs(moveX - downX) - Math.abs(moveY - downY);             if (xMove > 0f) {                 float hX = moveX - downX;                 boolean toLeft = hX < 0.0f;                 float movePercent = Math.abs(hX) / getMeasuredWidth();                 if (percentage < 1.0f && !toLeft) {                     percentage += movePercent;                 } else if (percentage > 0f && toLeft) {                     percentage -= movePercent;                 }                 if (percentage < 0f) percentage = 0f;                 if (percentage > 1f) percentage = 1f;                 progress = (int) Math.floor(percentage * maxProgress);             }             intercept = true;             downX = moveX;             downY = moveY;             postInvalidate();             if (null != mListener && beforeProgress != getProgress()) {                 beforeProgress = getProgress();                 mListener.onProgress(getProgress());             }             break;         case MotionEvent.ACTION_UP:             break;     }     return intercept || super.onTouchEvent(event);     //return true; } @Override public boolean dispatchTouchEvent(MotionEvent event) {     getParent().requestDisallowInterceptTouchEvent(true);     return super.dispatchTouchEvent(event); } 复制代码

OK,一个复制即用的自定义SeekBar便完成了。比官方原版的SeekBar使用起来顺眼多了(「・ω・)「

(欢迎下载Demo玩一玩!Demo地址)

在这里插入图片描述


作者:anyRTC
链接:https://juejin.cn/post/7054376809931997214

文章分类
ANDROID
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐