自定义view练手,效果图如下:
实现功能 可设置圆环颜色和线宽及触摸后的颜色和线宽 可设置圆环内圈显示的文本内容及字体大小、颜色 可设置触摸点的图片 可设置触摸的有效范围源码github链接使用示例<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="circlebar.oden.com.circleseekbardemo.MainActivity"> <circlebar.oden.com.circleseekbar.CircleSeekbar android:id="@+id/circle_seekbar" android:layout_centerInParent="true" android:layout_width="300dp" android:layout_height="300dp" /> <TextView android:id="@+id/tv_progress" android:text="0%" android:textColor="#FF383838" android:textSize="18sp" android:layout_centerInParent="true" android:layout_width="wrap_content" android:layout_height="wrap_content" /></RelativeLayout>public class MainActivity extends AppCompatActivity { String TAG = "MainActivity"; CircleSeekbar circleSeekbar; TextView tv_progress; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); circleSeekbar = (CircleSeekbar) findViewById(R.id.circle_seekbar); tv_progress = (TextView) findViewById(R.id.tv_progress); circleSeekbar.setOnSeekBarChangeListener(new CircleSeekbar.OnSeekBarChangeListener() { @Override public void onProgressChanged(CircleSeekbar circleSeekbar, int progress, boolean fromUser) { Log.d(TAG, "onProgressChanged progress: " + progress); tv_progress.setText(progress + "%"); } }); }}实现过程attrs中定义属性可设置的属性如下<?xml version="1.0" encoding="utf-8"?><resources> <declare-styleable name="CircleSeekbar"> <attr name="circleColor" format="color" /> <attr name="progressColor" format="color" /> <attr name="textColor" format="color" /> <attr name="textSize" format="dimension" /> <attr name="progress" format="integer" /> <attr name="maxProgress" format="integer" /> <attr name="progressWidth" format="dimension" /> <attr name="circleWidth" format="dimension" /> <attr name="thumb" format="reference" /> <attr name="enabled" format="boolean" /> <attr name="touchInside" format="boolean" /> </declare-styleable></resources>获取属性及初始化画笔 private void init(Context context, AttributeSet attrs) { float density = context.getResources().getDisplayMetrics().density; int thumbHalfheight = 0; int thumbHalfWidth = 0; circleColor = getResources().getColor(R.color.circleseekbar_gray); progressColor = getResources().getColor(R.color.circleseekbar_blue_light); textColor = getResources().getColor(R.color.circleseekbar_text_color); progressWidth = (int) (progressWidth * density); circleWidth = (int) (circleWidth * density); textSize = (int) (textSize * density); padding = (int) (padding * density); mThumb = getResources().getDrawable(R.drawable.seekbar_control_selector); if (null != attrs) { TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CircleSeekbar); circleColor = typedArray.getColor(R.styleable.CircleSeekbar_circleColor, circleColor); progressColor = typedArray.getColor(R.styleable.CircleSeekbar_circleColor, progressColor); textColor = typedArray.getColor(R.styleable.CircleSeekbar_textColor, textColor); textSize = typedArray.getColor(R.styleable.CircleSeekbar_textSize, textSize); circleWidth = (int) typedArray.getDimension(R.styleable.CircleSeekbar_circleWidth, circleWidth); progressWidth = (int) typedArray.getDimension(R.styleable.CircleSeekbar_progressWidth, progressWidth); mProgress = typedArray.getInteger(R.styleable.CircleSeekbar_progress, mProgress); maxProgress = typedArray.getInteger(R.styleable.CircleSeekbar_maxProgress, maxProgress); mTouchInside = typedArray.getBoolean(R.styleable.CircleSeekbar_touchInside, mTouchInside); Drawable thumb = typedArray.getDrawable(R.styleable.CircleSeekbar_thumb); if (thumb != null) { mThumb = thumb; } thumbHalfheight = (int) mThumb.getIntrinsicHeight() / 2; thumbHalfWidth = (int) mThumb.getIntrinsicWidth() / 2; mThumb.setBounds(-thumbHalfWidth, -thumbHalfheight, thumbHalfWidth, thumbHalfheight); typedArray.recycle(); } padding = thumbHalfheight + padding; maxWidth = circleWidth > progressWidth ? circleWidth : progressWidth; mProgress = (mProgress > maxProgress) ? maxProgress : mProgress; mProgress = (mProgress < 0) ? 0 : mProgress; circlePaint = new Paint(); circlePaint.setColor(circleColor); circlePaint.setAntiAlias(true); circlePaint.setStyle(Paint.Style.STROKE); circlePaint.setStrokeWidth(circleWidth); progressPaint = new Paint(); progressPaint.setColor(progressColor); progressPaint.setAntiAlias(true); progressPaint.setStyle(Paint.Style.STROKE); progressPaint.setStrokeWidth(progressWidth); paintDegree = new Paint(); paintDegree.setColor(textColor); paintDegree.setAntiAlias(true); paintDegree.setStyle(Paint.Style.FILL); paintDegree.setStrokeWidth(1); paintDegree.setTextSize(textSize); }测量获取圆的中心坐标、半径、以及用来画弧的RectF@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int height = getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec); int width = getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec); mWidth = Math.min(width, height); centreX = mWidth / 2; centreY = mWidth / 2; float left = getPaddingLeft() + maxWidth / 2 + padding; float top = getPaddingTop() + maxWidth / 2 + padding; float right = mWidth - getPaddingRight() - maxWidth / 2 - padding; float bottom = mWidth - getPaddingBottom() - maxWidth / 2 - padding; diameter = mWidth - getPaddingLeft() - getPaddingRight() - maxWidth - padding * 2; radius = diameter / 2; mCircleRect.set(left, top, left + diameter, top + diameter); updateThumbPosition(mCurAngle); setTouchInSide(mTouchInside); setMeasuredDimension(mWidth, mWidth); }绘制view @Override protected void onDraw(Canvas canvas) { canvas.drawColor(Color.RED); canvas.drawCircle(mWidth / 2, mWidth / 2, radius, circlePaint); canvas.drawArc(mCircleRect, -90, (float) mCurAngle, false, progressPaint); drawText(canvas); canvas.translate(mThumbXPos, mThumbYPos); mThumb.draw(canvas); } private void drawText(Canvas canvas) { float rotation = 360 / degreeText.length; for (int i = 0; i < degreeText.length; i++) { canvas.drawText(degreeText[i], centreX - paintDegree.measureText(degreeText[i]) / 2, 100/*字体的高度*/, paintDegree); canvas.rotate(rotation, centreX, centreY); } }设置触摸事件 @Override public boolean onTouchEvent(MotionEvent event) { if (enable) { this.getParent().requestDisallowInterceptTouchEvent(true);//通知父控件勿拦截本次触摸事件 switch (event.getAction()) { case MotionEvent.ACTION_DOWN: updateOnTouch(event); break; case MotionEvent.ACTION_MOVE: updateOnTouch(event); break; case MotionEvent.ACTION_UP: isFirstTouch = true; setPressed(false); this.getParent().requestDisallowInterceptTouchEvent(false); break; case MotionEvent.ACTION_CANCEL: isFirstTouch = true; setPressed(false); this.getParent().requestDisallowInterceptTouchEvent(false); break; } return true; } return false; }触摸事件中计算相应的角度及进度条的值 private void updateOnTouch(MotionEvent event) { boolean ignoreTouch = ignoreTouch(event.getX(), event.getY()); if (ignoreTouch) { return; } setPressed(true); getTouchDegrees(event.getX(), event.getY()); mProgress = getProgressForAngle(mCurAngle); updateProgress(mProgress, true); } //根据触摸点的坐标获取角度值 private double getTouchDegrees(float xPos, float yPos) { float x = xPos - centreX; float y = yPos - centreY; // convert to arc Angle double angle = Math.toDegrees(Math.atan2(y, x) + (Math.PI / 2)); if (angle < 0) { angle = 360 + angle; } if (isOnlyScrollOneCircle) { if (mCurAngle > 270 && angle < 90) { mCurAngle = 360; } else if (mCurAngle < 90 && angle > 270) { mCurAngle = 0; } else { mCurAngle = angle; } } else { mCurAngle = angle; } if (isFirstTouch) { //如果是滑动前第一次点击则总是移动到该度数 mCurAngle = angle; isFirstTouch = false; } Log.d(TAG, "getTouchDegrees: " + angle); return mCurAngle; } //根据触摸的角度值获取滑动条progree的值 private int getProgressForAngle(double angle) { int touchProgress = (int) Math.round(maxProgress * angle / 360); touchProgress = (touchProgress < 0) ? INVALID_PROGRESS_VALUE : touchProgress; touchProgress = (touchProgress > maxProgress) ? INVALID_PROGRESS_VALUE : touchProgress; return touchProgress; } //更新view private void updateProgress(int progress, boolean fromUser) { if (progress == INVALID_PROGRESS_VALUE) { return; } progress = (progress > maxProgress) ? maxProgress : progress; progress = (progress < 0) ? 0 : progress; mProgress = progress; if (onSeekBarChangeListener != null) { onSeekBarChangeListener.onProgressChanged(this, progress, fromUser); } updateThumbPosition(mCurAngle); invalidate(); } //更新thum的坐标 private void updateThumbPosition(double angle) { double cos = -Math.cos(Math.toRadians(angle)); if (angle < 180) { mThumbXPos = (int) (getMeasuredWidth() / 2 + Math.sqrt(1 - cos * cos) * radius); } else { mThumbXPos = (int) (getMeasuredWidth() / 2 - Math.sqrt(1 - cos * cos) * radius); } mThumbYPos = (int) (centreX + radius * (float) cos); }设置触摸的生效范围同时根据触摸状态同步改变thum的状态//设置触摸生效范围 public void setTouchInSide(boolean isEnabled) { int thumbHalfheight = (int) mThumb.getIntrinsicHeight() / 2; int thumbHalfWidth = (int) mThumb.getIntrinsicWidth() / 2; mTouchInside = isEnabled; if (mTouchInside) { mTouchIgnoreRadius = (float) radius / 4; } else { // Don't use the exact radius makes interaction too tricky mTouchIgnoreRadius = radius - Math.min(thumbHalfWidth, thumbHalfheight); } } private boolean ignoreTouch(float xPos, float yPos) { boolean ignore = false; float x = xPos - centreX; float y = yPos - centreY; float touchRadius = (float) Math.sqrt(((x * x) + (y * y))); if (touchRadius < mTouchIgnoreRadius) { ignore = true; } return ignore; } //重写drawableStateChanged,同步改变thum的状态 @Override protected void drawableStateChanged() { super.drawableStateChanged(); if (mThumb != null && mThumb.isStateful()) { int[] state = getDrawableState(); mThumb.setState(state); } invalidate(); } 设置监听器public interface OnSeekBarChangeListener { void onProgressChanged(CircleSeekbar circleSeekbar, int progress, boolean fromUser); } public void setOnSeekBarChangeListener(OnSeekBarChangeListener onSeekBarChangeListener) { this.onSeekBarChangeListener = onSeekBarChangeListener; }
Android自定义view-CircleSeekbar
发布日期:2021-10-25 13:14:39
浏览次数:7
分类:技术文章
本文共 10147 字,大约阅读时间需要 33 分钟。
转载于:https://www.cnblogs.com/Free-Thinker/p/9897706.html
转载地址:https://blog.csdn.net/weixin_30909575/article/details/99071601 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!
发表评论
最新留言
感谢大佬
[***.8.128.20]2024年04月01日 05时30分00秒
关于作者
喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
如何避免Java中的回调“地狱”
2019-04-26
【注意】关于Redis存在远程命令执行漏洞的安全公告
2019-04-26
微服务之旅:从 Netflix OSS 到 Istio Service Mesh
2019-04-26
自律到极致-人生才精致:第10期
2019-04-26
史上最易懂的Kubernetes儿童插图指南
2019-04-26
Spring Batch 入门教程
2019-04-26
使用 Optional 摆脱 NullPointException 的折磨
2019-04-26
装x撩m必备的16条Linux 命令,了解一下?
2019-04-26
开发那么多年,还不知道XSS怎么防?
2019-04-26
一文学会Java死锁和CPU 100% 问题的排查技巧
2019-04-26
7月编程语言排行榜来了,为什么不同媒体报道的结果不一样?
2019-04-26
开发高质量的软件要付出什么样的代价?
2019-04-26
JAVA:红黑树详解
2019-04-26
可能是全网最好的MySQL重要知识点 | 面试必备
2019-04-26
Spring Aware 到底是个啥?
2019-04-26
Spring、Spring MVC、Spring Boot三者的关系还傻傻分不清楚?
2019-04-26
唠唠面试常问的:面向对象六大原则
2019-04-26
追踪JVM中的本地内存
2019-04-26
RabbitMQ 延迟消息的极限是多少?
2019-04-26
简单的 HTTP 调用,为什么时延这么大?
2019-04-26