最近在项目中遇到一个显示折线图的效果,但此效果与一般的折线图显示有几点特殊的要求:1、折线图里面,需要用特定的颜色填充,并且此颜色右上而下是渐变的;2、此折线图只需要均匀显示10个数据;3、在0,3,6,9这几个点上面,需要把改点代表的数字显示出来。效果图如下:
此效果的时候不多解释,肯定需要用到我们的自定义View,通过canvas来绘制曲线,绘制文字,绘制多边形并且用线性变化的颜色填充它,在底部还绘制了一个黑色填充的矩形。
这里先普及一下API:
canvas.drawText(string, x,y,paint); //绘制string文字,x代表的是字符开始的x坐标,y代表的是字符底部的坐标。
canvas.drawLine(startx, starty, endx, endy, paint); //绘制线段,开始的坐标和结束的坐标连接起来就成了一个线段,每根线段连接起来就是折线了。
Path.moveTo(float x,float y);//把绘制点移动到某个地方。
Path.lineTo(float x,float y);//添加多边形的点,绘制多边形的时候就会根据这些点依次绘制,形成我们需要的多边形。
LinearGradient(float x0, float y0, float x1, float y1, int[] colors, float[] positions, TileMode tile);//线性渐变类的构造参数,x0,y0表示线性变化开始的点;x1,y1表示线性变化结束的点;colors数组表示颜色的变化分布;positions表示颜色的分配比例,它的个数需要和颜色值一一对应,如果是null则直接均匀线性变化;TileMode 表示颜色铺垫的模式,我们一般写的是TileMode.REPEAT。
下面贴出代码:
public class Custom_line extends View{
Point[] points;
ArrayList<Integer> data;
int maxdata;
Paint paint;
private static final int BOTTOMY = 100; //底部黑色矩形的高度
private static final String TAG = "Custom_line";
public Custom_line(Context context) {
this(context, null);
}
public Custom_line(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
public Custom_line(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
paint = new Paint();
paint.setColor(Color.rgb(00, 0x55, 0xff));
paint.setStyle(Style.FILL);
}
public void setDatas(ArrayList<Integer> data,int linemaxdata){
this.data = data;
this.maxdata = linemaxdata;
invalidate();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@SuppressLint("DrawAllocation")
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
points = getPoints();
if(points==null){
return;
}
drawPolygon(canvas);
drawLine(canvas);
drawRect(canvas);
drawText(canvas);
}
/**
* 绘制多边形
*/
private void drawPolygon(Canvas canvas){
Path jianbianpath = new Path();
jianbianpath.moveTo(0, getHeight() - BOTTOMY);
for(int i=0; i<points.length; i++){
jianbianpath.lineTo(points[i].x, points[i].y);
}
int minY ;
minY = points[0].y;
for(int i=1;i<points.length;i++){
if(points[i].y<minY){
minY = points[i].y;
}
}
Shader jianbianshader = new LinearGradient(getWidth()/2, minY,getWidth()/2, getHeight()-BOTTOMY,
new int [] {Color.rgb(34, 67, 172),Color.rgb(18, 19, 20)}, null, TileMode.REPEAT);
paint.setShader(jianbianshader);
jianbianpath.lineTo(points[points.length-1].x, getHeight()-BOTTOMY);
jianbianpath.close();
canvas.drawPath(jianbianpath, paint);
}
/**
* 绘制文字
* @param canvas
*/
private void drawText(Canvas canvas){
Paint textpaint = new Paint();
textpaint.setColor(Color.WHITE);
textpaint.setTextSize(40);
//计算最后一个文字的宽度
Rect bounds = new Rect();
String dataend = String.valueOf(data.get(9));
textpaint.getTextBounds(dataend, 0, dataend.length(), bounds);
canvas.drawText(data.get(0)+"", points[0].x, points[0].y -16, textpaint);
canvas.drawText(data.get(3)+"", points[3].x -20, points[3].y -16, textpaint);
canvas.drawText(data.get(6)+"", points[6].x-20, points[6].y -16, textpaint);
canvas.drawText(data.get(9)+"", points[9].x - bounds.width()-1, points[9].y -16, textpaint);
}
/**
* 绘制底部的黑色矩形
* @param canvas
*/
private void drawRect(Canvas canvas){
Rect rect = new Rect(0, getHeight() - BOTTOMY-1, getWidth(),getHeight());
Paint rectpaint = new Paint();
rectpaint.setColor(Color.rgb(18, 19, 20));
canvas.drawRect(rect, rectpaint);
}
/**
*
* @param canvas
*/
private void drawLine(Canvas canvas)
{
Point startp = new Point();
Point endp = new Point();
Paint linepaint = new Paint();
linepaint.setColor(Color.rgb(41, 88, 172));
for (int i = 0; i < points.length - 1; i++)
{
startp = points[i];
endp = points[i + 1];
canvas.drawLine(startp.x, startp.y, endp.x, endp.y, linepaint);
}
}
/**
* 根据传进来的数据获得应该显示的数据点
* @return
*/
private Point[] getPoints(){
if(data==null){
return null;
}
int count = data.size();
Point[] temppoints = new Point[count];
int viewHeight = getHeight()- BOTTOMY; //因为底部要画一个黑色的矩形,所以需要把这个高度减掉
int viewWidth = getWidth();
for(int i=0 ; i<count ; i++){
int y = (int) (viewHeight*(1- ((data.get(i)*1.0f)/maxdata)));
int x = (int) (((viewWidth*1.0f)/(count-1))*i);
Log.i(TAG, "x:"+x);
Point mpoint = new Point(x, y);
temppoints[i] = mpoint;
}
return temppoints;
}
}