当前位置: 移动技术网 > 移动技术>移动开发>Android > Android自定义带增长动画和点击弹窗提示效果的柱状图DEMO

Android自定义带增长动画和点击弹窗提示效果的柱状图DEMO

2019年07月24日  | 移动技术网移动技术  | 我要评论

项目中最近用到各种图表,本来打算用第三方的,例如mpandroid,这是一个十分强大的图表库,应用起来十分方便,但是最终发现和设计不太一样,没办法,只能自己写了。今天将写好的柱状图的demo贴在这,该柱状图可根据数据的功能有一下几点:

     1. 根据数据的多少,动态的绘制柱状图柱子的条数;

     2. 柱状图每条柱子的绘制都有动态的动画效果;

     3. 每条柱子有点击事件,点击时弹出提示框,显示相关信息,规定时间后,弹窗自动消失。

     好了,先上演示图:

     下边贴出相关代码:

     自定义柱状图类:

package com.example.histogram; 
import android.content.context; 
import android.graphics.canvas; 
import android.graphics.color; 
import android.graphics.paint; 
import android.graphics.rectf; 
import android.os.handler; 
import android.text.textpaint; 
import android.util.attributeset; 
import android.util.log; 
import android.view.motionevent; 
import android.view.view; 
import com.example.histogram.ui.ui; 
import java.text.numberformat; 
/** 
 * created by zhangzdon 2016/6/16 0016. 
 * 柱状图 
 */ 
public class histogram extends view implements runnable { 
  private handler handler = new handler(); // 用于延时更新,实现动画 
  private float animheight; // 进度条动画高度 
  private paint axislinepaint; // 坐标轴画笔 
  private paint hlinepaint; // 内部水平虚线画笔 
  private paint textpaint; // 绘制文本的画笔 
  private paint recpaint; // 绘制柱状图阴影背景的画笔 
  private paint datapaint; // 绘制柱状图的画笔 
  private paint textpaint2; // 绘制白色文本的画笔 
  private paint textpaint3; // 绘制坐标的画笔 
  private paint textpaint4; // 绘制x轴上的白色竖线的画笔 
  private string[] xtitlestring; // x轴刻度 
  private string[] ytitlestring; // y轴刻度 
  private string[] data; // 接口返回的indicatordata,用于计算柱子高度 
  numberformat numberformat; //用于格式化数字 
  private float currentheight; // 当前柱状图应有的高度,应由计算得来 
  private int num = -1; // 画多少条柱子,因为存在刚开机数据不足24条的情况 
  private float mrelativepxinheight; 
  private float mrelativepxinwidth; 
  private onchartclicklistener listener; 
  private int mdist; 
  public void setnum(int num) { 
    this.num = num; 
    invalidate(); 
  } 
  public void setdata(string[] data) { 
    this.data = data; 
    invalidate(); 
  } 
  public void setxtitlestring(string[] title) { 
    this.xtitlestring = title; 
    invalidate(); 
  } 
  public histogram(context context) { 
    this(context, null); 
  } 
  public histogram(context context, attributeset attrs) { 
    this(context, attrs, 0); 
  } 
  public void settitle(string[] title) { 
    this.xtitlestring = title; 
  } 
  public histogram(context context, attributeset attrs, int defstyleattr) { 
    super(context, attrs, defstyleattr); 
    init(context, attrs); 
  } 
  /** 
   * 进行相关初始化操作 
   * @param context 
   * @param attrs 
   */ 
  private void init(context context, attributeset attrs) { 
    axislinepaint = new paint(); 
    hlinepaint = new paint(); 
    textpaint = new paint(); 
    recpaint = new paint(); 
    datapaint = new paint(); 
    textpaint2 = new paint(); 
    textpaint3 = new paint(); 
    textpaint4 = new paint(); 
    numberformat = numberformat.getnumberinstance(); 
    numberformat.setminimumfractiondigits(3); //设置打印时保留三位小数 
    axislinepaint.setcolor(color.parsecolor("#dbdde4")); //设置坐标轴的颜色为白色 
    hlinepaint.setargb(51, 255, 255, 255); 
    textpaint.setcolor(color.parsecolor("#8593a1")); 
//    textpaint.settextsize(29); 
    textpaint.settextsize(ui.dip2px(getcontext(), 12)); 
    recpaint.setcolor(color.parsecolor("#f2f5fc")); 
    datapaint.setcolor(color.cyan); 
    textpaint2.setcolor(color.white); 
    textpaint2.settextsize(ui.dip2px(getcontext(), 12)); 
    textpaint3.setcolor(color.parsecolor("#000000")); 
    textpaint3.settextsize(ui.dip2px(getcontext(), 9)); 
    textpaint4.setcolor(color.parsecolor("#8593a1")); 
    textpaint4.settextsize(ui.dip2px(getcontext(), 6)); 
    axislinepaint.setantialias(true); 
    hlinepaint.setantialias(true); 
    textpaint.setantialias(true); 
    recpaint.setantialias(true); 
    datapaint.setantialias(true); 
    textpaint2.setantialias(true); 
    textpaint3.setantialias(true); 
    textpaint4.setantialias(true); 
  } 
  @override 
  protected void ondraw(canvas canvas) { 
    super.ondraw(canvas); 
    if(data == null || xtitlestring == null || num < 0 ) { 
      return; 
    } 
    //绘制y轴刻度 
    paint.fontmetrics metrics = textpaint3.getfontmetrics(); 
    int decent = (int) metrics.descent; 
    float width = getwidth(); 
    float height = getheight(); 
    //根据原型图得出,图中每px高度在实际中的相对尺寸 
    mrelativepxinheight = height / 470; 
    //根据原型图得出,图中每px宽度在实际中的相对尺寸 
    mrelativepxinwidth = width / 690; 
    textpaint3.settextalign(paint.align.right); 
    //绘制纵坐标 
    ytitlestring = new string[6]; 
    ytitlestring[5] = "0"; 
    ytitlestring[4] = "20"; 
    ytitlestring[3] = "40"; 
    ytitlestring[2] = "60"; 
    ytitlestring[1] = "80"; 
    ytitlestring[0] = "100"; 
    for (int i = 0; i < ytitlestring.length; i++) { 
      canvas.drawtext(ytitlestring[i], 88 * mrelativepxinwidth, (72 + i * 56) * mrelativepxinheight + decent, textpaint3); 
    } 
    //绘制x轴刻度 
    textpaint3.settextalign(paint.align.center); 
    textpaint4.settextalign(paint.align.center); 
    textpaint textpaint = new textpaint(); 
    textpaint.setcolor(color.parsecolor("#000000")); 
    textpaint.settextsize(ui.dip2px(getcontext(), 9)); 
    //计算柱子之间的间隔 
    //最左侧位置100 * mrelativepxinwidth,最右侧位置630 epxinwidth, 
    float totalwidth = 630 - 100; 
    // 柱子与之子之间的间隔 
    mdist = (int) (totalwidth / (xtitlestring.length + 1)); 
    for (int i = 0; i < xtitlestring.length; i++) { 
      //绘制白色竖线 
      canvas.drawline((100 + (i+1) * mdist) * mrelativepxinwidth, 348 * mrelativepxinheight, (100 + (i+1) * mdist) * mrelativepxinwidth, 352 * mrelativepxinheight, axislinepaint); 
      //绘制x轴文字 
      canvas.drawtext(xtitlestring[i], (100 + (i+1) * mdist) * mrelativepxinwidth, 370 * mrelativepxinheight, textpaint3); 
    } 
//    绘制矩形阴影 
    for (int i = 0; i < num; i++) { 
      rectf rectf = new rectf(); 
//      rectf.left = 111 * relativepxinwidth + i * 22 * relativepxinwidth; 
//      rectf.right = 121 * relativepxinwidth + i * 22 * relativepxinwidth; 
      rectf.left = 95 * mrelativepxinwidth + (i+1) * mdist * mrelativepxinwidth; 
      rectf.right = 105 * mrelativepxinwidth +(i+1) * mdist * mrelativepxinwidth; 
      rectf.top = 70 * mrelativepxinheight; 
      rectf.bottom = 338 * mrelativepxinheight; 
      canvas.drawroundrect(rectf, 10, 10, recpaint); 
    } 
    //    绘制x轴坐标线 
    for (int i = 0; i < 6; i++) { 
      canvas.drawline(100 * mrelativepxinwidth, (66 + i * 56) * mrelativepxinheight + decent, 630 * mrelativepxinwidth, (66 + i * 56) * mrelativepxinheight + decent, axislinepaint); 
    } 
//    延时绘制,实现动画效果。数字越大,延时越久,动画效果就会越慢 
    handler.postdelayed(this, 1); 
    for (int i = 0; i < num; i++) { 
      rectf datarectf = new rectf(); 
      datarectf.left = 95 * mrelativepxinwidth + (i + 1) * mdist * mrelativepxinwidth; 
      datarectf.right = 105 * mrelativepxinwidth + (i + 1) * mdist * mrelativepxinwidth; 
      datapaint.setcolor(color.parsecolor("#3ac2d9")); 
      //获取柱子高度 
      currentheight = float.parsefloat(data[num - 1 - i]); 
      if (currentheight == 0) { 
        datarectf.top = 346 * mrelativepxinheight; 
      } else if (currentheight == 100) { 
        datarectf.top = 70 * mrelativepxinheight; 
      } else { 
        if (animheight >= currentheight) { 
          datarectf.top = 346 * mrelativepxinheight - currentheight / 100 * 276 * mrelativepxinheight; 
        } else { 
          datarectf.top = 346 * mrelativepxinheight - 276 * mrelativepxinheight * (animheight / 100); 
        } 
      } 
      datarectf.bottom = 346 * mrelativepxinheight; 
//        限制最高高度 
      if (datarectf.top < 70 * mrelativepxinheight) { 
        datarectf.top = 70 * mrelativepxinheight; 
      } 
      canvas.drawroundrect(datarectf, 10, 10, datapaint); 
    } 
  } 
  //实现柱子增长的动画效果 
  @override 
  public void run() { 
    animheight += 1; 
    if (animheight >= 276 * mrelativepxinheight) { 
      return; 
    } else { 
      invalidate(); 
    } 
  } 
  @override 
  public boolean ontouchevent(motionevent event) { 
    switch (event.getaction()) { 
      case motionevent.action_down: { 
        //获取点击坐标 
        float x = event.getx(); 
        float y = event.gety(); 
        //判断点击点的位置 
        float leftx = 0; 
        float rightx = 0; 
        for (int i = 0; i < num; i++) { 
          leftx = 95 * mrelativepxinwidth + (i+ 1) * mdist * mrelativepxinwidth - mdist/2 * mrelativepxinwidth; 
          rightx = 105 * mrelativepxinwidth + (i+ 1) * mdist * mrelativepxinwidth + mdist/2 * mrelativepxinwidth; 
          if (x < leftx) { 
            continue; 
          } 
          if (leftx <= x && x <= rightx) { 
            //获取点击的柱子区域的y值 
            float top = 346 * mrelativepxinheight - float.parsefloat(data[num - 1 - i])/ 100 * 276 * mrelativepxinheight; 
            float bottom = 346 * mrelativepxinheight; 
            if (y >= top && y <= bottom) { 
              //判断是否设置监听 
              //将点击的第几条柱子,点击柱子顶部的坐值,用于弹出dialog提示数据,还要返回百分比currentheidht = float.parsefloat(data[num - 1 - i]) 
              if(listener != null) { 
                log.e("ss","x" + x +";y:" + y); 
                listener.onclick(i + 1, leftx + mdist/2,top,float.parsefloat(data[num - 1 - i])); 
              } 
              break; 
            } 
          } 
        } 
        break; 
      } 
      case motionevent.action_move: 
        log.e("touch", "action_move"); 
        break; 
      case motionevent.action_up: 
        log.e("touch", "action_up"); 
        break; 
    } 
    return true; 
  } 
  /** 
   * 柱子点击时的监听接口 
   */ 
  public interface onchartclicklistener { 
    void onclick(int num, float x, float y, float value); 
  } 
  /** 
   * 设置柱子点击监听的方法 
   * @param listener 
   */ 
  public void setonchartclicklistener(onchartclicklistener listener) { 
    this.listener = listener; 
  } 
} 

  在xml文件中的应用:

<?xml version="1.0" encoding="utf-8"?> 
<linearlayout 
  xmlns:android="http://schemas.android.com/apk/res/android" 
  xmlns:tools="http://schemas.android.com/tools" 
  android:id="@+id/activity_main" 
  android:layout_width="match_parent" 
  android:layout_height="match_parent" 
  android:orientation="vertical" 
  tools:context="com.example.histogram.mainactivity"> 
  <textview 
    android:layout_width="match_parent" 
    android:layout_height="40dp" 
    android:gravity="center" 
    android:text="繁忙度指示图(%)" 
    android:textsize="15sp" 
    android:textcolor="#000000" 
    /> 
  <com.example.histogram.histogram 
    android:id="@+id/staticview" 
    android:layout_width="400dp" 
    android:layout_height="500dp" 
    android:layout_gravity="center_horizontal" 
    android:layout_marginbottom="14dp" 
    android:layout_margintop="5dp"/> 
</linearlayout> 

   在activity中的实现:

package com.example.histogram; 
import android.os.bundle; 
import android.os.handler; 
import android.support.v7.app.appcompatactivity; 
import android.util.log; 
import android.view.view; 
import android.widget.popupwindow; 
import android.widget.textview; 
public class mainactivity extends appcompatactivity { 
  private popupwindow mpopupwindow; 
  @override 
  protected void oncreate(bundle savedinstancestate) { 
    super.oncreate(savedinstancestate); 
    setcontentview(r.layout.activity_main); 
    final histogram histogram = (histogram) findviewbyid(r.id.staticview); 
    string[] data ={"100","20","40","20","80","20","60","30","5","20","60","30","5","5","20","60","30","5"}; 
    final string[] title = {"1","2","3","4","5","6","7","8","9","6","7","8","9","9","6","7","8","9"}; 
    histogram.setnum(title.length); 
    histogram.setdata(data); 
    histogram.setxtitlestring(title); 
    histogram.setonchartclicklistener(new histogram.onchartclicklistener() { 
      @override 
      public void onclick(int num, float x, float y, float value) { 
        //显示提示窗 
        view inflate = view.inflate(mainactivity.this, r.layout.popupwindow, null); 
        textview textview = (textview) inflate.findviewbyid(r.id.main_tv); 
        textview.settext(value + "%\n" + title[num - 1]); 
        if(mpopupwindow != null) { 
          mpopupwindow.dismiss(); 
        } 
        mpopupwindow = new popupwindow(inflate,140, 60, true); 
        mpopupwindow.settouchable(true); 
        log.e("ss","num" + num +";x" + x+";y"+ y + ";value" + value 
            +";(int)((- histogram.getheight()) + y - 65)" 
            +(int)((- histogram.getheight()) + y - 65) 
        + "histogram.getheight()" + histogram.getheight()); 
        // 设置好参数之后再show 
//        toast.maketext(mainactivity.this, "num" + num +";x" + x+";y"+ y + ";value" + value 
//            +";popupwindow.getwidth()"+ mpopupwindow.getwidth()+";"+ mpopupwindow.getheight(), toast.length_short).show(); 
        mpopupwindow.showasdropdown(histogram,(int)(x - 65),(int)((- histogram.getheight()) + y - 65) ); 
        mpopupwindow.setbackgrounddrawable(getresources().getdrawable(r.mipmap.databg_busyness)); 
        new handler().postdelayed(new runnable(){ 
          public void run() { 
            mpopupwindow.dismiss(); 
          } 
        }, 1000); 
      } 
    }); 
  } 
} 

以上所述是小编给大家介绍的android自定义带增长动画和点击弹窗提示效果的柱状图,实现一个模拟后台数据登入的效果,希望对大家有所帮助

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网