因为本次需求中只有柱状图和扇形图的需求,所以本次示例将两种类型的图放在了一个view里,先看看自定义属性
<declare-styleable name="ChartView"> <attr name="textColor" format="color" /> <attr name="textSize" format="dimension" /> <attr name="chartType" format="enum"> <enum name="HISTOGRAM" value="101"/> <enum name="FAN" value="102"/> </attr> <attr name="proportion" format="integer"/> <attr name="axisColor" format="color"/> <attr name="sectionColor" format="color"/> <attr name="sectionAmount" format="integer"/> </declare-styleable>其中chartType表示图表类型,也就是上面提到的柱状或扇形图,proportion表示在柱状图里,每个柱子与柱子之间的间隔的宽度比,axisColor为坐标轴的颜色,sectionAmount表示坐标轴分成几个区间并且以横线将坐标轴分成对应个区域。
在onMeasure中对view的高度进行了一定处理,可能情况考虑得不周全,但在布局中将view的高度设为wrap_content就行了,默认高宽比是4:3。
先贴上图标中需要用到的数据模型,下文中用到的mEntities就是该模型的一个列表。
public static class Entity{ private String text; private int amount; private int color; } private void onDrawHistogram(Canvas canvas){ int paddingLeft = getPaddingLeft(); int paddingTop = getPaddingTop(); int paddingRight = getPaddingRight(); int paddingBottom = getPaddingBottom(); int contentWidth = getWidth() - paddingLeft - paddingRight; int contentHeight = getHeight() - paddingTop - paddingBottom; //柱子的个数 int column = mEntities.size(); //数据中amount最大的值,用该值来决定所有柱子的高度 int maxAmount = 0; for(Entity e : mEntities){ maxAmount = maxAmount < e.getAmount() ? e.getAmount() : maxAmount; } //n表示上面的maxAmount数值的高度为contentHeight的n倍(默认就是3/4) float n = (mSectionAmount-1)*1f/mSectionAmount; //每一个amount单元的高度 float perUnitHeight = contentHeight * n /maxAmount; //计算每列的宽度 int distance = contentWidth / ((mProportion+1)*column+1); //开始绘制 //绘制四条区间线 mChartPaint.setColor(mSectionColor); float unitSectionHeight = (contentHeight) / mSectionAmount; for(int i=0;i<mSectionAmount;i++){ canvas.drawLine(paddingLeft,paddingTop+unitSectionHeight*i,contentWidth,paddingTop+unitSectionHeight*i+1,mChartPaint); } //绘制柱状图 int startX = paddingLeft; for (int i = 0;i < column ;i++){ Entity e = mEntities.get(i); mChartPaint.setColor(e.getColor()); float h = contentHeight - perUnitHeight*e.getAmount(); canvas.drawRect(startX,contentHeight - mAnimValue * (contentHeight-h),startX+mProportion*distance,contentHeight,mChartPaint); //需要注意文字所在的高度计算 canvas.drawText(e.getText(),0,e.getText().length(),startX+mProportion/2f*distance,h-2*mTextSize,mTextPaint); String _amount = String.valueOf(e.getAmount()); canvas.drawText(_amount,0,_amount.length(),startX+mProportion/2f*distance,h-mTextSize,mTextPaint); startX += (mProportion+1)*distance; } //最后画绘制坐标轴,最后画的好处是不会让前面画的柱子挡住x轴 mChartPaint.setColor(mAxisColor); //y轴 canvas.drawLine(paddingLeft,paddingTop,paddingLeft+1,contentHeight,mChartPaint); //x轴 canvas.drawLine(paddingLeft,contentHeight-1,contentWidth,contentHeight,mChartPaint); }在代码中有一个mAnimValue变量,它表示给view添加动画的话,当前动画进度的值,后面会介绍动画的实现。
动画用起来就简单了,配合ValueAnimator,改变上面的mAnimValue,更新下就可以啦。至于什么时候使用,It’s up to yourself!!! 因为不晓得gif怎么弄,就算啦。
public void startAnimation(){ mValueAnimator = ValueAnimator.ofFloat(0f,1f); mValueAnimator.setDuration(DURATION); mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mAnimValue = (float) animation.getAnimatedValue(); postInvalidate(); } }); mValueAnimator.start(); }我觉得还是有必要贴下onDraw的代码
protected void onDraw(Canvas canvas) { super.onDraw(canvas); if(mEntities == null || mEntities.size() == 0){ return; } if(mChartType == CHART_TYPE_HISTOGRAM){ onDrawHistogram(canvas); }else if(mChartType == CHART_TYPE_FAN){ onoDrawFan(canvas); } }布局中使用
<com.huicheng.jingkaiqu.view.widgets.ChartView xmlns:app="http://schemas.android.com/apk/res-auto" android:background="@color/white" android:id="@+id/chartViewFan" android:layout_width="match_parent" android:layout_height="wrap_content" app:textSize="15sp" app:textColor="@color/textColor" app:chartType="FAN" app:proportion="4" android:padding="12dp"> </com.huicheng.jingkaiqu.view.widgets.ChartView> <com.huicheng.jingkaiqu.view.widgets.ChartView xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/chartView" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@color/white" android:padding="12dp" app:axisColor="@color/textColor" app:chartType="HISTOGRAM" app:proportion="4" app:sectionAmount="4" app:sectionColor="@color/tab_tv_normal" app:textColor="@color/textColor" app:textSize="15sp"> </com.huicheng.jingkaiqu.view.widgets.ChartView>