您的位置:首页 > 移动开发 > Android开发

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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: