Android自定义View——使用贝塞尔曲线实现流量进度条
2016-06-30 16:29
609 查看
第一次写带图片的博客,多少还是有点紧张,效果不好,请将就着看,前面的图是今天要写的控件的效果图,元素不多,分别是一个按钮和一个自定义的控件。
在此以前,我看过许多的书,比如《Android群英传》、《第一行代码》等,也看了很多大神的博客,但是即便是这样,当我看到这么多代码的时候,一直都没有真正的动手去敲过这些代码,以至于我总是觉得自定义View是一个多么高深莫测的技术,我们这些小白是难以触及的,但是当昨晚看了一篇鸡汤之后,觉得人还是要学会专注,要耐得住寂寞,要沉得住气。所以在未来的几天,我也会持续的更新自己的博客,希望能够得到大家的监督,也希望能够一起成长。
一般来说,不到迫不得已,还是不要去自定义自己的控件,毕竟现在Android API已经给我提供了功能这么强大的控件了,而且你也能够发现,就算是这么强大的谷歌,很多控件也还是有自己的bug,更何况我们自己写的控件,当然了,通过自定义控件,来加深我们对Android系统的控件的了解,这也是进阶的一个好方法。好了,废话我先说到这里,下面开始今天的主题。
贝塞尔曲线这个话题,很多大神的文章都有涉及,在郭神的微信公众号里面专门有一篇文章讲到了:http://mp.weixin.qq.com/s?__biz=MzA5MzI3NjE2MA==&mid=2650236243&idx=1&sn=00f003c809b1e3a3e5e7cb372d3ec970&scene=0#wechat_redirect ,所以我这里就不谈这个了。下面就开始进入编码阶段
新建一个Class文件,命名为MyLineView,让它继承View,实现它的三个构造函数,紧接着初始化一些画笔、Path,圆等数据。代码如下:
1.定义变量
private Paint mPaint, mPaint2; private Path mPath = new Path(); protected int mViewWidth, mViewHeight; protected int mWidth, mHeight; private float r, rArc, x; private float percent = 0.5f; private RectF rectF; private PointF mPointF = new PointF(0, 0);
在构造函数里面初始化数据
public MyLineView(Context context, AttributeSet attrs) { super(context, attrs); mPaint = new Paint(); mPaint.setColor(Color.BLACK); mPaint.setStrokeWidth(3); mPaint.setStyle(Paint.Style.STROKE); mPaint.setTextSize(100); mPaint2 = new Paint(); mPaint2.setColor(Color.CYAN); mPaint2.setStrokeWidth(8); mPaint2.setStyle(Paint.Style.FILL); }
准备工作已经做好了,紧接着我们复写View的一个onSizeChanged()方法,故名思议,就是当控件的大小发生改变的时候调用。我们在这里对变量进行赋值,代码如下:
@Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); /**控件的高宽*/ mViewWidth = w; mViewHeight = h; /**与屏幕左边的距离和距离右边的距离*/ mWidth = mViewWidth - getPaddingLeft() - getPaddingRight(); mHeight = mViewHeight - getPaddingTop() - getPaddingBottom(); /**定义半径*/ r = Math.min(mWidth, mHeight) * 0.4f; rectF = new RectF(-r, -r, r, r); }
好了,这个时候的变量已经拥有值了,那可以开始写最重要的操作了,开始写onDraw();
@Override protected void onDraw(Canvas canvas) { // super.onDraw(canvas); /** * 画曲线和外围的圆 */ canvas.translate(mViewWidth / 2, mViewHeight / 2); canvas.drawCircle(0, 0, r, mPaint); rArc = r * (1 - 2 * percent); double angle = Math.acos((double) rArc / r); x = r * (float) Math.sin(angle); mPath.addArc(rectF, 90 - (float) Math.toDegrees(angle), (float) Math.toDegrees(angle) * 2); mPath.moveTo(-x, rArc); mPath.rQuadTo(x / 2, -r / 8, x, 0); mPath.rQuadTo(x / 2, r / 8, x, 0); canvas.drawPath(mPath, mPaint2); mPath.rewind(); /** * 画文字 */ NumberFormat numberFormat = NumberFormat.getPercentInstance(); numberFormat.setMinimumFractionDigits(1); textCenter(new String[] { numberFormat.format(percent) }, mPaint, canvas, mPointF, Paint.Align.CENTER); }
文字的处理:
/** * 多行文本居中、居右、居左 * * @param strings * 文本字符串列表 * @param paint * 画笔 * @param canvas * 画布 * @param point * 点的坐标 * @param align * 居中、居右、居左 */ protected void textCenter(String[] strings, Paint paint, Canvas canvas, PointF point, Paint.Align align) { paint.setTextAlign(align); Paint.FontMetrics fontMetrics = paint.getFontMetrics(); float top = fontMetrics.top; float bottom = fontMetrics.bottom; int length = strings.length; float total = (length - 1) * (-top + bottom) + (-fontMetrics.ascent + fontMetrics.descent); float offset = total / 2 - bottom; for (int i = 0; i < length; i++) { float yAxis = -(length - i - 1) * (-top + bottom) + offset; canvas.drawText(strings[i], point.x, point.y + yAxis, paint); } }
好了,截止目前已经把控件写好了,但是作为一个控件,数据不可能固定不变,所以向外暴露一个方法,用于改变进度的大小;
public void setProgress(float percent) { if (percent != 0) { this.percent = percent; } else { this.percent = 0; } /**重绘*/ invalidate(); }
好了全部流程都在这里了,最后再贴上全部的代码;
package com.example.view.weight; import java.text.NumberFormat; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Path; import android.graphics.PointF; import android.graphics.RectF; import android.util.AttributeSet; import android.view.View; /** * 贝塞尔曲线 * * @author cheng * */ public class MyLineView extends View { private Paint mPaint, mPaint2; private Path mPath = new Path(); protected int mViewWidth, mViewHeight; protected int mWidth, mHeight; private float r, rArc, x; private float percent = 0.5f; private RectF rectF; private PointF mPointF = new PointF(0, 0); public MyLineView(Context context) { this(context, null); } public MyLineView(Context context, AttributeSet attrs) { super(context, attrs); mPaint = new Paint(); mPaint.setColor(Color.BLACK); mPaint.setStrokeWidth(3); mPaint.setStyle(Paint.Style.STROKE); mPaint.setTextSize(100); mPaint2 = new Paint(); mPaint2.setColor(Color.CYAN); mPaint2.setStrokeWidth(8); mPaint2.setStyle(Paint.Style.FILL); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); mViewWidth = w; mViewHeight = h; mWidth = mViewWidth - getPaddingLeft() - getPaddingRight(); mHeight = mViewHeight - getPaddingTop() - getPaddingBottom(); r = Math.min(mWidth, mHeight) * 0.4f; rectF = new RectF(-r, -r, r, r); } @Override protected void onDraw(Canvas canvas) { // super.onDraw(canvas); /** * 画曲线和外围的圆 */ canvas.translate(mViewWidth / 2, mViewHeight / 2); canvas.drawCircle(0, 0, r, mPaint); rArc = r * (1 - 2 * percent); double angle = Math.acos((double) rArc / r); x = r * (float) Math.sin(angle); mPath.addArc(rectF, 90 - (float) Math.toDegrees(angle), (float) Math.toDegrees(angle) * 2); mPath.moveTo(-x, rArc); mPath.rQuadTo(x / 2, -r / 8, x, 0); mPath.rQuadTo(x / 2, r / 8, x, 0); canvas.drawPath(mPath, mPaint2); mPath.rewind(); /** * 画文字 */ NumberFormat numberFormat = NumberFormat.getPercentInstance(); numberFormat.setMinimumFractionDigits(1); textCenter(new String[] { numberFormat.format(percent) }, mPaint, canvas, mPointF, Paint.Align.CENTER); } /** * 多行文本居中、居右、居左 * * @param strings * 文本字符串列表 * @param paint * 画笔 * @param canvas * 画布 * @param point * 点的坐标 * @param align * 居中、居右、居左 */ protected void textCenter(String[] strings, Paint paint, Canvas canvas, PointF point, Paint.Align align) { paint.setTextAlign(align); Paint.FontMetrics fontMetrics = paint.getFontMetrics(); float top = fontMetrics.top; float bottom = fontMetrics.bottom; int length = strings.length; float total = (length - 1) * (-top + bottom) + (-fontMetrics.ascent + fontMetrics.descent); float offset = total / 2 - bottom; for (int i = 0; i < length; i++) { float yAxis = -(length - i - 1) * (-top + bottom) + offset; canvas.drawText(strings[i], point.x, point.y + yAxis, paint); } } public void setProgress(float percent) { if (percent != 0) { this.percent = percent; } else { this.percent = 0; } invalidate(); } }
布局里面的代码:
<com.example.view.weight.MyLineView android:id="@+id/view_line" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:gravity="center" />
完成了,在最后感谢
https://github.com/Idtk/Blog/blob/master/Blog/6%E3%80%81Bezier.md
相关文章推荐
- Android事件分发机制
- Android 开源框架Universal-ImageLoader的使用
- Android EditText 银行卡四位空一格
- Android桌面悬浮窗进阶,QQ手机管家小火箭效果实现
- Android Studio系列教程6 (Gradle多渠道打包)
- Android Studio 使用起来很卡,内存CPU爆了
- 如何设置android studio让程序运行在真机中
- Android ListView加载多种布局
- Android布局属性
- Activity横竖屏切换时的生命周期
- Android获取SHA值
- android-invalidate()源码
- AndroidSweetSheet:ViewPager的实现(2)
- AndroidSweetSheet:ViewPager的实现(2)
- android 百度地图系列之地图初始化及定位
- android屏幕适配
- 短信管理器(SmsManager)
- Android开发学习之路-带文字的图片分享
- android studio 的oncreate 的 PersistableBundle persistentState
- Activity的生命周期理解笔记