Android如何绘制自定义表格,手把手教你实现Android中智能设备数据表格绘制

最近做一个android智能手表的app,要给用户呈现的就是用户每天,每周,每月数据信息,既然要使得用户能一眼就看出自己的数据趋势,当然最好的就是折线统计图或者柱状图了。

要实现这要的功能就需要借助于android强大的自定义控件了。

闲话休提,言归正传:

惯例先上效果,如下:

AAffA0nNPuCLAAAAAElFTkSuQmCC

下面开始自定义控件的第一步:

1.在工程目录res/values下新建attrs文件

2.在文件中声明需要的属性

3.在工程目录指定包名下创建自定义控件的类:public class HealthyTablesView extends View {    public HealthyTablesView(Context context) {        this(context,null);

}    public HealthyTablesView(Context context, AttributeSet attrs) {        this(context, attrs,0);

}    public HealthyTablesView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);

}

}

该类声明了三个参数的构造函数,让一个参数的构造函数调用二个参数的构造函数,让两个参数的构造函数调用三个参数的构造函数,接下来在第三个参数的构造函数中获取我们自定义控件的属性值:

老板,我贴代码了哦!TypedArray array = context.getTheme().obtainStyledAttributes(attrs,

R.styleable.HealthyTableView, defStyleAttr, 0);  int index = array.getIndexCount();  for (int i = 0; i 

{   int attr = array.getIndex(i);   switch (attr)

{   case R.styleable.HealthyTableView_coordinatesLineWidth:    // 这里将以px为单位,默认值为2px;

mCoordinatesLineWidth = array.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, 2, getResources().getDisplayMetrics()));    break;   case R.styleable.HealthyTableView_coordinatesTextColor:mCoordinatesTextColor = array.getColor(attr, Color.parseColor("#808080"));    break;   case R.styleable.HealthyTableView_coordinatesTextSize:

mCoordinatesTextSize = array.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 11, getResources().getDisplayMetrics()));    break;   case R.styleable.HealthyTableView_lineColor:

mLineColor = array.getColor(attr, Color.BLUE);    break;   case R.styleable.HealthyTableView_averageCircleradius:

mCircleradius = array.getDimensionPixelSize(attr,(int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10, getResources().getDisplayMetrics()));    break;   case R.styleable.HealthyTableView_bgColor:

mBgColor = array.getColor(attr, Color.WHITE);    break;   case R.styleable.HealthyTableView_lineWidth:

mLineWidth = array.getDimensionPixelSize(attr,(int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 11, getResources().getDisplayMetrics()));    break;   case R.styleable.HealthyTableView_maxcircleColor:

mMaxcircleColor = array.getColor(attr, Color.GREEN);    break;   case R.styleable.HealthyTableView_mincircleColor:

mMincircleColor = array.getColor(attr, Color.WHITE);    break;   case R.styleable.HealthyTableView_tableType:

mDrawType = array.getString(attr);    break;

}

}  // 记得释放资源

array.recycle();

}

好了,准备工作差不多了,然后呢?然后测量宽高后就开始画图了。@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        super.onMeasure(widthMeasureSpec, heightMeasureSpec);        int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);        int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);        int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);        int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);        /**

* 自定义控件的宽高必须由调用者自己指定具体的数值

*/

if (widthSpecMode == MeasureSpec.EXACTLY)

{

mWidth = widthSpecSize;

}        else

{

mWidth = 300;

}        if (heightSpecMode == MeasureSpec.EXACTLY)

{            //高是宽的3/5,这样好吗?

mHeight = (mWidth / 5) * 3;

}        else

{

mHeight = 230;

}

Log.i(TAG, "width=" + mWidth + "...height=" + mHeight);

setMeasuredDimension(mWidth, mHeight);

}

开始画图了:

重写onDraw(),在里面绘制坐标系:/**

* 画坐标系

*

* @param canvas

*/

private void drawCoordinates(Canvas canvas)

{  // X轴

Log.i(TAG, "drawCoordinates");

canvas.drawLine(getPaddingLeft(), mHeight - getPaddingBottom(),

mWidth - getPaddingRight(), mHeight - getPaddingBottom(),

xyPaint);  // X轴上的箭头

canvas.drawLine(mWidth - getPaddingRight() - 20,

mHeight - getPaddingBottom() - 10,

mWidth - getPaddingRight(), mHeight - getPaddingBottom(),

xyPaint);

canvas.drawLine(mWidth - getPaddingRight() - 20,

mHeight - getPaddingBottom() + 10,

mWidth - getPaddingRight(), mHeight - getPaddingBottom(),

xyPaint);  // 绘制Y轴

canvas.drawLine(getPaddingLeft(), getPaddingTop(), getPaddingLeft(),

mHeight - getPaddingBottom(), xyPaint);  // Y轴上的箭头

canvas.drawLine(getPaddingLeft() - 10, getPaddingTop() + 20 ,

getPaddingLeft(), getPaddingTop(), xyPaint);

canvas.drawLine(getPaddingLeft() + 10, getPaddingTop() + 20 ,

getPaddingLeft(), getPaddingTop(), xyPaint);

}

AAffA0nNPuCLAAAAAElFTkSuQmCC

接下来绘制X轴上的时间值,这里以周为例,因为没有真实的数据,此次讲义都已模拟数据为主;

定义一个数组,然后将X轴等分为7等分,画上间断线,写上数值//02号到8号,一周的时间weeks = new String[]{"02","03","04","05","06","07","08"};/**

* 绘制X轴上的数值

*

* @param canvas

*/

private void drawCoordinatesXvalues(Canvas canvas)

{  // -40 为X轴留点边界。 /6分成7等分

for (int i = 0; i 

{

textPaint.getTextBounds(weeks[i], 0, weeks[i].length(), textBound);   // 画间断线

canvas.drawLine(getPaddingLeft() + (i * XScale),

mHeight - getPaddingBottom() - 10,

getPaddingLeft() + (i * XScale),

mHeight - getPaddingBottom(), xyPaint);   // -textBound.width()/2 是为了让字体和间断线居中

canvas.drawText(weeks[i],

getPaddingLeft() + (i * XScale) - textBound.width() / 2,

mHeight - getPaddingBottom() + 30, textPaint);

}

}

上图:

AAffA0nNPuCLAAAAAElFTkSuQmCC

上面的逻辑和计算并不复杂,就是将X轴的距离等分7等分,然后画上间断线和数值就OK了。

接下来计算Y轴上的要画得数值,因为Y轴上的数值要根据用户的真实数据来确定,所以幅度很大,不确定性因素也很多。这样就需要我们动态的计算Y轴上的数值区间:

1.首先计算出用户数据中的最大值和最小值来确定区间:

2.将计算出的最大值和最小值向上向下取一定幅度的值,比如最大值123,最小值63,最大值就可以取123+10,最小值取60-10,/**

* 最高位 为什么要取出最高值,这里主要是通过计算动态的算出Y轴上的数值区间,

* 比如心率是60-100,不计算写死就是0-180,这样折线的所有点就全部落在中间一点的地带,上下都有较大的空白,影响美观(心率一般在60-100之间)

* 比如计步的幅度很大,如果不通过动态计算就不知道Y轴画的数值给多少合适,比如Y轴数值写死为0-20000,

* 那么如果运动量偏少,比如都是1000步左右,折线就显得几乎和X=0平齐了

* @param num

* @return

*/

private int getResultNum(float num)

{  int resultNum;  int gw = 0; // 个位

int sw = 0; // 十位

int bw = 0; // 百位

int qw = 0; // 千位

int ww = 0; // 万位

if (num > 0)

{

gw = (int) (num % 10 / 1);

}  if (num > 10)

{

sw = (int) (num % 100 / 10);

}  if (num > 100)

{

bw = (int) (num % 1000 / 100);

}  if (num > 1000)

{

qw = (int) (num % 10000 / 1000);

}  if (num > 10000)

{

ww = (int) (num % 100000 / 10000);

}  /*********************************/

if (ww >= 1)

{

resultNum=qw>5? ww * 10000 + 10000: ww * 10000 + 5000;

}  else if (qw >= 1)

{

resultNum=bw>5?qw*1000+1000:qw*1000+500;

}  else if (bw >= 1)

{

resultNum = bw * 100 + sw * 10 + 10;

}  else if (sw >= 1)

{

resultNum=gw>5?sw * 10 + 20:sw * 10 + 10;

}  else

{

resultNum = 0;

}  return resultNum;

}

上面的代码显然是统一加上了某个数值,这个数值可以根据你的项目需求自己定义,但取下限的时候显然就要减去某个数值:具体为什么要这么做注释写得比较详细。

真正意义上的计算Y轴上数值刻度了:/**

* 传入数组中的最大值和最小值,计算出在Y轴上数值的区间

*

* @param max

* @param min

* @return

*/

private int[] cacluterYValues(float max, float min)

{  int[] values;  int min1;  int max1;  int resultNum = getResultNum(min); // 计算出的最小值

max1 = getResultNum(max); // 计算出最大值

if (resultNum <= 20) // 如果小于等于20 就不要减20,否则Y最小值是0了

{

min1 = resultNum - 10;

}  else

{

min1 = resultNum - 20;

}  if (resultNum <= 10 || resultNum == 0) // 如果小于10 就不用再减了,否则就是负数了

{

min1 = 0;

}  // 将计算出的数值均分为5等分

double ceil = Math.ceil((max1 - min1) / 4);

values = new int[]

{ min1, (int) (min1 + ceil), (int) (min1 + ceil * 2),

(int) (min1 + ceil * 3), (int) (min1 + ceil * 4) };  return values;

}

这样就计算出来了Y轴需要动态画的数值。

接下来就开始画吧:模拟数据的代码这里就不贴了,后面会给出整个项目的源码,感兴趣的自己看看就懂了。/**

* 画Y轴上的数值

*

* @param canvas

*/

private void drawYValues(Canvas canvas, float max, int[] value)

{ //这里除以max这个最大值是为了有多大的去见就分成多少等分,是的后面折线的点更精准,否者就会对不齐刻度,

float YScale = ((float) mHeight - getPaddingBottom() - getPaddingTop()

- 40) / max;  for (int i = 0; i 

{

String text = value[i] + "";   int scale = value[i] - value[0];

canvas.drawLine(getPaddingLeft(),

mHeight - getPaddingBottom() - (YScale * scale),

getPaddingLeft() + 10,

mHeight - getPaddingBottom() - (YScale * scale), textPaint);

textPaint.getTextBounds(text, 0, text.length(), textBound);   // +textBound.height()/2 主要是为了让字体和间断线居中

canvas.drawText(text,

getPaddingLeft() - 40, mHeight - getPaddingBottom()

- (YScale * scale) + textBound.height() / 2,

textPaint);

}

}

效果图:

AAffA0nNPuCLAAAAAElFTkSuQmCC

显然,画线的逻辑并不复杂,只是计算Y轴上的值花了一定精力。

现在画折线了:

1.首先画出小圆点,然后将各个小圆点收尾相连接就是折线效果了:private void drawLine(Canvas canvas, float arraymax, float yMin)

{  //这里是整个Y轴可用高度除以最大值,就是每个值占有刻度上的几等分;

float YScale = ((mHeight - getPaddingBottom() - getPaddingTop() - 40))/ arraymax;  for (int i = 0; i 

{   //为什么是values[i] - arraymin(数据值-Y坐标最小值)?

//因为圆点是以数据值来画得,数据值和Y轴坐标最小值的差就是整个数据的区间;

int scale = (int) (values[i] - yMin);   int j;   /**

* 画折线

*/

if (i 

{    int textScale = (int) (values[i + 1] - yMin);

j = i + 1;

canvas.drawLine(getPaddingLeft() + (XScale * i),

mHeight - getPaddingBottom() - (YScale * scale),

getPaddingLeft() + (XScale * j),

mHeight - getPaddingBottom() - (YScale * textScale),

linePaint);

}

String text = String.valueOf(values[i]);

textPaint.getTextBounds(text, 0, text.length(), textBound);

canvas.drawText(text,

getPaddingLeft() + (XScale * i) - textBound.width() / 2,

mHeight - getPaddingBottom() - (YScale * scale) - 15,

textPaint);   /**

* 两个小圆点

*/

canvas.drawCircle(getPaddingLeft() + (XScale * i),

mHeight - getPaddingBottom() - (YScale * scale), 10,

maxCirclePaint);

canvas.drawCircle(getPaddingLeft() + (XScale * i),

mHeight - getPaddingBottom() - (YScale * scale), 10 - 2,

minCirclePaint);

}

}

注意上面的arraymax yMin两个值的含义。arraymax一定是Y轴上区间的差值,比如轴上的数组为[60,70,80,90,100],那么arrayma就是100-60;yMin见注释。

这里为什么要画两个圆?两个同心圆能够达到大圆是空心的效果,那画笔设置为STROKE不就行了?

AAffA0nNPuCLAAAAAElFTkSuQmCC

看到了吧,感觉从圆中间穿过去了,是不是觉得不爽啊,于是有人就说,我把圆的半径算出来就行了,画线的时候减去这个半径,哥哥,如果前后两点不在同一直线上你还得算夹角,你慢慢算吧。算好了告诉我!

AAffA0nNPuCLAAAAAElFTkSuQmCC是不是美观很多啊?骚年?

这里的工作基本就完了,至于睡眠要画两条线,获取不同的数据 调用两次画圆点和线的方法就OK了。

至于代码里如果觉得部分逻辑混乱冗余,那就将就一下吧。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 数据台是一个集管理和整合企业数据的平台,它可以为企业提供数据分析、决策支持、业务运营等方面的服务。搭建数据台有助于提高企业的数据治理能力、加速数据价值的释放,并支持企业实现数字化转型。 搭建数据台的过程可以通过以下步骤来实现: 1. 数据需求识别:首先,企业需要明确自己的数据需求,包括哪些数据需要被整合和管理,以及需要利用这些数据做出什么样的决策或支持什么业务需求。 2. 数据源接入:根据数据需求,企业需要将来自不同系统和渠道的数据源接入到数据。这可以通过建立数据连接、集成接口、ETL工具等方式实现。 3. 数据清洗和整合:接入的数据往往需要经过清洗和整合,以确保数据的准确性、一致性和完整性。通过数据清洗和整合,可以提高数据质量并消除数据冗余。 4. 数据存储和管理:在数据,企业需要建立合适的数据存储和管理机制,以确保数据的安全性和可访问性。常见的数据存储方式有关系型数据库、数据仓库、数据湖等。 5. 数据加工和分析:通过数据加工和分析,可以为企业提供丰富的数据洞察和决策支持。这可以通过使用数据挖掘、机器学习、人工智能等技术实现。 6. 数据可视化和报表:将分析结果以可视化的形式展示给用户,并生成数据报表,以帮助用户更好地理解和利用数据。 最后,企业可以通过提供数据台的PDF下载等方式,将搭建数据台的经验和指南分享给其他企业,以促进数据台在行业的普及和应用。这样,更多的企业可以借鉴和应用这些经验,加速自身的数字化转型和数据驱动业务发展的进程。 ### 回答2: 数据台是指企业利用先进的数据技术和平台,将分散的数据资源进行整合和集成,实现数据的全面管理和应用。搭建数据台有助于企业更好地理解和使用数据,提升决策效果和业务价值。 要搭建数据台,首先需要明确目标和需求。企业需要明确想要实现的具体目标,并根据业务需求确定所需要的数据资源,以及数据台的功能和特性。 接下来,需要选择适合企业需求的数据台平台。有许多不同的数据台平台可以选择,包括开源的平台和商业化的平台。企业可以根据自身技术实力、预算和需求等综合考虑,选择最适合的平台。 然后,需要进行数据的集成和整合。企业需要将分散存储在不同系统数据资源进行整合,建立数据集市或数据仓库。这需要清洗、清理和转换数据,确保数据的质量和一致性。 同时,还需要建立数据治理体系。数据台需要有明确的数据治理策略和规范,包括数据的标准化、存储和访问权限的管理等。这有助于提高数据的可信度和安全性。 最后,需要将数据台与企业的业务系统进行集成和应用。数据台可以为企业提供数据分析、数据挖掘和机器学习等功能,帮助企业更好地理解和应用数据,推动业务发展。 在搭建数据台的过程,企业可以参考一些实战经验和案例,了解其他企业在搭建数据台时遇到的问题和解决方法。同时,也可以寻求专业的培训或咨询支持,帮助企业顺利地搭建自己的数据台。 总之,搭建数据台需要明确目标、选择合适的平台、进行数据的集成和治理,最终与业务系统进行集成和应用。通过合理规划和实施,企业可以有效地搭建数据台,提升数据价值和业务效果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值