代码在注释中,与柱状图的实现类似
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.DashPathEffect;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.AttributeSet;
import android.view.View;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
/**
* Created by wangliang on 0013/2016/9/13.
* 思路:每传递一次坐标名称则更新一次视图,坐标的传递采用动态计算的方法,当更改坐标轴名称的时候,所有视图清空进行重绘,柱状图显示数量由所传递的徐州的名称决定
*/
public class BrokenLineGrapNoScrollView extends View {
private Context context;
private final String TAG = "HistogramNoScrollView";
private Integer defaultAxisColor = Color.BLACK;
private Integer defaultAxisWidth;
private List<String> xAxisListNames;
private List<String> yAxisListNames;
private List<Coordinate> xAxisCoordinateList;//所有x轴基准点坐标
private List<Coordinate> yAxisCoordinateList;//所有y轴基准点坐标
private List<Coordinate> xAxisTextCoordinateList;//所有x轴文字坐标
private List<Coordinate> yAxisTextCoordinateList;//所有y轴文字坐标
private List<Coordinate> listAllNoAxisCoordinate;//除轴上坐标之外的其他柱状图的坐标
private Paint xAxisTextPaint;//x轴文字画笔
private Paint yAxisTextPaint;//y轴文字画笔
private Paint xAxisPaint;//x轴画笔
private Paint yAxisPaint;//y轴画笔
private Paint brokenLineGrapPaint;//柱状图画笔
private Paint averageValuePaint;//平均值线
private Path averageValuePath;//平均值线
private Integer axisTextColor;
private Integer axisTextSize;
private Integer defaultBetweenXAxisAndTextSpace;
private Integer defaultBetweenYAxisAndTextSpace;
private int xCoordinateDistance;//x轴每段的宽度
private Double maxValue;//柱状图所能显示的最大值
private Double averageValue;//柱状图的平均值
private Map<String,Double> histogramValue;//柱状图所哟柱子的值
private float histogramShowFanWei;//柱状图所能显示的范围
private Boolean whetherShowXAxis;//是否显示y轴
private Boolean whetherShowYAxis;//是否显示x轴
private Boolean whetherShowAverageValue;//是否显示柱状图平均值
public BrokenLineGrapNoScrollView(Context context) {
super(context);
initSetting(context,null);
}
public BrokenLineGrapNoScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
initSetting(context,null);
}
public BrokenLineGrapNoScrollView(Context context, AttributeSet attrs, Integer defStyleAttr) {
super(context, attrs, defStyleAttr);
initSetting(context,defStyleAttr);
}
private void initSetting(Context context, Integer defStyleAttr) {
this.context = context;
defaultBetweenXAxisAndTextSpace = 0;
defaultBetweenYAxisAndTextSpace = 0;
defaultAxisColor = Color.BLACK;
defaultAxisWidth = context.getResources().getDimensionPixelOffset(R.dimen.common_dp_0_1);
axisTextColor = Color.RED;
axisTextSize = context.getResources().getDimensionPixelOffset(R.dimen.default_text_medium);
xAxisTextPaint = new Paint();
xAxisTextPaint.setTextSize(axisTextSize);
xAxisTextPaint.setColor(axisTextColor);
yAxisTextPaint = new Paint();
yAxisTextPaint.setTextSize(axisTextSize);
yAxisTextPaint.setColor(axisTextColor);
xAxisPaint = new Paint();
xAxisPaint.setColor(defaultAxisColor);
// xAxisPaint.setStrokeWidth(defaultAxisWidth);
xAxisPaint.setAntiAlias(true);
yAxisPaint = new Paint();
yAxisPaint.setColor(defaultAxisColor);
// yAxisPaint.setStrokeWidth(defaultAxisWidth);
yAxisPaint.setAntiAlias(true);
brokenLineGrapPaint = new Paint();
brokenLineGrapPaint.setColor(Color.BLACK);
brokenLineGrapPaint.setTextSize(axisTextSize);
brokenLineGrapPaint.setAntiAlias(true);
averageValuePaint = new Paint ( ) ;
averageValuePaint.setColor ( Color.BLACK ) ;
//设置画直线格式
averageValuePaint.setStyle ( Paint.Style.STROKE ) ;
/**设置虚线效果
* 里边的float数组的意思是:先画长度为3的实线,再间隔长度为2的空白,之后一直重复这个单元。
* 这个数组的长度只要大于等于2就行,你可以设置多个数值,产生不同效果,最后这个0指的是与起始位置的偏移量。
*/
averageValuePaint.setPathEffect ( new DashPathEffect( new float [ ] { 3, 2 }, 0 ) ) ;
}
/**
* 设置x轴坐标文字
* @param xAxisListNames
* @return
*/
public BrokenLineGrapNoScrollView setXAxisNames(List<String> xAxisListNames, boolean whetherShowXAxis){
this.xAxisListNames = xAxisListNames;
this.whetherShowXAxis = whetherShowXAxis;
xAxisCoordinateList = null;//所有x轴基准点坐标
yAxisCoordinateList = null;//所有y轴基准点坐标
xAxisTextCoordinateList = null;//所有x轴文字坐标
listAllNoAxisCoordinate = null;//除轴上坐标之外的其他柱状图的坐标
return this;
}
/**
* 设置y轴坐标文字
* @param yAxisListNames
* @return
*/
public BrokenLineGrapNoScrollView setYAxisNames(List<String> yAxisListNames, boolean whetherShowYAxis){
this.yAxisListNames = yAxisListNames;
this.whetherShowYAxis = whetherShowYAxis;
yAxisCoordinateList = null;//所有y轴基准点坐标
yAxisTextCoordinateList = null;
listAllNoAxisCoordinate = null;//除轴上坐标之外的其他柱状图的坐标
return this;
}
/**
* 设置柱状图所要显示的最大值以及平均值
* @param maxValue 最大值
* @param averageValue 平均值
* @param whetherShowAverageValue 是否显示平均值线
* @return
*/
public BrokenLineGrapNoScrollView setMaxValueAndAverageValue(double maxValue, double averageValue, boolean whetherShowAverageValue){
this.maxValue = maxValue;
this.averageValue = averageValue;
this.whetherShowAverageValue = whetherShowAverageValue;
listAllNoAxisCoordinate = null;//除轴上坐标之外的其他柱状图的坐标
return this;
}
public BrokenLineGrapNoScrollView setHistogramValue(Map<String,Double> histogramValue){
this.histogramValue = histogramValue;
listAllNoAxisCoordinate = null;//除轴上坐标之外的其他柱状图的坐标
return this;
}
@Override
protected void onDraw(Canvas canvas) {
if(xAxisTextCoordinateList == null){
getXAxisTextCoordinateList();
}
if(xAxisCoordinateList == null){
getXAxisCoordinateList();
}
if(yAxisTextCoordinateList == null){
getYAxisTextCoordinateList();
}
if(yAxisCoordinateList == null){
getYAxisCoordinateList();
}
if(listAllNoAxisCoordinate == null){
getListAllNoAxisCoordinate();
}
/**
* 获取所要画的虚线的path
*/
if(averageValuePath == null && maxValue != null && averageValue != null){
histogramShowFanWei = getHeight() - (fm.descent - fm.ascent) - defaultBetweenXAxisAndTextSpace;
averageValuePath = new Path();
averageValuePath.moveTo(0, (float) (histogramShowFanWei - histogramShowFanWei * (averageValue / maxValue)));
averageValuePath.lineTo(getWidth() - defaultBetweenYAxisAndTextSpace, (float) (histogramShowFanWei - histogramShowFanWei * (averageValue / maxValue)));
}
/**
* 画x轴
*/
if(xAxisTextCoordinateList.size() > 0 && whetherShowXAxis) {
canvas.drawLine(getX() - defaultBetweenYAxisAndTextSpace,
getHeight() - (fm.descent - fm.ascent) - defaultBetweenXAxisAndTextSpace,
getWidth(),
getHeight() - (fm.descent - fm.ascent) - defaultBetweenXAxisAndTextSpace
, xAxisPaint);
}
/**
* 画y轴
*/
if(yAxisTextCoordinateList.size() > 0 && whetherShowYAxis) {
canvas.drawLine(getX() - defaultBetweenYAxisAndTextSpace,
0,
getX() - defaultBetweenYAxisAndTextSpace,
getHeight() - (fm.descent - fm.ascent) - defaultBetweenXAxisAndTextSpace
, xAxisPaint);
}
/**
* 画虚线,代表着平均值
*/
if(listAllNoAxisCoordinate.size() > 0 && whetherShowAverageValue){
canvas.drawPath(averageValuePath,averageValuePaint);
}
/**
* 绘制x轴上的坐标刻度文字
*/
for(int i = 0 ; i < xAxisTextCoordinateList.size() ; i++){
canvas.drawText(xAxisTextCoordinateList.get(i).getCoordinatename(),
xAxisTextCoordinateList.get(i).getxCoordinate(),
xAxisTextCoordinateList.get(i).getyCoordinate(),
xAxisTextPaint);
}
/**
* 绘制折线图
*/
for(int i = 0 ; i < listAllNoAxisCoordinate.size() ; i++){
float textWidth = brokenLineGrapPaint.measureText(listAllNoAxisCoordinate.get(i).getCoordinatename());
Integer circleRadius = null;
if(circleRadius == null){
circleRadius = 10;
}
canvas.drawCircle(listAllNoAxisCoordinate.get(i).getxCoordinate(),
listAllNoAxisCoordinate.get(i).getyCoordinate(),circleRadius,brokenLineGrapPaint);
if(listAllNoAxisCoordinate.get(i).getxCoordinate() <= getX() + defaultBetweenYAxisAndTextSpace){
canvas.drawText(listAllNoAxisCoordinate.get(i).getCoordinatename(),
listAllNoAxisCoordinate.get(i).getxCoordinate(),
listAllNoAxisCoordinate.get(i).getyCoordinate() - circleRadius - 10 ,brokenLineGrapPaint);
}else if(listAllNoAxisCoordinate.get(i).getxCoordinate() >= getWidth()){
canvas.drawText(listAllNoAxisCoordinate.get(i).getCoordinatename(),
listAllNoAxisCoordinate.get(i).getxCoordinate() - textWidth,
listAllNoAxisCoordinate.get(i).getyCoordinate() - circleRadius - 10 ,brokenLineGrapPaint);
}else {
canvas.drawText(listAllNoAxisCoordinate.get(i).getCoordinatename(),
listAllNoAxisCoordinate.get(i).getxCoordinate() - textWidth / 2,
listAllNoAxisCoordinate.get(i).getyCoordinate() - circleRadius - 10 ,brokenLineGrapPaint);
}
if(i > 0){
canvas.drawLine(listAllNoAxisCoordinate.get(i - 1).getxCoordinate(),
listAllNoAxisCoordinate.get(i - 1).getyCoordinate(),
listAllNoAxisCoordinate.get(i).getxCoordinate(),
listAllNoAxisCoordinate.get(i).getyCoordinate(),brokenLineGrapPaint);
}
}
}
private Paint.FontMetrics fm;
/**
* 根据文字获得文字相对于x轴的坐标
* @param text
* @param xCoordinateDistance
* @return
*/
private float[] getXtextCoordinate(String text,float xCoordinateDistance){
float[] xy = new float[2];
fm = xAxisTextPaint.getFontMetrics();
float textWidth = xAxisTextPaint.measureText(text);
float textHeight = fm.descent - fm.ascent;
xy[0] = xCoordinateDistance - textWidth / 2;
xy[1] = getHeight();
return xy;
}
/**
* 获取x轴刻度文字坐标
*/
private void getXAxisTextCoordinateList(){
xAxisTextCoordinateList = new ArrayList<>();
if(xAxisListNames != null) {
xCoordinateDistance = getWidth() / (xAxisListNames.size() + 1);
for (int i = 0; i < xAxisListNames.size(); i++) {
float[] xtextCoordinate = getXtextCoordinate(xAxisListNames.get(i), xCoordinateDistance * (i + 1));
xAxisTextCoordinateList.add(new Coordinate(xtextCoordinate[0], xtextCoordinate[1], xAxisListNames.get(i)));
}
}
}
/**
* 获取x轴线坐标
*/
private void getXAxisCoordinateList(){
xAxisCoordinateList = new ArrayList<>();
if(xAxisListNames != null) {
fm = xAxisTextPaint.getFontMetrics();
float textHeight = fm.descent - fm.ascent;
xCoordinateDistance = getWidth() / (xAxisListNames.size() + 1);
for (int i = 0; i < xAxisListNames.size(); i++) {
xAxisCoordinateList.add(new Coordinate(getX() + xCoordinateDistance * (i + 1)
, getHeight() - textHeight, xAxisListNames.get(i)));
}
}
}
/**
* 获取y轴刻度文字坐标
*/
private void getYAxisTextCoordinateList(){
yAxisTextCoordinateList = new ArrayList<>();
}
/**
* 获取y轴线坐标
*/
private void getYAxisCoordinateList(){
yAxisCoordinateList = new ArrayList<>();
}
private void getListAllNoAxisCoordinate(){
listAllNoAxisCoordinate = new ArrayList<>();
histogramShowFanWei = getHeight() - (fm.descent - fm.ascent) - defaultBetweenXAxisAndTextSpace;
if(maxValue != null && xAxisListNames != null && xAxisCoordinateList != null){
for(int i = 0 ; i < xAxisListNames.size() ; i++) {
if (histogramValue.get(xAxisListNames.get(i)) != null) {
listAllNoAxisCoordinate.add(new Coordinate(xAxisCoordinateList.get(i).getxCoordinate(),
(float) (xAxisCoordinateList.get(i).getyCoordinate() - (histogramShowFanWei * (histogramValue.get(xAxisListNames.get(i)) / maxValue))),
xAxisCoordinateList.get(i).getCoordinatename()));
}
}
Collections.sort(listAllNoAxisCoordinate,new Coordinate());
}
}
}
调用方法:
List<String> list = new ArrayList<>();
list.add("123");
list.add("456");
list.add("789");
list.add("147");
list.add("258");
Map<String,Double> map = new HashMap<>();
map.put(list.get(0),10d);
map.put(list.get(1),15d);
map.put(list.get(3),7d);
((HistogramNoScrollView)findViewById(R.id.test1)).setXAxisNames(list,true);
((HistogramNoScrollView)findViewById(R.id.test1)).setMaxValueAndAverageValue(20,10,true);
((HistogramNoScrollView)findViewById(R.id.test1)).setHistogramValue(map);
安卓自定义控件之不可滑动柱状图:http://blog.youkuaiyun.com/cmwly/article/details/52537297
安卓自定义控件之坐标存储类:http://blog.youkuaiyun.com/cmwly/article/details/49951913