欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android自定义控件实现折线图

程序员文章站 2022-08-20 16:20:39
本文实例实现一个如下图所示的android折线图,供大家参考,具体内容如下 首先是控件绘图区域的划分,控件左边取一小部分(控件总宽度的八分之一)绘制表头,右边...

本文实例实现一个如下图所示的android折线图,供大家参考,具体内容如下

Android自定义控件实现折线图

Android自定义控件实现折线图

Android自定义控件实现折线图

首先是控件绘图区域的划分,控件左边取一小部分(控件总宽度的八分之一)绘制表头,右边剩余的部分绘制表格

确定表格的行列数,首先绘制一个三行八列的网格,设置好行列的坐标后开始绘制

/*绘制三条横线*/
for(int i=0;i<3;i++){
  canvas.drawline(textwide, mlineys[i], totalwidth, mlineys[i], mpaintline);
}
/*绘制八条竖线*/
for(int i=0;i<8;i++){
  canvas.drawline(mlinexs[i], 0, mlinexs[i], totalheight, mpaintline);
}

网格绘制完成后,开始绘制折线图

根据输入的节点数据,分别绘制两条折线

通过canvas的drawline方法依次连接两点即可

在每个数据节点处绘制一个小圆,突出显示

/*绘制第一条折线的路径*/
for (int i = 0; i < mperformance_1.length - 1; i++) {
  /*折线图的折线的画笔设置粗一点*/
  mpaintline.setstrokewidth(5);
  /*计算当前节点的坐标值*/
  float prepointx =mlinexs[i];
  float prepointy =mlineys[2] - (mlineys[2] - mlineys[mperformance_1[i].type]) * animcurrentvalue;
  /*计算下一个节点的坐标值*/
  float nextpointx=mlinexs[i + 1];
  float nextpointy=mlineys[2] - (mlineys[2] - mlineys[mperformance_1[i + 1].type]) * animcurrentvalue;
  /*连接当前坐标和下一个坐标,绘制线段*/
  canvas.drawline(prepointx, prepointy, nextpointx, nextpointy, mpaintline1);
  /*当前节点坐标处绘制小圆*/
  canvas.drawcircle(prepointx, prepointy, msmalldotradius, mpointpaint);
}

两条折线重合的地方,需要特殊考虑,比如希望两条折线重合的地方折线变为白色

设置下两条折线的画笔即可

mpaintline2.setxfermode(new porterduffxfermode(porterduff.mode.screen));
mpaintline1.setxfermode(new porterduffxfermode(porterduff.mode.screen));

Android自定义控件实现折线图

测试代码及效果;

final random random=new random();
final linechartview myview=(linechartview)findviewbyid(r.id.custom_view);
final linechartview.performance[] performances1=new linechartview.performance[8];
final linechartview.performance[] performances2=new linechartview.performance[8];
myview.setonclicklistener(new view.onclicklistener(){
  @override
  public void onclick(view v){
    for(int i=0;i<performances1.length;i++){
      switch (random.nextint(2016)%3){
        case 0:
          performances1[i]= linechartview.performance.win;
          break;
        case 1:
          performances1[i]= linechartview.performance.draw;
          break;
        case 2:
          performances1[i]= linechartview.performance.lose;
          break;
        default:
          performances1[i]= linechartview.performance.lose;
          break;
      }
      switch (random.nextint(2016)%3){
        case 0:
          performances2[i]= linechartview.performance.win;
          break;
        case 1:
          performances2[i]= linechartview.performance.draw;
          break;
        case 2:
          performances2[i]= linechartview.performance.lose;
          break;
        default:
          performances1[i]= linechartview.performance.lose;
          break;
      }
    }
    myview.setperformances(performances1,performances2);
  }
});

Android自定义控件实现折线图

完整代码如下:

public class linechartview extends view {
  private context context;
  /*动画插值器*/
  decelerateinterpolator mdecelerateinterpolator = new decelerateinterpolator();
  /*动画刷新的次数*/
  private int mduration = 10;
  /*当前动画进度值*/
  private int mcurrenttime = 0;
  private performance[] mperformance_1, mperformance_2;
  /*两条折线的颜色*/
  private int mlinecolor1, mlinecolor2;
  /*绘制表头文字画笔*/
  private paint mpainttext = new paint();
  /*绘制表格的画笔*/
  private paint mpaintline = new paint();
  /*第一条折线的画笔*/
  private paint mpaintline1 =new paint();
  /*第二条折线的画笔*/
  private paint mpaintline2 =new paint();
  /*坐标点的小圆点画笔*/
  private paint mpointpaint = new paint();
  private float msmalldotradius = 4;
  private typedvalue typedvalue;
  private int mpaintclolor;
  /*handler刷新界面产生动画效果*/
  private handler mhandler = new handler();
  private runnable manimation = new runnable() {
    @override
    public void run() {
      if (mcurrenttime < mduration) {
        mcurrenttime++;
        linechartview.this.invalidate();
      }
    }
  };
 
  public linechartview(context context) {
    super(context);
    this.context=context;
    init();
  }
 
  public linechartview(context context, attributeset attrs) {
    super(context, attrs);
    this.context=context;
    init();
  }
 
  public linechartview(context context, attributeset attrs, int defstyleattr) {
    super(context, attrs, defstyleattr);
    this.context=context;
    init();
  }
 
  public enum performance {
    win(0),
    draw(1),
    lose(2);
    public int type;
    performance(int type) {
      this.type = type;
    }
  }
 
  public void setperformances(performance[] performance1, performance[] performance2) {
    if (performance1 == null) {
      performance1 = new performance[0];
    }
    if (performance2 == null) {
      performance2 = new performance[0];
    }
    mperformance_1 = arrays.copyof(performance1, performance1.length > 8 ? 8 : performance1.length);
    mperformance_2 = arrays.copyof(performance2, performance2.length > 8 ? 8 : performance2.length);
    if (isshown()) {
      mcurrenttime = 0;
      this.invalidate();
    }
  }
 
  /**
   * 设置折线1的颜色
   *
   * @param mlinecolor1
   */
  public void setlinecolor1(int mlinecolor1) {
    this.mlinecolor1 = mlinecolor1;
  }
 
  /**
   * 设置折线2的颜色
   *
   * @param mlinecolor2
   */
  public void setlinecolor2(int mlinecolor2) {
    this.mlinecolor2 = mlinecolor2;
  }
 
  private void init() {
    mlinecolor1=color.blue;
    mlinecolor2 = color.green;
    typedvalue=new typedvalue();
    context.gettheme().resolveattribute(r.attr.title_bar,typedvalue,true);
    mpaintclolor =getresources().getcolor(typedvalue.resourceid);
 
    final linechartview.performance[] performances1=new linechartview.performance[8];
    final linechartview.performance[] performances2=new linechartview.performance[8];
    final random random=new random();
    for(int i=0;i<performances1.length;i++){
      switch (random.nextint(2016)%3){
        case 0:
          performances1[i]= linechartview.performance.win;
          break;
        case 1:
          performances1[i]= linechartview.performance.draw;
          break;
        case 2:
          performances1[i]= linechartview.performance.lose;
          break;
        default:
          performances1[i]= linechartview.performance.lose;
          break;
      }
      switch (random.nextint(2016)%3){
        case 0:
          performances2[i]= linechartview.performance.win;
          break;
        case 1:
          performances2[i]= linechartview.performance.draw;
          break;
        case 2:
          performances2[i]= linechartview.performance.lose;
          break;
        default:
          performances1[i]= linechartview.performance.lose;
          break;
      }
    }
    setperformances(performances1,performances2);
  }
 
 
  /**
   * @param canvas
   */
  @override
  protected void ondraw(canvas canvas) {
    super.ondraw(canvas);
    /*获取控件总宽高*/
    float totalwidth = getwidth();
    float totalheight = getheight();
    /*左边取总宽度的八分之一绘制表格头部*/
    float textwide = totalwidth / 8;
    /*左边留一点空白*/
    float left_offset = 10;
    /*折线图的总宽度等于控件的总宽度减去表头和留白*/
    float chartwide = totalwidth - textwide - left_offset;
    /*一共三行,设置每一行的垂直坐标*/
    float[] mlineys = new float[]{totalheight / 8, totalheight / 2, totalheight * 7 / 8};
    /*一共八列,设置每一列的水平坐标*/
    float[] mlinexs = new float[]{
        textwide + left_offset + chartwide * 0 / 8,
        textwide + left_offset + chartwide * 1 / 8,
        textwide + left_offset + chartwide * 2 / 8,
        textwide + left_offset + chartwide * 3 / 8,
        textwide + left_offset + chartwide * 4 / 8,
        textwide + left_offset + chartwide * 5 / 8,
        textwide + left_offset + chartwide * 6 / 8,
        textwide + left_offset + chartwide * 7 / 8,
    };
 
    /*绘制表头文字*/
    mpainttext.setstyle(paint.style.fill);
    mpainttext.setcolor(mpaintclolor);
    mpainttext.setalpha(226);
    mpainttext.settextsize(28);
    /*从中间开始绘制*/
    mpainttext.settextalign(paint.align.center);
    /*测量文字大小,并计算偏移量*/
    paint.fontmetrics fontmetrics = mpainttext.getfontmetrics();
    float textbaselineoffset = (fontmetrics.bottom - fontmetrics.top) / 2 - fontmetrics.bottom;
    canvas.drawtext("胜场", textwide / 2, mlineys[0] + textbaselineoffset, mpainttext);
    canvas.drawtext("平局", textwide / 2, mlineys[1] + textbaselineoffset, mpainttext);
    canvas.drawtext("负场", textwide / 2, mlineys[2] + textbaselineoffset, mpainttext);
 
    /*绘制表格画笔设置*/
    mpaintline.setstyle(paint.style.stroke);
    mpaintline.setantialias(true);
    mpaintline.setcolor(mpaintclolor);
    mpaintline.setalpha(80);
    mpaintline.setstrokewidth(1);
    /*开始绘制表格*/
    /*绘制三条横线*/
    for(int i=0;i<3;i++){
      canvas.drawline(textwide, mlineys[i], totalwidth, mlineys[i], mpaintline);
    }
    /*绘制八条竖线*/
    for(int i=0;i<8;i++){
      canvas.drawline(mlinexs[i], 0, mlinexs[i], totalheight, mpaintline);
    }
    /*折线图画笔设置*/
    mpaintline1.setstyle(paint.style.stroke);
    /*设置透明度,取值范围为0~255,数值越小越透明,0表示完全透明*/
    mpaintline1.setalpha(0);
    mpaintline1.setantialias(true);
    mpaintline1.setcolor(mlinecolor1);
    mpaintline1.setstrokewidth(5);
    mpaintline2.setstyle(paint.style.stroke);
    /*设置透明度,取值范围为0~255,数值越小越透明,0表示完全透明*/
    mpaintline2.setalpha(0);
    mpaintline2.setantialias(true);
    mpaintline2.setcolor(mlinecolor2);
    mpaintline2.setstrokewidth(5);
    if (typedvalue.resourceid==r.color.white){
      /*porterduff.mode.screen 上下层都显示。*/
      mpaintline2.setxfermode(new porterduffxfermode(porterduff.mode.screen));
      mpaintline1.setxfermode(new porterduffxfermode(porterduff.mode.screen));
    }else {
      /*porterduff.mode.darken 上下层都显示。变暗*/
      mpaintline2.setxfermode(new porterduffxfermode(porterduff.mode.darken));
      mpaintline1.setxfermode(new porterduffxfermode(porterduff.mode.darken));
    }
    /*画节点处的小圆点的画笔设置*/
    mpointpaint.setstyle(paint.style.stroke);
    mpointpaint.setantialias(true);
    mpointpaint.setcolor(mpaintclolor);
 
 
    /*计算当前动画进度对应的数值*/
    float animcurrentvalue = mdecelerateinterpolator.getinterpolation(1.0f * mcurrenttime / mduration);
    mpaintline.setcolor(mlinecolor1);
    /*绘制第一条折线的路径*/
    for (int i = 0; i < mperformance_1.length - 1; i++) {
      /*折线图的折线的画笔设置粗一点*/
      mpaintline.setstrokewidth(5);
      /*计算当前节点的坐标值*/
      float prepointx =mlinexs[i];
      float prepointy =mlineys[2] - (mlineys[2] - mlineys[mperformance_1[i].type]) * animcurrentvalue;
      /*计算下一个节点的坐标值*/
      float nextpointx=mlinexs[i + 1];
      float nextpointy=mlineys[2] - (mlineys[2] - mlineys[mperformance_1[i + 1].type]) * animcurrentvalue;
      /*连接当前坐标和下一个坐标,绘制线段*/
      canvas.drawline(prepointx, prepointy, nextpointx, nextpointy, mpaintline1);
      /*当前节点坐标处绘制小圆*/
      canvas.drawcircle(prepointx, prepointy, msmalldotradius, mpointpaint);
    }
    /*第一个折线图的最后一个节点的坐标*/
    float lastpointx=mlinexs[mperformance_1.length - 1];
    float lastpointy= mlineys[2] - (mlineys[2] - mlineys[mperformance_1[mperformance_1.length - 1].type]) * animcurrentvalue;
    /*绘制最后一个节点的外围小圆*/
    canvas.drawcircle(lastpointx,lastpointy ,msmalldotradius, mpointpaint);
 
    /*绘制第二条折线*/
    mpaintline.setcolor(mlinecolor2);
    for (int i = 0; i < mperformance_2.length - 1; i++) {
      /*折线图的折线的画笔设置粗一点*/
      mpaintline.setstrokewidth(5);
      /*计算当前节点的坐标值*/
      float prepointx =mlinexs[i];
      float prepointy =mlineys[2] - (mlineys[2] - mlineys[mperformance_2[i].type]) * animcurrentvalue;
      /*计算下一个节点的坐标值*/
      float nextpointx=mlinexs[i + 1];
      float nextpointy=mlineys[2] - (mlineys[2] - mlineys[mperformance_2[i + 1].type]) * animcurrentvalue;
      /*连接当前坐标和下一个坐标,绘制线段*/
      canvas.drawline(prepointx, prepointy, nextpointx, nextpointy, mpaintline2);
      /*当前节点坐标处绘制小圆*/
      canvas.drawcircle(prepointx, prepointy, msmalldotradius, mpointpaint);
    }
     /*第一个折线图的最后一个节点的坐标*/
    lastpointx=mlinexs[mperformance_2.length - 1];
    lastpointy= mlineys[2] - (mlineys[2] - mlineys[mperformance_2[mperformance_2.length - 1].type]) * animcurrentvalue;
    /*绘制最后一个节点的外围小圆*/
    canvas.drawcircle(lastpointx,lastpointy ,msmalldotradius, mpointpaint);
    mhandler.postdelayed(manimation, 20);
  }
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。