安卓自定义控件之不可滑动的折线图

本文介绍了一个自定义的不可滑动折线图组件的实现细节,包括如何通过设置坐标轴名称、最大值、平均值等参数来动态更新视图。该组件基于Android平台开发,利用了Canvas绘图API来实现折线图的绘制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

代码在注释中,与柱状图的实现类似

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值