Android上实现柱状图表

简介: Android上实现柱状图表

Android上实现柱状图算法实现

第一步:

获取Android设备的屏幕大小

第二步:

在View对象中使用Canvas绘制蓝色边框与白色背景XY轴两条线,代码如下

第三步:

绘制柱状图标题

第四步:

根据数据集计算出每个系列数据所占X轴的大小,来绘制X 数据名称

第五步:

根据数据集计算出数据单元大小,并将数据单元映射为像素单元,绘制出标尺单位与

背景虚线

第六步:

根据数据集的值来计算出柱状图的高度,以及柱状图的宽度大小,映射为像素值以后

完成绘制。

程序效果图:

1335700297_2087.png

技术点详解:

在View中获取Android设备屏幕大小的方法为:

// get default screen size from system service
WindowManager wm = (WindowManager) this.getContext().getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
int width = display.getWidth();

在Activity中获取Android设备屏幕大小的方法为:

 DisplayMetrics displaymetrics = new DisplayMetrics();
 getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
 int height = displaymetrics.heightPixels;
 int wwidth = displaymetrics.widthPixels;

计算X轴中每个系列所占大小的代码为:

int count = series.getSeriesCount();
int xUnit = (width - 2 - xOffset)/count;

其中xOffset, yOffset值计算公式如下:

int xOffset = (int)(width * 0.1);
int yOffset = (int)(height * 0.1);


计算每个系类中,每个柱状图之间缝隙大小的为:

int barWidth = (int)(xUnit/Math.pow(itemList.size(),2));
int startPos = xOffset + 2 + xPadding + xUnit*i;
int interval = barWidth/2;

其中barWidth表示每个柱状矩形的宽度,interval表示同一数据系列中表示

每个矩形之间的间隔。


另外一些技巧:

1.在起始位置填充额外的长度大小,让柱状图不会紧贴Y轴,看上去更美观

默认的xPadding值等于10, int xPadding = 10;

2.使用反锯齿功能,让图形看上去更柔和,启用反锯齿功能的代码为:

myPaint.setAntiAlias(true);

3.当要填充一个矩形时候设置Paint的Style

myPaint.setStyle(Style.FILL);

当要绘制一个边框矩形时候设置Paint的Style

myPaint.setStyle(Style.STROKE);

4.如何绘制虚线(dotted line, dash line),使用DashPathEffect对象

本文中的实现代码如下:

myPaint.setStyle(Style.STROKE);
myPaint.setStrokeWidth(1);
myPaint.setColor(Color.LTGRAY);
myPaint.setPathEffect(new DashPathEffect(new float[] {1,3}, 0));


相关的Class说明:

DataSeries对象用来构造数据集,根据key来得到对应的数据系列。源代码如下:

package com.gloomyfish;
 
import java.util.HashMap;
import java.util.List;
 
public class DataSeries {
  private HashMap<String, List<DataElement>> map;
  
  public DataSeries() {
    map = new HashMap<String, List<DataElement>>();
  }
  
  public void addSeries(String key, List<DataElement> itemList) {
    map.put(key, itemList);
  }
  
  public List<DataElement> getItems(String key) {
    return map.get(key);
  }
  
  public int getSeriesCount() {
    return map.size();
  }
  
  public String[] getSeriesKeys() {
    return map.keySet().toArray(new String[0]);
  }
 
}

DataElement数据元素,属性有数据名称,值大小,显示颜色等,源代码如下:

package com.gloomyfish;
 
public class DataElement {
  
  public DataElement(String name, float value, int color) {
    this.itemName = name;
    this.value = value;
    this.color = color;
  }
  public String getItemName() {
    return itemName;
  }
  public void setItemName(String itemName) {
    this.itemName = itemName;
  }
  public float getValue() {
    return value;
  }
  public void setValue(float value) {
    this.value = value;
  }
  
  public void setColor(int color) {
    this.color = color;
  }
  
  public int getColor() {
    return this.color;
  }
  
  private String itemName;
  private int color;
  private float value;
}

BarChartPanel独立的组件,继承自Android View对象,是柱状图的图形组件,可以为

任何版本的Android使用。源代码如下:

package com.gloomyfish;
 
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.List;
 
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.DashPathEffect;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.view.Display;
import android.view.View;
import android.view.WindowManager;
 
public class BarChartPanel extends  View{
  
  private String plotTitle;
  private DataSeries series;
  public final static int[] platterTable = new int[]{Color.RED, Color.BLUE, Color.GREEN, Color.YELLOW, Color.CYAN};
  public BarChartPanel(Context context, String plotTitle) {
    this(context);
    this.plotTitle = plotTitle;
  }
  
  public BarChartPanel(Context context) {
    super(context);
  }
  
  public void setSeries(DataSeries series) {
    this.series = series;
  }
  
  @Override  
    public void onDraw(Canvas canvas) { 
    
    // get default screen size from system service
    WindowManager wm = (WindowManager) this.getContext().getSystemService(Context.WINDOW_SERVICE);
    Display display = wm.getDefaultDisplay();
    int width = display.getWidth();
    
    // remove application title height
    int height = display.getHeight() - 80; 
    System.out.println("width = " + width);
    System.out.println("height = " + height);
    
    // draw background
    Paint myPaint = new Paint();
    myPaint.setColor(Color.BLUE);
    myPaint.setStrokeWidth(2);
    canvas.drawRect(0, 0, width, height, myPaint);
    myPaint.setColor(Color.WHITE);
    myPaint.setStrokeWidth(0);
    canvas.drawRect(2, 2, width-2, height-2, myPaint);
    
    // draw XY Axis
    int xOffset = (int)(width * 0.1);
    int yOffset = (int)(height * 0.1);
    System.out.println("xOffset = " + xOffset);
    
    myPaint.setColor(Color.BLACK);
    myPaint.setStrokeWidth(2);
    canvas.drawLine(2+xOffset, height-2-yOffset, 2+xOffset, 2, myPaint);
    canvas.drawLine(2+xOffset, height-2-yOffset, width-2, height-2-yOffset, myPaint);
    
    // draw text title
    myPaint.setAntiAlias(true);
    myPaint.setStyle(Style.FILL);
    canvas.drawText(plotTitle, (width-2)/4, 30, myPaint);
    
    // draw data series now......
    if(series == null) {
      getMockUpSeries();
    }
    int xPadding = 10;
    if(series != null) {
    int count = series.getSeriesCount();
    int xUnit = (width - 2 - xOffset)/count;
      String[] seriesNames = series.getSeriesKeys();
      for(int i=0; i<seriesNames.length; i++) {
        canvas.drawText(seriesNames[i], xOffset + 2 + xPadding + xUnit*i, height-yOffset + 10, myPaint);
      }
      
      // Y Axis markers
      float min = 0, max = 0;
      for(int i=0; i<seriesNames.length; i++) {
        List<DataElement> itemList = series.getItems(seriesNames[i]);
        if(itemList != null && itemList.size() > 0) {
          for(DataElement item : itemList) {
            if(item.getValue() > max) {
              max = item.getValue();
            }
            if(item.getValue() < min) {
              min = item.getValue();
            }
          }
        }
      }
      
      int yUnit = 22; 
      int unitValue = (height-2-yOffset)/yUnit;
      myPaint.setStyle(Style.STROKE);
      myPaint.setStrokeWidth(1);
      myPaint.setColor(Color.LTGRAY);
      myPaint.setPathEffect(new DashPathEffect(new float[] {1,3}, 0));
      float ymarkers = (max-min)/yUnit;
      NumberFormat nf = NumberFormat.getInstance();
      nf.setMinimumFractionDigits(2);
      nf.setMaximumFractionDigits(2);
      for(int i=0; i<20; i++) {
        canvas.drawLine(2+xOffset, height-2-yOffset - (unitValue * (i+1)), width-2, height-2-yOffset - (unitValue * (i+1)), myPaint);
      }
      
      // clear the path effect
      myPaint.setColor(Color.BLACK);
      myPaint.setStyle(Style.STROKE);
      myPaint.setStrokeWidth(0);
      myPaint.setPathEffect(null); 
      for(int i=0; i<20; i++) {
        float markValue = ymarkers * (i+1);
        canvas.drawText(nf.format(markValue), 3, height-2-yOffset - (unitValue * (i+1)), myPaint);
      }
      
      // draw bar chart now
      myPaint.setStyle(Style.FILL);
      myPaint.setStrokeWidth(0);
      String maxItemsKey = null;
      int maxItem = 0;
      for(int i=0; i<seriesNames.length; i++) {
        List<DataElement> itemList = series.getItems(seriesNames[i]);
        int barWidth = (int)(xUnit/Math.pow(itemList.size(),2));
        int startPos = xOffset + 2 + xPadding + xUnit*i;
        int index = 0;
        int interval = barWidth/2;
        if(itemList.size() > maxItem) {
          maxItemsKey = seriesNames[i];
          maxItem = itemList.size();
        }
        for(DataElement item : itemList) {
          myPaint.setColor(item.getColor());
          int barHeight = (int)((item.getValue()/ymarkers) * unitValue);
          canvas.drawRect(startPos + barWidth*index + interval*index, height-2-yOffset-barHeight, 
              startPos + barWidth*index + interval*index + barWidth, height-2-yOffset, myPaint);
          index++;
        }
      }
      
      List<DataElement> maxItemList = series.getItems(maxItemsKey);
      int itemIndex = 0;
      int basePos = 10;
      for(DataElement item : maxItemList) {
        myPaint.setColor(item.getColor());
        canvas.drawRect(basePos + itemIndex * 10, height-yOffset + 15, basePos + itemIndex * 10 + 10, height-yOffset + 30, myPaint);
        myPaint.setColor(Color.BLACK);
        canvas.drawText(item.getItemName(), basePos + (itemIndex+1) * 10, height-yOffset + 25, myPaint);
        itemIndex++;
        basePos = basePos + xUnit*itemIndex;
      }
    }
  }
  
    
    public DataSeries getMockUpSeries() {
      series = new DataSeries();
      List<DataElement> itemListOne = new ArrayList<DataElement>();
      itemListOne.add(new DataElement("shoes",120.0f, platterTable[0]));
      itemListOne.add(new DataElement("jacket",100.0f, platterTable[1]));
      series.addSeries("First Quarter", itemListOne);
      
      List<DataElement> itemListTwo = new ArrayList<DataElement>();
      itemListTwo.add(new DataElement("shoes",110.0f, platterTable[0]));
      itemListTwo.add(new DataElement("jacket",50.0f, platterTable[1]));
      series.addSeries("Second Quarter", itemListTwo);
      
      List<DataElement> itemListThree = new ArrayList<DataElement>();
      itemListThree.add(new DataElement("shoes",100.0f, platterTable[0]));
      itemListThree.add(new DataElement("jacket",280.0f, platterTable[1]));
      series.addSeries("Third Quarter", itemListThree);
      
      List<DataElement> itemListFour = new ArrayList<DataElement>();
      itemListFour.add(new DataElement("shoes",120.0f, platterTable[0]));
      itemListFour.add(new DataElement("jacket",100.0f, platterTable[1]));
      series.addSeries("Fourth Quarter", itemListFour);
      return series;
    }
 
}

BarChartDemoActivity测试该组件的Android Activity启动类。

package com.gloomyfish;
 
import android.app.Activity;
import android.os.Bundle;
 
public class BarChartDemoActivity extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(new BarChartPanel(this, "Quarter Vs. sales volume"));
    }
 
}

转载请务必注明出处

相关文章
|
监控 测试技术 Shell
性能测试 基于Python结合InfluxDB及Grafana图表实时监控Android系统和应用进程
性能测试 基于Python结合InfluxDB及Grafana图表实时监控Android系统和应用进程
251 0
性能测试 基于Python结合InfluxDB及Grafana图表实时监控Android系统和应用进程
|
XML Android开发 数据格式
Android 图表开发开源库MPAndroidChart
Android 图表开发开源库MPAndroidChart
Android 图表开发开源库MPAndroidChart
|
Android开发
【Android 应用开发】Android 图表绘制 achartengine 示例解析(二)
【Android 应用开发】Android 图表绘制 achartengine 示例解析(二)
169 0
【Android 应用开发】Android 图表绘制 achartengine 示例解析(二)
|
Android开发
Android自定义柱状图表效果
本文通过示例代码介绍如何自定义简单的直方图表,此图表并非常见的直方图表,而是可以分组的。此文不会过多涉及原理,比较简单,示例图片如下(gif图片没有制作好,有闪烁,请见谅): 对于该示例的代码实现,其实重点在于坐标轴、文字、直方图的位置控制,需要随滑动距离而动态更新。
2504 0
|
前端开发 Android开发
Android 进阶自定义View(5)图表统计PieChartView圆饼图的实现
今天讲图表统计中比较常用的一个,像支付宝的月账单啥的,都是用圆饼图来做数据统计的,先看一下我最终实现的效果图: image.png 该效果实际上是两个实心圆叠加后的效果。
3851 0
|
前端开发 Android开发
Android 进阶自定义View(4)图表统计LineChartView曲线图的实现
接着上篇,今天介绍一下曲线图 / 折线图的实现方法,先上效果图: image.png 曲线图很简单了,坐标轴跟刻度线跟上篇柱状图的绘制一样一样滴,绘制曲线图,关键就是确定好Y轴的每个点,然后用绘制曲线的方法,把点连起来就OK了。
3950 0
|
前端开发 Android开发 Kotlin
Android 进阶自定义View(3)图表统计BarChartView柱状图的实现
《一》导语: 最近写了几个统计数据相关的图表,刚好放在自定义View这块的跟大家分享一下。对于图表这块,可能对于很多App的开发都用得不是很多,但是对于一些有数据分析统计需求相关的,例如P2P类型的,就比较常用了。
4026 0
|
前端开发 JavaScript Android开发
Ionic中使用Chart.js进行图表展示以及在iOS/Android中的性能差异
Angular Chart 简介 在之前的文章中介绍了使用 Ionic 开发跨平台(iOS & Android)应用中遇到的一些问题的解决方案。 在更新0.1.3版本的过程中遇到了需要使用图表展示一周搜索引擎抓取变化的需求,因为之前使用过 Chart.js, 所以去查了些资料果然是有既有的模块的。
1394 0
|
4天前
|
安全 Android开发 iOS开发
探索安卓与iOS开发的差异:平台特性与用户体验的深度对比
在移动应用开发的广阔天地中,安卓和iOS两大平台各占半壁江山。本文旨在通过数据驱动的分析方法,深入探讨这两大操作系统在开发环境、用户界面设计及市场表现等方面的差异。引用最新的行业报告和科研数据,结合技术专家的观点,本文将提供对开发者和市场分析师均有价值的洞见。
|
6天前
|
Java 开发工具 Android开发
探索Android与iOS开发的差异:平台选择对项目成功的影响
在移动应用开发的广阔天地中,Android和iOS两大平台各自占据着半壁江山。本文将深入探讨这两个平台在开发过程中的关键差异点,包括编程语言、开发工具、用户界面设计、性能优化以及市场覆盖等方面。通过对这些关键因素的比较分析,旨在为开发者提供一个清晰的指南,帮助他们根据项目需求和目标受众做出明智的平台选择。