android:自定义view--横向柱形图

此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);

 

 

 

 

 

点击打开链接下载源码
 

 

  

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值