Android 自定义控件--横向柱状图

来源:互联网 发布:开淘宝店页面图片 编辑:程序博客网 时间:2024/04/29 05:03

**摘要**


本文主要内容是:在Android系统下自定义图形。效果如下图:自定义横向柱状图

**思路**


如上图所示,我们应该把问题简单化,看上去图片是有规律的,类似一个列表,那么我们就当一列是一个Item吧。item
再把一个Item分解: 分成文字和图形部分。

文字部分

文字部分就是对当个柱状图的说明,我们暂且叫他“itemName”吧。我们看到了itemName是右对齐的,那么我们就得获取所有数据的itemName,然后看谁最长,然后计算这段文字有多长,此时,我们就知道了itemName所占的长度了(当个文字的长度 * 文字个数),获取当个文字的长宽方法如下:

/**     * 获取单个字符的高和宽     */    private int[] getTextWH() {        int[] wh = new int[2];        // 一个矩形        Rect rect = new Rect();        String text = "我";        Paint paint = new Paint();        // 设置文字大小        paint.setTextSize(dip2px(getContext(), 16));        paint.getTextBounds(text, 0, text.length(), rect);        wh[0] = rect.width();        wh[1] = rect.height();        return wh;    }

那么我们就得设置文字画笔是右对齐的。

 /**     *绘制文字说明  右对齐     * @param canvas     * @param text     */    private void drawText(Canvas canvas, String text) {        int x = getWidth();        int y = getHeight();        Paint textPaint = new Paint();        textPaint.setColor(getResources().getColor(R.color.text_line_chart));        textPaint.setTextSize(dip2px(getContext(), 16));        // 设置文字右对齐        textPaint.setTextAlign(Paint.Align.RIGHT);        float tX = (x - getFontlength(textPaint, text)) / 2;        float tY = (y - getFontHeight(textPaint)) / 2 + getFontLeading(textPaint);        // 注意第二个参数,右对齐,文字是从右开始写的,那么  x 就是对齐处的X坐标        canvas.drawText(text, mTextW, tY, textPaint);    }

图形部分

图形部分主要是绘制矩形,那么怎么绘制呢?很简单的嘛: 先获取屏幕宽度SW,然后获取数值的大小DV, SW / DV 就得到了一个单位DV所占的位置了,然后直接canvas.drawRect …..但是,但是 我们是画一个列表的,要对其的,我们要的是数据有参考、对比性的。是吧???那么我们就得算出所有 DV的最大值。然后计算一个DV所占的位置,最后才能绘制。 记得,我们的图形是在 ItemName右边的。那么我们图形的X坐标就是 ItemName所占的宽度开始的。

/**绘制图形     * @param canvas     */    private void drawLine(Canvas canvas) {        double chart_length = (getWidth() - mTextW) / (double) mMaxV;        int start_complete_left = mTextW + 10,                start_complete_top = 4,                start_complete_right = start_complete_left + (int) (chart_length * mData.getRecover_complete())- dip2px(getContext(), 6),                start_uncomplete_right = start_complete_left + (int) (chart_length * (mData.getRecover_complete() + mData.getRecover_uncomplete()))- dip2px(getContext(), 6);        Log.e("TAG", start_complete_left + "..." + start_complete_top + ",,," + start_complete_right + "ds"                + start_uncomplete_right);        this.arcPaint = new Paint();        this.arcPaint.setColor(getResources().getColor(R.color.line_chart_uncomplete));        this.arcPaint.setAntiAlias(true);// 去除锯齿        // 绘制未完成的,        canvas.drawRect(start_complete_left, start_complete_top, start_uncomplete_right, mChartH, arcPaint);        // 绘制完成的        this.arcPaint.setColor(getResources().getColor(R.color.line_chart_complete));        canvas.drawRect(start_complete_left, start_complete_top, start_complete_right, mChartH, arcPaint);    }

文字加图形

画好一个,那么我们就得把 一个个item 放进列表里面去, 那么就得重写一个LinearLayout, 然后add()上去。 为什么不用ListView或者RecycleView?你一定在想,因为itemName的长度不确定,我们又得右对齐,所以只能用LinearLayout了

public LinChartLayout(Context context) {        super(context);        this.setOrientation(VERTICAL);        setView();    }    public void setView() {        if (mData != null && !mData.isEmpty()) {            int text_max_length = 0;            int value_max = 0;            for (LineChartData data : mData) {                // 获取最长文字的个数                if (text_max_length <= data.getName().length()) {                    text_max_length = data.getName().length();                }                // 获取数据值的大小                int total = data.getRecover_complete() + data.getRecover_uncomplete();                // 获取数据的值                if (value_max <= total) {                    value_max = total;                }            }            int[] wh = getTextWH();            // 文字区域的宽            int textAreW = text_max_length * wh[0] + dip2px(getContext(), 10);            // 图形区域的宽            int chartAreW = scrW - textAreW - 10;            LinearLayout.LayoutParams layoutParams = new LayoutParams(scrW - dip2px(getContext(), 10),dip2px(getContext(), 32));            // 设置居中            layoutParams.gravity = Gravity.CENTER;            // 设置Margin            layoutParams.topMargin = dip2px(getContext(), 4);            layoutParams.bottomMargin = dip2px(getContext(), 4);            // 遍历添加LinChartView            for (LineChartData i : mData) {                LinChartView chartView = new LinChartView(getContext());                chartView.setData(textAreW, chartAreW,value_max, i);                this.addView(chartView, layoutParams);            }        }    }

**全部代码**


下面是一些参考的代码:

LinChartLayout.java

/** * @author bishiqiangan@yeah.net * 重写一个LinearLayout, 遍历添加LinChartView */public class LinChartLayout extends LinearLayout {    /**     *  列表的数据源     */    private List<LineChartData> mData;    /**     * 屏幕的宽     *     */    private int scrW;    public LinChartLayout(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);    }    public LinChartLayout(Context context, AttributeSet attrs) {        super(context, attrs);    }    public LinChartLayout(Context context) {        super(context);        this.setOrientation(VERTICAL);        setView();    }    public void setView() {        if (mData != null && !mData.isEmpty()) {            int text_max_length = 0;            int value_max = 0;            for (LineChartData data : mData) {                // 获取最长文字的个数                if (text_max_length <= data.getName().length()) {                    text_max_length = data.getName().length();                }                // 获取数据值的大小                int total = data.getRecover_complete() + data.getRecover_uncomplete();                // 获取数据的值                if (value_max <= total) {                    value_max = total;                }            }            int[] wh = getTextWH();            // 文字区域的宽            int textAreW = text_max_length * wh[0] + dip2px(getContext(), 10);            // 图形区域的宽            int chartAreW = scrW - textAreW - 10;            LinearLayout.LayoutParams layoutParams = new LayoutParams(scrW - dip2px(getContext(), 10),dip2px(getContext(), 32));            // 设置居中            layoutParams.gravity = Gravity.CENTER;            // 设置Margin            layoutParams.topMargin = dip2px(getContext(), 4);            layoutParams.bottomMargin = dip2px(getContext(), 4);            // 遍历添加LinChartView            for (LineChartData i : mData) {                LinChartView chartView = new LinChartView(getContext());                chartView.setData(textAreW, chartAreW,value_max, i);                this.addView(chartView, layoutParams);            }        }    }    /**     * 获取单个字符的高和宽     */    private int[] getTextWH() {        int[] wh = new int[2];        // 一个矩形        Rect rect = new Rect();        String text = "我";        Paint paint = new Paint();        // 设置文字大小        paint.setTextSize(dip2px(getContext(), 16));        paint.getTextBounds(text, 0, text.length(), rect);        wh[0] = rect.width();        wh[1] = rect.height();        return wh;    }    public void setData(List<LineChartData> d,int scrw) {        this.mData = d;        this.scrW = scrw;    }    /**     * 根据手机的分辨率从 dp 的单位 转成为 px(像素)     */    public static int dip2px(Context context, float dpValue) {     final float scale = context.getResources().getDisplayMetrics().density;     return (int) (dpValue * scale + 0.5f);    }    /**     * 根据手机的分辨率从 px(像素) 的单位 转成为 dp     */    public static int px2dip(Context context, float pxValue) {     final float scale = context.getResources().getDisplayMetrics().density;     return (int) (pxValue / scale + 0.5f);    }

LinChartView.java

/** * @author bishiqiangan@yeah.net * 重写一个View , 绘制一个横向柱状图 */public class LinChartView extends View {    private LineChartData mData;    private int mTextW, mChartH, mMaxV;    private Paint arcPaint = null;    public LinChartView(Context context) {        super(context);    }    public LinChartView(Context context, AttributeSet attrs) {        super(context, attrs);    }    public LinChartView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);    }    @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        if (mData == null) {            return;        }        // 画文字        drawText(canvas, mData.getName());        //画图形        drawLine(canvas);    }    /**绘制图形     * @param canvas     */    private void drawLine(Canvas canvas) {        double chart_length = (getWidth() - mTextW) / (double) mMaxV;        int start_complete_left = mTextW + 10,                start_complete_top = 4,                start_complete_right = start_complete_left + (int) (chart_length * mData.getRecover_complete())- dip2px(getContext(), 6),                start_uncomplete_right = start_complete_left + (int) (chart_length * (mData.getRecover_complete() + mData.getRecover_uncomplete()))- dip2px(getContext(), 6);        Log.e("TAG", start_complete_left + "..." + start_complete_top + ",,," + start_complete_right + "ds"                + start_uncomplete_right);        this.arcPaint = new Paint();        this.arcPaint.setColor(getResources().getColor(R.color.line_chart_uncomplete));        this.arcPaint.setAntiAlias(true);// 去除锯齿        // 绘制未完成的,        canvas.drawRect(start_complete_left, start_complete_top, start_uncomplete_right, mChartH, arcPaint);        // 绘制完成的        this.arcPaint.setColor(getResources().getColor(R.color.line_chart_complete));        canvas.drawRect(start_complete_left, start_complete_top, start_complete_right, mChartH, arcPaint);    }    /**     *绘制文字说明  右对齐     * @param canvas     * @param text     */    private void drawText(Canvas canvas, String text) {        int x = getWidth();        int y = getHeight();        Paint textPaint = new Paint();        textPaint.setColor(getResources().getColor(R.color.text_line_chart));        textPaint.setTextSize(dip2px(getContext(), 16));        // 设置文字右对齐        textPaint.setTextAlign(Paint.Align.RIGHT);        float tX = (x - getFontlength(textPaint, text)) / 2;        float tY = (y - getFontHeight(textPaint)) / 2 + getFontLeading(textPaint);        // 注意第二个参数,右对齐,文字是从右开始写的,那么  x 就是对齐处的X坐标        canvas.drawText(text, mTextW, tY, textPaint);    }    /**     * @return 返回指定笔和指定字符串的长度     */    public static float getFontlength(Paint paint, String str) {        return paint.measureText(str);    }    /**     * @return 返回指定笔的文字高度     */    public static float getFontHeight(Paint paint) {        Paint.FontMetrics fm = paint.getFontMetrics();        return fm.descent - fm.ascent;    }    /**     * @return 返回指定笔离文字顶部的基准距离     */    public static float getFontLeading(Paint paint) {        Paint.FontMetrics fm = paint.getFontMetrics();        return fm.leading - fm.ascent;    }    public void setData(int textW, int chartW, int max_valur, LineChartData data) {        Log.e("TAG", max_valur + "...max");        this.mTextW = textW;        this.mChartH = chartW;        this.mMaxV = max_valur;        this.mData = data;        this.postInvalidate();    }    /**     * 根据手机的分辨率从 dp 的单位 转成为 px(像素)     */    public static int dip2px(Context context, float dpValue) {        final float scale = context.getResources().getDisplayMetrics().density;        return (int) (dpValue * scale + 0.5f);    }    /**     * 根据手机的分辨率从 px(像素) 的单位 转成为 dp     */    public static int px2dip(Context context, float pxValue) {        final float scale = context.getResources().getDisplayMetrics().density;        return (int) (pxValue / scale + 0.5f);    }}

LineChartData.java

/** * @author bishiqiangan@yeah.net * 数据模型类 */public class LineChartData {    private int recover_complete;    private String name;    private int recover_uncomplete;    public int getRecover_complete() {        return recover_complete;    }    public void setRecover_complete(int recover_complete) {        this.recover_complete = recover_complete;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public int getRecover_uncomplete() {        return recover_uncomplete;    }    public void setRecover_uncomplete(int recover_uncomplete) {        this.recover_uncomplete = recover_uncomplete;    }}

调用的方法:
==android:orientation=”vertical”== 记得添加

  <com.example.testandroid.LinChartLayout                android:id="@+id/linechart"                android:layout_width="match_parent"                android:layout_height="wrap_content"                android:layout_margin="6dip"                ==android:orientation="vertical"==                />

Android开发技术QQ群:64026923, 欢迎你的加入

3 0
原创粉丝点击