- 最近写一个天气的软件练手,需要用到折线图显示天气,效果图如下
自定义View步骤
现在我们来使用Canvas类自定义一个View控件。自定义控件步骤如下:
1. 自定义View,首先定义一个MyView类继承View类。
2.重写View的两个构造器。
View是包含四个构造器的,我们必须重写MyWidgetView(Context context, AttributeSet attrs)构造器,因为该构造器的第二个参数是与xml布局文件相联系的,如果没有重写该构造器,将不能在布局中使用该控件。这里我们重写他的两个构造器:
public MyView(Context context) {
super(context);
}
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
}
3. 重写onMeasure(int widthMeasureSpec, int heightMeasureSpec)方法,定义控件的尺寸:宽度和高度。在布局中使用该控件时会会传入控件的尺寸,只有当传入尺寸之后且调用onMesure之后,控件才会有宽度和高度。
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
width = getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec);
height = getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec);
setMeasuredDimension(width, height);//设置宽和高
}
4.重写onDraw(Canvas canvas)方法,我们在该方法中定义绘制View,当我们在Activity或其他地方使该控件时, UI主线程会调用onDraw方法绘制。
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
}
onDraw(Canvas canvas)方法中传入了一个Canvas对象,我们在定义控件时,使用Canvas绘制。
折线图
public class TempTrendView extends View {
//两个构造函数,没说的。
public TempTrendView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public TempTrendView(Context context) {
super(context);
init();
}
//定义一些必要的属性,两个ArrayLsit是存放传进来的温度值
Paint paint;
private int screenWidth;
private int viewHeight;
private int everyWidth;
private ArrayList<Integer> highTemp;
private ArrayList<Integer> lowTemp;
public void setHighTemp(ArrayList<Integer> highTemp) {
this.highTemp = highTemp;
//这个函数是用来清屏的,每次加载,都先把以前的内容清除掉
invalidate();
}
public void setLowTemp(ArrayList<Integer> lowTemp) {
this.lowTemp = lowTemp;
//这个函数是用来清屏的,每次加载,都先把以前的内容清除掉
invalidate();
}
//初始化各种实例及进行必要的设置
private void init() {
paint = new Paint();
paint.setColor(Color.WHITE);
paint.setStrokeWidth(4);
paint.setAntiAlias(true);
paint.setTextSize(40);
}
//获取屏幕宽度,折线图控件本身的高度用于计算
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
WindowManager wm = (WindowManager) getContext().getSystemService(
Context.WINDOW_SERVICE);
screenWidth = wm.getDefaultDisplay().getWidth();
viewHeight = getLayoutParams().height;
//一周分为7天,我们要把宽度均分
everyWidth = screenWidth / 7;
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (highTemp == null) {
//System.out.println("highTemp不能为空");
return;
}
//System.out.println(highTemp.size());
//设置开始绘制高温的x坐标和y坐标
float startX = everyWidth / 2;
float startY = viewHeight / 5*2;
float detay;
//这两个用来记录是否为当天的位置坐标,在最后划竖线的时候用到
float todaySY = 0;
float todayEY = 0;
//这个变量是每差1度纵向的偏移量
int fudu = -10;
//这个是文字的偏移量
int textPianyi = -40;
//用来记录第二天和第一天总的纵向偏移的距离的
int extra = -5;
canvas.drawText("一周温度走势图", screenWidth / 2 - 140, 50, paint);
paint.setColor(Color.rgb(241,162,89));
for (int i = 0; i < highTemp.size() - 1; i++) {
//设置画笔的颜色透明度
paint.setAlpha(0xff);
//就是等比例的改变不同点的高度显示
detay = (highTemp.get(i + 1) - highTemp.get(i)) * fudu;
//折线点的圆的绘制
canvas.drawCircle(startX, startY, 15, paint);
//折线的绘制
canvas.drawLine(startX, startY, startX + everyWidth,
startY + detay, paint);
canvas.drawText(highTemp.get(i) + "℃", startX - 15, startY+ textPianyi + extra,paint);
if (i == 0) {
todaySY = startY;
}
startX = startX + everyWidth;
startY = startY + detay;
}
canvas.drawCircle(startX, startY, 15, paint);
// paint.setColor(0xffFF3030);
canvas.drawText(highTemp.get(highTemp.size() - 1) + "℃", startX, startY +textPianyi+ extra, paint);
//这是绘制最顶温度的代码,最低温度和上面思路基本一致,就不再解释
if (lowTemp == null) {
//System.out.println("lowTemp不能为空");
return;
}
startX = everyWidth / 2;
//这里设置最低温度也和最高温度一样的起始位置,然后根据二者插值,同时计算上纵向二者的偏移量
startY = viewHeight / 5*2 +(lowTemp.get(0) - highTemp.get(0)) * fudu;
paint.setColor(Color.rgb(71,213,234));
for (int i = 0; i < lowTemp.size() - 1; i++) {
paint.setAlpha(0xff);
detay = (lowTemp.get(i + 1) - lowTemp.get(i)) * fudu;
canvas.drawLine(startX, startY, startX + everyWidth,startY + detay, paint);
canvas.drawCircle(startX, startY, 15, paint);
canvas.drawText(lowTemp.get(i) + "℃", startX + 5, startY- textPianyi-extra,paint);
if (i == 0) {
todayEY = startY;
}
startX = startX + everyWidth;
startY = startY + detay;
}
canvas.drawCircle(startX, startY, 15, paint);
canvas.drawText(lowTemp.get(lowTemp.size()-1) + "℃", startX + 5, startY -textPianyi-extra,paint);
drawDottedLine(everyWidth / 2, todaySY, todayEY+ (viewHeight - todayEY) - 30, canvas);
canvas.drawText("今天", everyWidth / 2 - 38, viewHeight - 5, paint);
}
/**
* 绘制竖直的虚线, endY必须大于startY
* 根据记录的当天的点位置,划线标注出当天对应的温度位置
*/
private void drawDottedLine(float sX, float startY, float endY,Canvas canvas) {
while (startY < endY) {
canvas.drawLine(sX, startY, sX, startY + 10, paint);
startY = startY + 20;
}
}
}
- 注释就在上面的代码中了,这里就不再对应贴出写解释了。