此demo也是从我们真实项目中copy出来的;大致步骤和制作MyTabView差不多,只不过稍微繁琐一些;
/**
* Created by zheng on 2017/12/4.
* 横向的柱形图
*
* 1:绘制XY轴
* 2:绘制XY轴刻度和旁边的文本
* 3:绘制圆角矩形(六个月份的柱子)
* 4:绘制和背景颜色一样的矩形(遮挡圆角矩形左边的圆角)
*
*/
public class HPillarView extends View {
private Paint mPaint;
//线的颜色,柱子右边显示**人的颜色
private int lineColor, dataFontColor;
//X轴距离左边和右边的距离,Y轴距离上边和下边的距离
private float xLeftSpace, xRightSpace,
yTopSpace, yBottomSpace;
//XY轴刻度的大小:X轴刻度的高,Y轴刻度的宽
private float xDividerHeight, yDividerWidth;
//XY轴刻度颜色
private int xDividerColor, yDividerColor;
//x轴下面的字体距离X轴的距离,Y轴左边的字体距离Y轴的距离
private float txtXSpace, txtYSpace;
//每个柱子的高度
private float pillarHeight;
//柱子距离Y轴刻度的距离
private float pillarMarginY = 10;
private final float FULL_AMOUNT = 7000;
private List<Integer> dataList = new ArrayList<>();
private float xyFontSize;
public HPillarView(Context context) {
this(context, null);
}
public HPillarView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public HPillarView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initXmlAttrs(context, attrs);
initPaint();
}
private void initPaint() {
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setStyle(Paint.Style.FILL);
mPaint.setStrokeWidth(2);
}
private void initXmlAttrs(Context context, AttributeSet attrs) {
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.hpillar);
if (typedArray == null) return;
lineColor = typedArray.getColor(R.styleable.hpillar_line_color_h, Color.BLACK);
dataFontColor = typedArray.getColor(R.styleable.hpillar_data_font_color_h, Color.BLACK);
xLeftSpace = typedArray.getDimension(R.styleable.hpillar_x_left_space_h, 50);
xRightSpace = typedArray.getDimension(R.styleable.hpillar_x_right_space_h, 80);
yTopSpace = typedArray.getDimension(R.styleable.hpillar_y_top_space_h, 30);
yBottomSpace = typedArray.getDimension(R.styleable.hpillar_y_bottom_space_h, 100);
yDividerWidth = typedArray.getDimension(R.styleable.hpillar_y_divider_width, 14);
xDividerHeight = typedArray.getDimension(R.styleable.hpillar_x_divider_height, 14);
xDividerColor = typedArray.getColor(R.styleable.hpillar_x_divider_color, Color.BLACK);
yDividerColor = typedArray.getColor(R.styleable.hpillar_y_divider_color, Color.BLACK);
xyFontSize = typedArray.getDimension(R.styleable.hpillar_xy_font_size_h, 30);
txtXSpace = typedArray.getDimension(R.styleable.hpillar_txt_x_space, 20);
txtYSpace = typedArray.getDimension(R.styleable.hpillar_txt_y_space, 15);
pillarHeight = typedArray.getDimension(R.styleable.hpillar_pillar_height, 20);
typedArray.recycle();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (dataList.size() == 0) return;
/**
* 绘制XY轴
*/
initPaintColor(lineColor);
float xLineTop = getHeight() - yBottomSpace;//view的高度 - Y轴距离view顶部的距离
//两点确定一线
canvas.drawLine(
xLeftSpace, xLineTop,
getWidth() - xRightSpace, xLineTop,
mPaint
);
canvas.drawLine(
xLeftSpace, yTopSpace,
xLeftSpace, getHeight() - yBottomSpace,
mPaint
);
/**
* 绘制Y轴刻度 和 左边的文字
*/
//Y轴每个刻度的高度:获取Y轴真是高度,然后除以6获取每个刻度的高度
float spaceVertical = (getHeight() - yTopSpace - yBottomSpace) / 6;
//Y轴刻度左右的X坐标
float yScaleLeftX = xLeftSpace - yDividerWidth / 2;
float yScaleRightX = xLeftSpace + yDividerWidth / 2;
float[] pts = {
yScaleLeftX, yTopSpace, yScaleRightX, yTopSpace,
yScaleLeftX, yTopSpace + spaceVertical, yScaleRightX, yTopSpace + spaceVertical,
yScaleLeftX, yTopSpace + spaceVertical * 2, yScaleRightX, yTopSpace + spaceVertical * 2,
yScaleLeftX, yTopSpace + spaceVertical * 3, yScaleRightX, yTopSpace + spaceVertical * 3,
yScaleLeftX, yTopSpace + spaceVertical * 4, yScaleRightX, yTopSpace + spaceVertical * 4,
yScaleLeftX, yTopSpace + spaceVertical * 5, yScaleRightX, yTopSpace + spaceVertical * 5
};
//绘制一组线:pts中得数据四个为一组,同样代表起点终点坐标
canvas.drawLines(pts, mPaint);
//绘制Y轴刻度左边文字
Rect yTxtRect;
mPaint.setTextSize(xyFontSize);
int[] ys = {2, 4, 6, 8, 10, 12};
for (int i = 0; i < ys.length; i++) {
String number = ys[(ys.length - 1 - i)] + "";
yTxtRect = new Rect();
//为了让六个月份居中,测量最大数
mPaint.getTextBounds("12", 0, 2, yTxtRect);
//设置居中
mPaint.setTextAlign(Paint.Align.CENTER);
canvas.drawText(
number,
xLeftSpace - yTxtRect.width() / 2 - txtYSpace,
yTopSpace + (spaceVertical * i) + yTxtRect.height() / 2,
mPaint
);
}
/**
* 绘制X轴刻度 和 X轴下面的文字
*/
//获取x轴刻度间距 view宽度 - x轴左边距 - X轴右边距 - Y轴刻度宽度一半 - 柱子距离刻度的距离
float xSpaceVertical = (getWidth() - xLeftSpace - xRightSpace - yDividerWidth / 2 - pillarMarginY) / 7;
//X刻度上下Y坐标
float xScaleTopY = getHeight() - yBottomSpace - xDividerHeight / 2;
float xScaleBottomY = getHeight() - yBottomSpace + xDividerHeight / 2;
float[] xptsx = {
xLeftSpace + yDividerWidth / 2 + pillarMarginY + xSpaceVertical, xScaleTopY, xLeftSpace + xSpaceVertical + yDividerWidth / 2 + pillarMarginY, xScaleBottomY,
xLeftSpace + yDividerWidth / 2 + pillarMarginY + xSpaceVertical * 2, xScaleTopY, xLeftSpace + xSpaceVertical * 2 + yDividerWidth / 2 + pillarMarginY, xScaleBottomY,
xLeftSpace + yDividerWidth / 2 + pillarMarginY + xSpaceVertical * 3, xScaleTopY, xLeftSpace + xSpaceVertical * 3 + yDividerWidth / 2 + pillarMarginY, xScaleBottomY,
xLeftSpace + yDividerWidth / 2 + pillarMarginY + xSpaceVertical * 4, xScaleTopY, xLeftSpace + xSpaceVertical * 4 + yDividerWidth / 2 + pillarMarginY, xScaleBottomY,
xLeftSpace + yDividerWidth / 2 + pillarMarginY + xSpaceVertical * 5, xScaleTopY, xLeftSpace + xSpaceVertical * 5 + yDividerWidth / 2 + pillarMarginY, xScaleBottomY,
xLeftSpace + yDividerWidth / 2 + pillarMarginY + xSpaceVertical * 6, xScaleTopY, xLeftSpace + xSpaceVertical * 6 + yDividerWidth / 2 + pillarMarginY, xScaleBottomY,
xLeftSpace + yDividerWidth / 2 + pillarMarginY + xSpaceVertical * 7, xScaleTopY, xLeftSpace + xSpaceVertical * 7 + yDividerWidth / 2 + pillarMarginY, xScaleBottomY
};
initPaintColor(xDividerColor);
canvas.drawLines(xptsx, mPaint);
//绘制X轴刻度下面的文字
initPaintColor(lineColor);
mPaint.setTextSize(xyFontSize);
int[] xs = {1000, 2000, 3000, 4000, 5000, 6000, 7000};
Rect xNumberBound;
for (int i = 0; i < xs.length; i++) {
String number = xs[i] + "";
xNumberBound = new Rect();
mPaint.getTextBounds(number, 0, number.length(), xNumberBound);
canvas.drawText(
number,
xLeftSpace + yDividerWidth + xSpaceVertical * (i + 1),
getHeight() - yBottomSpace + xNumberBound.height() + txtXSpace,
mPaint
);
}
/**
* 绘制六组数据的柱形图
*/
//获取X轴的宽度
float fullWidth = getWidth() - xLeftSpace - xRightSpace - yDividerWidth / 2 - pillarMarginY;
for (int x = 0; x < dataList.size(); x++) {
//柱子Y轴上坐标:view高度 - Y轴距离view下边的距离 - Y轴刻度高度*(x+1) - 柱子高度一半
float yTop = getHeight() - yBottomSpace - spaceVertical * (x + 1) - pillarHeight / 2;
//柱子Y轴下坐标:view高度 - Y轴距离view下边的距离 - Y轴刻度高度*(x+1) + 柱子高度一半
float yBottom = getHeight() - yBottomSpace - spaceVertical * (x + 1) + pillarHeight / 2;
float xRight = xLeftSpace + yDividerWidth / 2 + pillarMarginY + fullWidth * (dataList.get(x) / FULL_AMOUNT);
//设置渐变背景
LinearGradient lg = new LinearGradient(xLeftSpace + xDividerHeight, yTop, xRight, yBottom, Color.parseColor("#45b0ff"), Color.parseColor("#5dcaa9"), Shader.TileMode.MIRROR);
//Shader就是着色器
mPaint.setShader(lg);
//绘制圆角矩形
canvas.drawRoundRect(
new RectF(
xLeftSpace + yDividerWidth / 2,
yTop,
xRight,
yBottom
),
15,
15,
mPaint
);
//最后将画笔去除掉Shader
mPaint.setShader(null);
initPaintColor(dataFontColor);
mPaint.setTextAlign(Paint.Align.LEFT);
String number = dataList.get(x) + "人";
Rect numRect = new Rect();
mPaint.getTextBounds(number, 0, number.length(), numRect);
canvas.drawText(number, xRight + xDividerHeight, getHeight() - yBottomSpace - spaceVertical * (x + 1) + numRect.height() / 2, mPaint
);
}
//按着Y轴刻度右边绘制一个矩形,遮挡圆角矩形左边的圆角
mPaint.setColor(Color.parseColor("#fff9ef"));
canvas.drawRect(
new RectF(
xLeftSpace + yDividerWidth / 2,
0,
xLeftSpace + yDividerWidth / 2 + pillarMarginY,
getHeight() - yBottomSpace - 10
),
mPaint
);
}
private void initPaintColor(int color) {
mPaint.setColor(color);
}
public void setData(List<Integer> data) {
dataList.clear();
dataList.addAll(data);
invalidate();
}
}
设置自定义属性需要在styles.xml中声明:
<declare-styleable name="hpillar">
<!--XY轴颜色-->
<attr name="line_color_h" format="color"/>
<!--柱子右边文字颜色-->
<attr name="data_font_color_h" format="color" />
<!--X轴距离view左边和右边的距离-->
<attr name="x_left_space_h" format="dimension" />
<attr name="x_right_space_h" format="dimension" />
<!--Y轴距离view上边和下边的距离-->
<attr name="y_bottom_space_h" format="dimension" />
<attr name="y_top_space_h" format="dimension" />
<!--X轴刻度的高,Y轴刻度的宽-->
<attr name="x_divider_height" format="dimension" />
<attr name="y_divider_width" format="dimension" />
<!--XY轴刻度颜色-->
<attr name="x_divider_color" format="color" />
<attr name="y_divider_color" format="color" />
<!--刻度文本字体大小-->
<attr name="xy_font_size_h" format="dimension" />
<!--轴下面文本离X轴距离-->
<attr name="txt_x_space" format="dimension" />
<!--Y轴左边文本离Y轴距离-->
<attr name="txt_y_space" format="dimension" />
<!--每个柱子的高度-->
<attr name="pillar_height" format="dimension"/>
</declare-styleable>
这些属性都有默认初始值,如需修改可以再布局文件中设置赋值:
<com.example.zheng.hpillarview.view.HPillarView
android:id="@+id/pillar_h"
android:layout_width="match_parent"
android:layout_height="200dp"
android:background="#fff9ef"
app:line_color_h="#4eb0f1"
app:xy_font_size_h="10sp"
app:data_font_color_h="#5cc9aa"
app:y_top_space_h="20dp"
app:x_divider_color="#f8cac2"/>
最后在页面中使用就非常简单了(一步搞定):
//初始化数据
chartList.add(3999);
chartList.add(6001);
chartList.add(1888);
chartList.add(6666);
chartList.add(4999);
chartList.add(2999);
hPillarView= (HPillarView) findViewById(R.id.pillar_h);
//设置数据(一步搞定)
hPillarView.setData(chartList);