Android柱形图 绘制

一直在维护www.halloandroid.com我的个人博客网站,发现漏了一篇没发在上面,补回来。

上次写了一篇有关自定义折线图的文章,今天写一篇关于柱形图的文章,因为看了网上一位阿拉丁神灯大神的文章,所以自己也想动手写一个,熟练和学习一下自定义View。话不多说,开始撸!

整体的思路:

画柱状图 –>画竖线 –>画顶部横线 –>画文字

自定义View四步骤走起;

还是我们自定View的那几个步骤:

  • 1、自定义View的属性
  • 2、在View的构造方法中获得我们自定义的属性
  • [ 3、重写onMesure ]
  • [ 4、重写onLayout ]
  • 5、重写onDraw

一,在attrs里面进行声明

 <declare-styleable name="HistogramView">
        <attr name="cylinderColor" format="color|reference"/><!--柱体颜色-->
        <attr name="axesColor" format="color|reference"/><!--坐标线颜色-->
        <attr name="textColor"/><!--文字颜色-->
        <attr name="cylinderWith" format="integer"/><!--主题宽度-->
        <attr name="maxSize" format="float"/><!--最大值-->
        <attr name="textSize"/><!--文字字体大小-->
    </declare-styleable>

二、在View的构造方法中获得我们自定义的属性

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

        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.HistogramView);
        maxSize = a.getFloat(R.styleable.HistogramView_maxSize, 100f);
        cylinderWidth = a.getInt(R.styleable.HistogramView_cylinderWith, 50);
        textSize = a.getDimensionPixelSize(R.styleable.RoundProgressBar_textSize, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
                12, getResources().getDisplayMetrics()));
        cylinderColor = a.getColor(R.styleable.HistogramView_cylinderColor, 0xff40E0D0);
        axesColor = a.getColor(R.styleable.HistogramView_axesColor, 0xffcdcdcd);
        textColor = a.getColor(R.styleable.HistogramView_textColor, 0xffababab);
        a.recycle();
        initColorPaint();
    }
        private void initColorPaint() {
        //设开始X坐标为0
        startX = 0;
        //设开始Y坐标为50
        startY = 50;
        //初始化柱状图画笔
        mBarPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mBarPaint.setColor(cylinderColor);
        mBarPaint.setStyle(Paint.Style.FILL);
        //初始化线的画笔
        mLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mLinePaint.setStyle(Paint.Style.FILL);
        mLinePaint.setColor(axesColor);
        mLinePaint.setStrokeWidth(2);

        mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mTextPaint.setTextSize(textSize);
        mTextPaint.setStrokeWidth(1);
        mTextPaint.setColor(textColor);
    }

三、重写onMeasure,处理一下wrapcontent问题

 @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.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST) {//都是wrapcontent
            setMeasuredDimension((int) maxSize, startY + 10 + histogramrNum * (cylinderWidth + 2 * 10));
        } else if (widthSpecMode == MeasureSpec.AT_MOST) {//如果宽度为wrapcontent  高度为数值的时候
            setMeasuredDimension((int) maxSize, heightSpecSize);// 宽度设置为maxSize
        } else if (heightSpecMode == MeasureSpec.AT_MOST) { //如果宽度为数值的时候,高度为wrapcontent
            setMeasuredDimension(widthSpecSize, startY + 10 + histogramrNum * (cylinderWidth + 2 * 10)); //高度为每条柱状图的宽度加上间距再乘以柱状图条数再加上开始Y值后得到的值
        }
    }

四、重写onLayout,在Ondraw之前获取对应的宽高

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);

        measuredWidth = getMeasuredWidth();//获得测量后的宽度

        measuredHeight = getMeasuredHeight();//获得测量后的高度

        stopX = measuredWidth - cylinderWidth; //计算结束X的值

        deltaX = (stopX - (startX + 7 * cylinderWidth / 5)) / verticalLineNum; //计算每条竖线之间的间距

        deltaY = (measuredHeight - startY - cylinderWidth * histogramrNum) / histogramrNum; //计算每条柱状图之间的间距

        numPerUnit = maxSize / verticalLineNum; //计算出每条竖线所代表的数值

        currentHorizentalLineProgress = stopX;//初始化最上面横线的初始进度
    }

五、进入最重要的正题了,重写onDraw,糖炒栗子啦。

首先画柱状图

画柱状图的方法就是使用canvas.drawRect(float left, float top, float right, float bottom, Paint paint)

 /**
         * 画柱状图
         */
        for (int i = 0; i < histogramrNum; i++) {//for循环画出每一个柱状图
            if (currentBarProgress[i] < (numList.get(i) / maxSize) * stopX) {
            //这个for循环是为了一点点的画出增加感,就和progress差不多
                currentBarProgress[i] += 5;
                postInvalidateDelayed(10);
            }
            canvas.drawText(nameList.get(i), startX, startY + deltaY + i * (deltaY + cylinderWidth) + 3 * cylinderWidth / 4, mTextPaint);

            canvas.drawRect(startX + 7 * cylinderWidth / 5, startY + deltaY + i * (deltaY + cylinderWidth), currentBarProgress[i], startY + deltaY + i * (deltaY + cylinderWidth) + cylinderWidth, mBarPaint);
        }

接着画竖线和文字

有了上面画柱状图的思路,画竖线就非常容易想了,和画柱状图是一个思路,也是Activity中传入需要画几条竖线,然后在onDraw方法里分别去计算他们的当前进度值,然后再分别去画。

 /**
         * 画竖线
         */
        for(int i=0 ; i<verticalLineNum ; i++){
            if (currentVerticalLineProgress< measuredHeight) {
                currentVerticalLineProgress+=3;
                postInvalidateDelayed(10);
            }
            canvas.drawLine((startX+7*barWidth/5)+(i+1)*deltaX, startY, (startX+7*barWidth/5)+(i+1)*deltaX, currentVerticalLineProgress, mLinePaint);
            //画文字
            canvas.drawText(numPerUnit*(i+1)+unit, (startX+7*barWidth/5)+(i+1)*deltaX-barWidth, startY-barWidth/5, mTextPaint);
        }

最后画顶部横线

/**
         * 画最上面的横线
         */
        if (currentHorizentalLineProgress>startX+7*barWidth/5) {
            currentHorizentalLineProgress-=10;
            postInvalidateDelayed(10);
        }
        canvas.drawLine(stopX, startY, currentHorizentalLineProgress, startY, mLinePaint);
    }

使用方法:

布局文件中:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_histogram"
    android:layout_width="match_parent"
    android:layout_height="match_parent"

    tools:context="com.shanlovana.rcimageview.activities.HistogramActivity">

    <TextView
        android:id="@+id/textview"
        android:layout_width="match_parent"
        android:layout_height="30dp"
        android:text="我公司盈利情况"
        android:textColor="@color/colorAccent"
        android:textSize="24sp"
        />

    <com.shanlovana.rcimageview.views.HistogramView
        android:id="@+id/histogram_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/textview"
        android:layout_marginTop="10dp"
        app:axesColor="@color/colorAccent"
        app:cylinderWith="40"
        app:textColor="@color/colorPrimary"
        app:textSize="20sp"
        app:maxSize="10"
        app:cylinderColor="#000000"
        />
</RelativeLayout>

Activity中:

public class HistogramActivity extends AppCompatActivity {
    HistogramView mHistogramView;

    private ArrayList<Float> numList;
    private ArrayList<String> nameList;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_histogram);
        mHistogramView = (HistogramView) findViewById(R.id.histogram_view);
        // mPercentageBar= (PercentageBar) findViewById(R.id.percentageBar_view);
        numList = new ArrayList<Float>();
        nameList = new ArrayList<String>();
        numList.add(7.0f);
        numList.add(3.0f);
        numList.add(4.0f);
        numList.add(7.0f);
        numList.add(4.0f);
        numList.add(8.0f);
        numList.add(9.0f);
        numList.add(9.5f);
        nameList.add("一");
        nameList.add("二");
        nameList.add("三");
        nameList.add("四");
        nameList.add("五");
        nameList.add("六");
        nameList.add("七");
        nameList.add("八");
        mHistogramView.setUnit("万");
        mHistogramView.setNumListForEvery(numList);
        mHistogramView.settNameListForEvery(nameList);

    }
}

效果图如下:

这个demo希望给你思路,还有已知的bug,文字的大小会影响柱状图的显示,会出现文字被柱状图压住,最近会修改。

源码因为这里github传不上去,回去传,到时候再发链接出来。

时间: 2024-08-15 23:52:32

Android柱形图 绘制的相关文章

Android UI 绘制过程浅析(五)自定义View

前言 这已经是Android UI 绘制过程浅析系列文章的第五篇了,不出意外的话也是最后一篇.再次声明一下,这一系列文章,是我在拜读了csdn大牛郭霖的博客文章<带你一步步深入了解View>后进行的实践. 前面依次了解了inflate的过程,以及绘制View的三个步骤:measure, layout, draw.这一次来亲身实践一下,通过自定义View来加深对这几个过程的理解. 自定义View的分类 根据实现方式,自定义View可以分为以下3种类型. 自绘控件.View的绘制代码(onDraw

Android 图表绘制 achartengine 示例解析

作者 : 韩曙亮 转载请注明出处 : http://blog.csdn.net/shulianghan/article/details/38420197 一. AChartEngine 简介 1. 项目地址 AChartEngine 简介 : AChartEngine 是 Android 平台的图表开发库, 能绘制 折线图, 饼图, 气泡图, 柱状图, 散点图, 面积图等统计图表; 最新版本 : 1.1.0 版本; AChartEngine 地址 : https://code.google.co

【转】Android Shape绘制虚线在手机端查看是实线的问题

Android share绘制虚线在手机上显示实线问题 原文博客链接:http://wv1124.iteye.com/blog/2187204 博客分类: Android 可以说这是一个Bug, 据说在4.0以上机器会出现,我测试是android 4.4.2 Xml代码   <?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android

android精确绘制文字位置的方法

android 中使用Canvas的drawText绘制文本的位置,是基于基线的.如下图: 其中字母Q的小尾巴在横线下面了. 怎么样找准字母的中心位置呢? 先看下面的例子:(右边的数字,表示字体的 left, top, right, bottom) 这里面的关键是Paint.getTextBound. getTextBound会填充一个Rect,这个Rect表示的就是一个字的left, top, right, bottom.注意到left和top并不是从0,0开始的. left和right应该是

简单研究Android View绘制一

2015-07-27 16:52:58 一.如何通过继承ViewGroup来实现自定义View?首先得搞清楚Android时如何绘制View的,参考Android官方文档:How Android Draws Views 以下翻译摘自:http://blog.csdn.net/linghu_java/article/details/23882681,这也是一片好文章,推荐大家看看- When an Activity receives focus, it will be requested to d

Android Canvas绘制

public class DrawView extends View { public DrawView(Context context) {  super(context); } @Override protected void onDraw(Canvas canvas) {  super.onDraw(canvas);  /*   * 方法 说明 drawRect 绘制矩形 drawCircle 绘制圆形 drawOval 绘制椭圆 drawPath 绘制任意多边形   * drawLine

Android 饼图绘制

private BlurMaskFilter PaintBGBlur; private int ScrHeight; private int ScrWidth; private Paint[] arrPaintArc; private Paint PaintText = null; private Path pathArc = null; private RectF arcRF0 = null; private int[] colors = new int[] { Color.RED, Colo

简单研究Android View绘制三 布局过程

2015-07-28 17:29:19 这一篇主要看看布局过程 一.布局过程肯定要不可避免的涉及到layout()和onLayout()方法,这两个方法都是定义在View.java中,源码如下: 1 /** 2 * Assign a size and position to a view and all of its 3 * descendants 4 * 5 * <p>This is the second phase of the layout mechanism. 6 * (The fir

Android如何绘制视图,解释了为何onMeasure有时要调用多次(转)

当Activity获取焦点的时候,它就需要绘制布局.Android框架会处理绘制过程,但这个Activity必须提供它布局树的根节点. 绘制过程是从布局的根节点开始的.这个过程需要测量和绘制布局树.绘制过程是通过遍历树和渲染每个与绘制区域相交的视图来处理的.接下来,ViewGroup职责就是请求它的每个子视图都会绘制(使用draw()方法),同时View的职责就是绘制自身.由于这个树都是依序遍历,这就意味着这个父视图会在子视图之前绘制,并且会按照出现在树中的顺序绘制它们的兄弟姐妹. 框架不会绘制