自定义view教你如何打造圆形的seekbar
来源:互联网 发布:淘宝注册账号打电话 编辑:程序博客网 时间:2024/06/07 23:04
转载请标明出处:
http://blog.csdn.net/liuzg1220;
本文出自:【HugeBug的博客】
简介
seekBar大家都不陌生吧,圆形的seekbar不知道你有没有见过,见过的你又没有写过呢?
几年的一个项目,老板对我说android 自带的seekbar太丑了,让我写一个圆的,我想了想觉得这个难不倒我,于是就试着写了一个。先看效果图:
分析
首先这是一个自定的view,这是毋庸置疑的。这个自定义的view怎么写呢?根据上图的观察,我们看到外面的一个圆环,圆环里面有一个用来显示进度的textView。OK,带着这样的思路我们思考一下如何实现。
第一件事我能想到的是画两个同心圆,两个圆的半径不一样。产生一个圆环。
第二件要做的事我们要根据手指滑动位置画不同颜色的圆弧,这样就可以产生进度条的效果了。
第三件事我们要在中间的显示具体的数值,事实上具体的数字这里还有一个圆形的背景图。
分析完了,不扯蛋,直接上代码:
代码实现
自定义的MCircleSeekBar :
<span style="font-size:14px;">package com.lzg.circleseekbar.widget;/** * @author lzg * 2014/4/13 */import android.content.Context;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.RectF;import android.util.AttributeSet;import android.view.MotionEvent;import android.view.View;import com.lzg.circleseekbar.R;/** * The Class CircularSeekBar. */public class MCircleSeekBar extends View {/** The context */private Context mContext;/** seekbar变化监听 */private OnSeekChangeListener mListener;/** 画圆环的paint */private Paint circleColor;/** 画内圆的paint */private Paint innerColor;/** 圆环的Paint,其实是画弧形 */private Paint circleRing;/** 移动之后的角度 */private int angle = 0;/** 12点位置的开始角度 */private int startAngle = 270;/** 圆环宽度 */private int barWidth = 30;/** view的宽 */private int width;/** view的高 */private int height;/** seekbar总值 */private int maxProgress = 100;/** 当前的百分值 */private int progress;/** 百分值 */private int progressPercent;/** 内圆半径 */private float innerRadius;/** 外圆半径 */private float outerRadius;/** 圆心X坐标 */private float cx;/** 圆心Y坐标 */private float cy;/** 画圆图层左边边距 */private float left;/** 画圆图层右边边距 */private float right;/** T画圆图层顶端边距 */private float top;/** 画圆图层底边边距 */private float bottom;/** 背景图左边边距 */private float bgLeft;/** 背景图右边边距 */private float bgRight;/** 背景图顶端边距 */private float bgTop;/** 背景图底边边距 */private float bgBottom;/** progressMark X坐标 */private float dx;/** progressMark Y坐标 */private float dy;/** 12点钟位置的X坐标 */private float startPointX;/** 12点钟位置的Y坐标 */private float startPointY;/** * 标记的seekbar当前位置的X坐标,预设置值为12点位置 */private float markPointX;/** * T标记的seekbar当前位置的Y坐标,预设置值为12点位置 */private float markPointY;/** * 进度调整指数 */private float adjustmentFactor = 100;/** 正常状态下的进度条点 */private Bitmap progressMark;/** 手指按下后的进度条点 */private Bitmap progressMarkPressed;/** * 中间背景图 */private Bitmap progressBg;/** 是否手指按下的标志位 */private boolean IS_PRESSED = false;/** * 绘制圆中间的背景图的Paint */private Paint paint = null;private Canvas canvas = new Canvas();/** 画圆图层. */private RectF rect = new RectF();/** 背景图层 */RectF rectBg = new RectF();{mListener = new OnSeekChangeListener() {@Overridepublic void onProgressChange(MCircleSeekBar view, int newProgress) {}};/* * Paint.ANTI_ALIAS_FLAG为抗锯齿 */paint = new Paint(Paint.ANTI_ALIAS_FLAG);circleColor = new Paint();innerColor = new Paint();circleRing = new Paint();/* * 设置外圆的颜色为蓝色 */circleColor.setColor(Color.GRAY);// Set default background color to Gray/* * 设置内圆的颜色 */innerColor.setColor(Color.parseColor("#313131"));/* * 设置圆环的颜色 */circleRing.setColor(Color.parseColor("#ff33b5e5"));/* * Paint.ANTI_ALIAS_FLAG为抗锯齿 */circleColor.setAntiAlias(true);innerColor.setAntiAlias(true);circleRing.setAntiAlias(true);circleColor.setStrokeWidth(5);innerColor.setStrokeWidth(5);circleRing.setStrokeWidth(5);circleRing.setStyle(Paint.Style.FILL);}/** * MCircleSeekBar的构造方法. * * @param context * @param attrs * @param defStyle */public MCircleSeekBar(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);mContext = context;initDrawable();}/** * MCircleSeekBar的构造方法. * * @param context * @param attrs */public MCircleSeekBar(Context context, AttributeSet attrs) {super(context, attrs);mContext = context;initDrawable();}/** * MCircleSeekBar的构造方法. * * @param context */public MCircleSeekBar(Context context) {super(context);mContext = context;initDrawable();}/** * 初始化图片 */public void initDrawable() {progressMark = BitmapFactory.decodeResource(mContext.getResources(),R.drawable.seek_bar);progressMarkPressed = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.seek_bar1);progressBg = BitmapFactory.decodeResource(mContext.getResources(),R.drawable.open_perecent_bg);}/* * 重写view的计算方法 * * @see android.view.View#onMeasure(int, int) */@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);width = getWidth(); // Get View Widthheight = getHeight();// Get View Heightint size = (width > height) ? height : width; // Choose the smallercx = width / 2; // Center X for circlecy = height / 2; // Center Y for circleouterRadius = size / 2 * 80 / 100; // Radius of the outer circleinnerRadius = outerRadius - barWidth; // Radius of the inner circleleft = cx - outerRadius; // Calculate left bound of our rectright = cx + outerRadius;// Calculate right bound of our recttop = cy - outerRadius;// Calculate top bound of our rectbottom = cy + outerRadius;// Calculate bottom bound of our rectbgLeft = cx - innerRadius;bgRight = cx + innerRadius;bgTop = cy - innerRadius;bgBottom = cy + innerRadius;startPointX = getXByProgress(progress);startPointY = getYByProgress(progress);markPointX = startPointX;// Initial locatino of the marker X coordinatemarkPointY = startPointY;// Initial locatino of the marker Y coordinaterect.set(left, top, right, bottom); // assign size to rectrectBg.set(bgLeft, bgTop, bgRight, bgBottom);}/** * */@Overrideprotected void onDraw(Canvas canvas) {/* * 画外圆 */canvas.drawCircle(cx, cy, outerRadius, circleColor);/* * 画圆弧 */canvas.drawArc(rect, startAngle, angle, true, circleRing);/* * 画内圆 */canvas.drawCircle(cx, cy, innerRadius, innerColor);/* * 画seek点 */canvas.drawBitmap(progressBg, null, rectBg, paint);/* * 获取seek点X坐标 */dx = getXFromAngle();/* * 获取seek点Y坐标 */dy = getYFromAngle();/* * 绘制seek点的位置 */drawMarkerAtProgress(canvas);super.onDraw(canvas);}/** * 画progressMark(seek)在圆环上的位置. * * @param canvas */public void drawMarkerAtProgress(Canvas canvas) {if (IS_PRESSED) {canvas.drawBitmap(progressMarkPressed, dx, dy, null);} else {canvas.drawBitmap(progressMark, dx, dy, null);}}/** * 获取seek点X坐标 * * @return */public float getXFromAngle() {int size1 = progressMark.getWidth();int size2 = progressMarkPressed.getWidth();int adjust = (size1 > size2) ? size1 : size2;float x = markPointX - (adjust / 2);return x;}/** * 获取seek点Y坐标 * * @return */public float getYFromAngle() {int size1 = progressMark.getHeight();int size2 = progressMarkPressed.getHeight();int adjust = (size1 > size2) ? size1 : size2;float y = markPointY - (adjust / 2);return y;}/** * 获取圆弧角度 * * @return the angle */public int getAngle() {return angle;}/** * 设置圆弧的角度 * * @param angle */public void setAngle(int angle) {this.angle = angle;float donePercent = (((float) this.angle) / 360) * 100;float progress = (donePercent / 100) * getMaxProgress();setProgressPercent(Math.round(donePercent));setProgress(Math.round(progress));}/** * 设置seekbar变化的监听 * * @param listener */public void setSeekBarChangeListener(OnSeekChangeListener listener) {mListener = listener;}/** * 获取seekbar变化的监听 * * @return the seek bar change listener */public OnSeekChangeListener getSeekBarChangeListener() {return mListener;}/** * 获得bar宽度 * * @return the bar width */public int getBarWidth() {return barWidth;}/** * 设置bar宽度 * * @param barWidth */public void setBarWidth(int barWidth) {this.barWidth = barWidth;}/** * 定义seekbar变化监听接口 * * @see OnSeekChangeEvent */public interface OnSeekChangeListener {/** * On progress change. * * @param view * @param newProgress */public void onProgressChange(MCircleSeekBar view, int newProgress);}/** * Gets the max progress. * * @return the max progress */public int getMaxProgress() {return maxProgress;}/** * * @param maxProgress * */public void setMaxProgress(int maxProgress) {this.maxProgress = maxProgress;}/** * * @return the progress */public int getProgress() {return progress;}/** * * @param progress * */public void setProgress(int progress) {this.progress = progress;if (!IS_PRESSED) {int newPercent = (this.progress * 100) / this.maxProgress;int newAngle = (newPercent * 360) / 100;this.setAngle(newAngle);this.setProgressPercent(newPercent);}mListener.onProgressChange(this, this.getProgress());}/** * * @return */public int getProgressPercent() {return progressPercent;}/** * * @param progressPercent */public void setProgressPercent(int progressPercent) {this.progressPercent = progressPercent;}/** * * @param color */public void setRingBackgroundColor(int color) {circleRing.setColor(color);}/** * * @param color */public void setBackGroundColor(int color) {innerColor.setColor(color);}/** * * @param color */public void setProgressColor(int color) {circleRing.setColor(color);}/** * 重写onTouch方法 */@Overridepublic boolean onTouchEvent(MotionEvent event) {float x = event.getX();float y = event.getY();boolean up = false;switch (event.getAction()) {case MotionEvent.ACTION_DOWN:moved(x, y, up);break;case MotionEvent.ACTION_MOVE:moved(x, y, up);break;case MotionEvent.ACTION_UP:up = true;moved(x, y, up);break;}return true;}/** * seek点移动方法 * * @param x * the x * @param y * the y * @param up * the up */private void moved(float x, float y, boolean up) {float distance = (float) Math.sqrt(Math.pow((x - cx), 2)+ Math.pow((y - cy), 2));if (distance < outerRadius + adjustmentFactor&& distance > innerRadius - adjustmentFactor && !up) {IS_PRESSED = true;/* * 根据三角函数整切定理计算得到X.Y的坐标 */markPointX = (float) (cx + outerRadius* Math.cos(Math.atan2(x - cx, cy - y) - (Math.PI / 2)));markPointY = (float) (cy + outerRadius* Math.sin(Math.atan2(x - cx, cy - y) - (Math.PI / 2)));float degrees = (float) ((float) ((Math.toDegrees(Math.atan2(x - cx, cy - y)) + 360.0)) % 360.0);if (degrees < 0) {degrees += 2 * Math.PI;}setAngle(Math.round(degrees));invalidate();} else {IS_PRESSED = false;invalidate();}}public float getXByProgress(int progress) {float x = 0;float angle = (float) (2 * progress * Math.PI / 100);x = (float) (cx + outerRadius * Math.cos(angle - Math.PI / 2));return x;}public float getYByProgress(int progress) {float y = 0;float angle = (float) (2 * progress * Math.PI / 100);y = (float) (cy + outerRadius * Math.sin(angle - Math.PI / 2));return y;}public void setMarkPointXY(int progress) {this.progress = progress;}/** * * @return */public float getAdjustmentFactor() {return adjustmentFactor;}/** * * @param adjustmentFactor */public void setAdjustmentFactor(float adjustmentFactor) {this.adjustmentFactor = adjustmentFactor;}}</span>布局文件activity_main.xml:
<span style="font-size:14px;"><FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_gravity="center_horizontal" android:background="#e0313131" tools:context=".MainActivity" > <com.lzg.circleseekbar.widget.MCircleSeekBar android:id="@+id/m_circleSeekBar_set_perencet" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" /> <RelativeLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" > <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:orientation="horizontal" > <TextView android:id="@+id/tv_perencet_set_perencet" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="0" android:textColor="#ffffff" android:textSize="60sp" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="%" android:textColor="#ffffff" android:textSize="40sp" /> </LinearLayout> </RelativeLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="bottom" android:orientation="vertical" > <TextView style="@style/line_text_style" /> </LinearLayout></FrameLayout></span>
BaseActivity:
<span style="font-size:14px;">package com.lzg.circleseekbar;import android.app.Activity;import android.view.View;/** * * @author lzg * */public class BaseActivity extends Activity {/** * 解决强制类型转换的问题 * * @param id * @return */@SuppressWarnings("unchecked")public <T extends View> T getViewById(int id) {return (T) findViewById(id);}}</span>界面调用的主MainAcitivity:
<span style="font-size:14px;">package com.lzg.circleseekbar;import android.os.Bundle;import android.widget.TextView;import com.lzg.circleseekbar.widget.MCircleSeekBar;import com.lzg.circleseekbar.widget.MCircleSeekBar.OnSeekChangeListener;/** * * @author lzg * */public class MainActivity extends BaseActivity {private TextView tvPerencetValue;private MCircleSeekBar mCircleSeekBar;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);tvPerencetValue = getViewById(R.id.tv_perencet_set_perencet);mCircleSeekBar = getViewById(R.id.m_circleSeekBar_set_perencet);mCircleSeekBar.setSeekBarChangeListener(new OnSeekChangeListener() {public void onProgressChange(MCircleSeekBar view, int newProgress) {tvPerencetValue.setText(Integer.toString(view.getProgress()));}});}}</span>
到此代码全部结束了!
是不是感觉代码有点多,思路清晰了,也就水到渠成了。。。
如果你觉得有用就关注我吧,在这里将不定期的发表原创文章!如果你觉得有用就留个言,点个赞吧!
下面是我微信公众号:如果你喜欢就扫扫看吧!
源码下载
1 0
- 自定义view教你如何打造圆形的seekbar
- android自定义view-打造圆形ImageView(一)
- android自定义view-打造圆形ImageView(二)
- android自定义view-打造圆形ImageView(三)
- android自定义view-打造圆形ImageView(一)
- CircularSeekBar 自定义 圆形 seekbar
- 打造你自己的圆形或圆环形progressBar——圆形SeekBar演变圆形progressbar
- 自定义View-打造属于你的炫酷按钮
- android自定义view-打造圆形ImageView(四)终结篇
- 自定义View?一起来打造一个圆形进度条吧。
- 安卓自定义圆形seekBar
- 自定义view,垂直seekbar
- 仿MIUI计时器+圆形SeekBar+钟表表盘UI 三位一体式自定义View
- android_studio的自定义View的圆形进度条
- 手把手教你打造一个心电图效果View Android自定义View
- 自定义可圆形移动的view
- 自定义View(带进度的圆形进度条)
- 圆形图片的实现自定义view
- session匹配错误
- 图片匹配技术之巴氏距离
- hdu-2199-Can you solve this equation?
- Android开发学习之路-- 关于服务Service
- HTML 学习(一)之 简介
- 自定义view教你如何打造圆形的seekbar
- 好文章
- 在ubuntu linux下搭建Java Web开发环境
- hdu4465
- 公共子序列的个数
- bzoj 4569: [Scoi2016]萌萌哒 (st表+并查集)
- 使用友盟SDK提交Appstore审核被拒的常见解决方法
- 32位保护模式学习小结(2)---任务的隔离和特权级保护
- HDU 4496 D-City (并查集)