记录一个温度曲线的View
最近做项目需求的看到需求自定义一个温度曲线的图。因为之前的搭档了解需求的时候没有很好的了解产品的需求,将温度的折线图分成了两个View,温度高的在一个View,温度低的在一个View。这样的做法其实是没有很好的了解产品的需求的。为什么这么说,因为一旦拆成两个View,那么哪些相交的点绘制就会有缺陷了。什么意思,看图。

记录一个温度曲线的View

如果按照两个View去做,就会有这种局限性。相交的点就会被切。所以这儿就从头修改了这个自定义View。

有了上面的需求,那么就开端咱们的设计了。首要为了咱们自定义View的能比较好的通用性,咱们需求把一些可能会变的东西提取出来。这儿仅仅提取一些很常用的特点,其他需求自定义的,可自己加上。直接看代码

<declare-styleable name="NewWeatherChartView">
  <!--开端的x坐标-->
  <attr name="new_start_point_x" format="dimension"/>
  <!--两点之间x坐标的间隔-->
  <attr name="new_point_x_margin" format="dimension"/>
  <!--显现温度的字体大小-->
  <attr name="temperature_text_size" format="dimension"/>
  <!--圆点的半径-->
  <attr name="point_radius" format="dimension"/><!--选中气候项,温度字体的色彩-->
  <attr name="select_temperature_text_color" format="reference|color"/>
  <!--未选中气候项,温度字体的色彩-->
  <attr name="unselect_temperature_text_color" format="reference|color"/>
  <!--选中气候项,圆点的色彩-->
  <attr name="select_point_color" format="reference|color"/>
  <!--未选中气候项,圆点的色彩-->
  <attr name="unselect_point_color" format="reference|color"/>
    <!--连接线的色彩-->
  <attr name="line_color" format="reference|color"/>
  <!--连接线的类型,能够是实线,也能够是虚线,默许是虚线。0虚线,1实线-->
  <attr name="line_type" format="integer"/></declare-styleable>
public class NewWeatherChartView extends View {
  private final static String TAG = "NewWeatherChartView";
  private List<WeatherInfo> items;//温度的数据源//都是能够在XML里边装备的特点,现在项目里边都是用的默许装备。
  private int mLineColor;
  private int mSelectTemperatureColor;
  private int mUnSelectTemperatureColor;
  private int mSelectPointColor;
  private int mUnselectPointColor;
  private int mLineType;
  private int mTemperatureTextSize;
  private int mPointStartX = 0;
  private int mPointXMargin = 0;
  private int mPointRadius;
​
​
  
  private Point[] mHighPoints; //高温的点的坐标
  private Point[] mLowPoints; //低温的点的坐标//这儿是为了便利写代码,多创建了几个画笔,也能够用一个画笔,然后装备不同的特点
  private Paint mLinePaint;   //用于画线画笔
  private Paint mTextPaint; // 用于画小圆点周围的温度文字的画笔
  private Paint mCirclePaint;//用来画小圆点的画笔private Float mMaxTemperature = Float.MIN_VALUE;//最高温度
  private Float mMinTemperature = Float.MAX_VALUE;//最低温度
  private Path mPath;//连接线的途径
  
  private DecimalFormat mDecimalFormat;
​
​
  private int mTodayIndex = -1;//用于判断哪一个被选中private Context mContext;
    ...
}

以上便是一些初始化的东西了,那么现在就来思考一下,怎样去画这些东西,上面的初始化也说明了,咱们主要是画线,画文字,然后画圆点。那么应该从哪开端呢?首要是从点坐标开端,因为无论是线,还是文字,他们的位置和点都有联系。那么找到点的坐标便是首要的作业。怎样找点的坐标,以及最开端的X坐标是多少。第一个点的X坐标是根据咱们的装备来的,那么第二个点的x坐标呢?,第二个点的x坐标便是第一个点的x坐标加上他们之间的在X方向上间隔,而在x方向上的间隔也是根据特点装备的。所以咱们能够很简单得到一切点的x坐标。那么圆点的y坐标呢?首要咱们看一张图。

记录一个温度曲线的View

咱们的点,应该是均匀分布在剩下高度里边的。

剩下高度 = 控件高度-2*文字的高度。

点的y坐标为

*剩下高度-((当时温度-最低温度)/(最高温度-最低温度)剩下高度)+文字高度

看起来有点杂乱,可是有公式的话,代码会比较简单。接下来就需求看初始化的代码了和计算点坐标的代码了

代码如下:

//首要从两个参数的构造函数里边获取各种装备的值
public NewWeatherChartView(Context context, AttributeSet attrs) {
  super(context, attrs);
  TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.NewWeatherChartView);
  mPointStartX = (int) typedArray.getDimension(R.styleable.NewWeatherChartView_new_start_point_x, 0);
  mPointXMargin = (int) typedArray.getDimension(R.styleable.NewWeatherChartView_new_point_x_margin, 0);
  mTemperatureTextSize = (int) typedArray.getDimension(R.styleable.NewWeatherChartView_temperature_text_size, 20);
  mPointRadius = (int) typedArray.getDimension(R.styleable.NewWeatherChartView_point_radius, 8);
​
  mSelectPointColor = typedArray.getColor(R.styleable.NewWeatherChartView_select_point_color, context.getResources().getColor(R.color.weather_select_point_color));
  mUnselectPointColor = typedArray.getColor(R.styleable.NewWeatherChartView_unselect_point_color, context.getResources().getColor(R.color.weather_unselect_point_color));
  mLineColor = typedArray.getColor(R.styleable.NewWeatherChartView_line_color, context.getResources().getColor(R.color.weather_line_color));
  mSelectTemperatureColor = typedArray.getColor(R.styleable.NewWeatherChartView_select_temperature_text_color, context.getResources().getColor(R.color.weather_select_temperature_color));
  mUnSelectTemperatureColor = typedArray.getColor(R.styleable.NewWeatherChartView_unselect_temperature_text_color, context.getResources().getColor(R.color.weather_unselect_temperature_color));
​
  mLineType = typedArray.getInt(R.styleable.NewWeatherChartView_line_type, 0);
​
  this.mContext = context;
  typedArray.recycle();
}
​
private void initData() {
  //初始化线的画笔
  mLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
  mLinePaint.setStyle(Paint.Style.STROKE);
  mLinePaint.setStrokeWidth(2);
  mLinePaint.setDither(true);
  //装备虚线
  if (mLineType == 0) {
    DashPathEffect pathEffect = new DashPathEffect(new float[]{10, 5}, 1);
    mLinePaint.setPathEffect(pathEffect);
   }
  mPath = new Path();
​
  //初始化文字的画笔
  mTextPaint = new Paint();
  mTextPaint.setAntiAlias(true);
  mTextPaint.setTextSize(sp2px(mTemperatureTextSize));
  mTextPaint.setTextAlign(Paint.Align.CENTER);
​
  // 初始化圆点的画笔
  mCirclePaint = new Paint();
  mCirclePaint.setStyle(Paint.Style.FILL);
​
  mDecimalFormat = new DecimalFormat("0");
​
  for (int i = 0; i < items.size(); i++) {
    float highY = items.get(i).getHigh();
    float lowY = items.get(i).getLow();
    if (highY > mMaxTemperature) {
      mMaxTemperature = highY;
     }
    if (lowY < mMinTemperature) {
      mMinTemperature = lowY;
     }
    if (DateUtil.fromTodayDate(items.get(i).getDate()) == 0) {
      mTodayIndex = i;
     }
   }
  float span = mMaxTemperature - mMinTemperature;
  //这种状况是为了避免一切温度都一样的状况
  if (span == 0) {
    span = 6.0f;
   }
  mMaxTemperature = mMaxTemperature + span / 6.0f;
  mMinTemperature = mMinTemperature - span / 6.0f;
​
  mHighPoints = new Point[items.size()];
  mLowPoints = new Point[items.size()];
}
​
public int sp2px(float spValue) {
  return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, spValue, Resources.getSystem().getDisplayMetrics());
}
​
public int dip2px(float dpValue) {
  return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpValue, Resources.getSystem().getDisplayMetrics());
​
}

这些准备作业昨晚之后,咱们就能够去onDraw里边画图了。

protected void onDraw(Canvas canvas) {
  Logging.d(TAG, "onDraw: ");
  if (items == null) {
    return;
   }
  int pointX = mPointStartX; // 开端的X坐标
  int textHeight = sp2px(mTemperatureTextSize);//文字的高度
  int remainingHeight = getHeight() - textHeight * 2;//除掉文字后,剩下的高度// 计算每一个点的X和Y坐标
  for (int i = 0; i < items.size(); i++) {
    int x = pointX + mPointXMargin * i;
    float highTemp = items.get(i).getHigh();
    float lowTemp = items.get(i).getLow();
    int highY = remainingHeight - (int) (remainingHeight * ((highTemp - mMinTemperature) / (mMaxTemperature - mMinTemperature))) + textHeight;
    int lowY = remainingHeight - (int) (remainingHeight * ((lowTemp - mMinTemperature) / (mMaxTemperature - mMinTemperature))) + textHeight;
    mHighPoints[i] = new Point(x, highY);
    mLowPoints[i] = new Point(x, lowY);
   }
​
  // 画线
  drawLine(mHighPoints, canvas);
  drawLine(mLowPoints, canvas);
  for (int i = 0; i < mHighPoints.length; i++) {
    // 画文本度数 例如:3
    String yHighText = mDecimalFormat.format(items.get(i).getHigh());
    String yLowText = mDecimalFormat.format(items.get(i).getLow());
    int highDrawY = mHighPoints[i].y - dip2px(mPointRadius + 8);
    int lowDrawY = mLowPoints[i].y + dip2px(mPointRadius + 8 + sp2px(mTemperatureTextSize));
​
    if (i == mTodayIndex) {
      mTextPaint.setColor(mSelectTemperatureColor);
      mCirclePaint.setColor(mSelectPointColor);
     } else {
      mTextPaint.setColor(mUnSelectTemperatureColor);
      mCirclePaint.setColor(mUnselectPointColor);
     }
    canvas.drawText(yHighText + "", mHighPoints[i].x, highDrawY, mTextPaint);
    canvas.drawText(yLowText + "", mLowPoints[i].x, lowDrawY, mTextPaint);
    canvas.drawCircle(mHighPoints[i].x, mHighPoints[i].y, mPointRadius, mCirclePaint);
    canvas.drawCircle(mLowPoints[i].x, mLowPoints[i].y, mPointRadius, mCirclePaint);
​
   }
}
​
​
private void drawLine(Point[] ps, Canvas canvas) {
  Point startp;
  Point endp;
  mPath.reset();
  mLinePaint.setAntiAlias(true);
  for (int i = 0; i < ps.length - 1; i++) {
    startp = ps[i];
    endp = ps[i + 1];
    mLinePaint.setColor(mLineColor);
    canvas.drawLine(startp.x, startp.y, endp.x, endp.y, mLinePaint);
   }
}

以上便是一切要害代码了,当然,还有一个赋值的代码

public void setData(List<WeatherInfo> list) {
  this.items = list;
  initData();
}

来看一下最终的效果图吧。

记录一个温度曲线的View
以上便是一个简单的温度图了,可是这个图有许多地方能够优化,也有许多地方能够提取出来当作特点。比方我举一个优化的点,文字的丈量,上面的代码对文字的丈量其实是十分粗糙的。仔细观察会发现上面一条线,文字间隔点的间隔和下面一条线文字间隔点的间隔是不一样的。这便是上面没有进行文字丈量的结果,我这儿进行了一轮文字丈量的优化,如下图:
记录一个温度曲线的View
这儿是不是好许多了呢?我们还能够进行许多地方的优化。以上便是这篇文章的全部内容了。

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。