美文网首页
Android简单自定义图表

Android简单自定义图表

作者: XINHAO_HAN | 来源:发表于2018-03-16 10:38 被阅读0次

作者:XINHAO_HAN

更新:2019/06/26

可以说大部分网上下载的,库都比较大,而且作者考虑的都比较全,所以有些东西并不是那么方便,

这块有一个需求就是在图表上画一个最高最低线,我原先用的Mp库,感觉不是很方便,而且项目还是有点庞大

所以自己就写了一个,供大家参考

数据使用

效果图:


1521109504951mzline.gif

版效果图:

样式 1521194439145mzasdf.gif

思路:

    根据需求有个竖状表,每个表格的高度是 [View高度/(最大值 - 最小值)] = 每个等份的高度,然后再乘以传入进来的数值

    滑动处理我采用了更改坐标制度

    Demo写的不是很好,只供一个参考,0.0

XML

  <com.example.xhcharlib.XHLineChar

       android:layout_width="match_parent"
       android:layout_height="300dp"
       android:id="@+id/xhchar"
       ></com.example.xhcharlib.XHLineChar>

代码使用

 xhLineChar.setMaxTable(200.5f);//表的最高状态
        xhLineChar.setMinTable(0f);//表的最低状态

        ArrayList<XHData> xhData = new ArrayList<>();

        /*xhData.add(new XHData(20,0));
        xhData.add(new XHData(50,80));
        xhData.add(new XHData(60,30));
        xhData.add(new XHData(80,26));
        xhData.add(new XHData(17,26));
        xhData.add(new XHData(80,65));
        xhData.add(new XHData(26,74));
        xhData.add(new XHData(20,86));*/

                                                
        xhData.add(new XHData(true,5.5f,"2018/3",Color.parseColor("#dd7e6b")));
        //标记,表的数据/日期/点的颜色(默认#adadad)
        xhData.add(new XHData(true,5.6f,"2018/3",Color.parseColor("#00ffff")));
        xhData.add(new XHData(true,80.5f,"2018/3",Color.parseColor("#6aa84f")));
        xhData.add(new XHData(true,70.5f,"2018/3",Color.parseColor("#cc0033")));
        xhData.add(new XHData(true,200.5f,"2018/3",Color.parseColor("#66ff33")));
        xhData.add(new XHData(true,180.5f,"2018/3",Color.parseColor("#6699ff")));
        xhData.add(new XHData(true,160.5f,"2018/3",Color.parseColor("#3366ff")));
        xhData.add(new XHData(true,140.5f,"2018/3",Color.parseColor("#00ffff")));
        xhData.add(new XHData(true,190.5f,"2018/3",Color.parseColor("#006600")));
        xhData.add(new XHData(true,10.5f,"2018/3",Color.parseColor("#FF83FA")));
        xhData.add(new XHData(true,30.5f,"2018/3",Color.parseColor("#FF3030")));
        xhData.add(new XHData(true,12.5f,"2018/3",Color.parseColor("#FF7F50")));
        xhData.add(new XHData(true,5.5f,"2018/3",Color.parseColor("#EE30A7")));
        xhData.add(new XHData(true,5.6f,"2018/3"));
        xhData.add(new XHData(true,80.5f,"2018/3"));
        xhData.add(new XHData(true,70.5f,"2018/3"));
        xhData.add(new XHData(true,200.5f,"2018/3"));
        xhData.add(new XHData(true,180.5f,"2018/3"));
        xhData.add(new XHData(true,160.5f,"2018/3"));
        xhData.add(new XHData(true,140.5f,"2018/3/15"));
        xhData.add(new XHData(true,190.5f,"2018/3/15"));
        xhData.add(new XHData(true,10.5f,"2018/3/15"));
        xhData.add(new XHData(true,30.5f,"2018/3/15"));
        xhData.add(new XHData(true,12.5f,"2018/3/15"));
        xhData.add(new XHData(true,5.5f,"2018/3/15"));
        xhData.add(new XHData(true,5.6f,"2018/3/15"));
        xhData.add(new XHData(true,80.5f,"2018/3/15"));
        xhData.add(new XHData(true,70.5f,"2018/3/15"));
        xhData.add(new XHData(true,200.5f,"2018/3/15"));
        xhData.add(new XHData(true,180.5f,"2018/3/15"));
        xhData.add(new XHData(true,160.5f,"2018/3/15"));
        xhData.add(new XHData(true,140.5f,"2018/3/15"));
        xhData.add(new XHData(true,190.5f,"2018/3/15"));
        xhData.add(new XHData(true,10.5f,"2018/3/15"));
        xhData.add(new XHData(true,30.5f,"2018/3/15"));
        xhData.add(new XHData(true,12.5f,"2018/3/15"));
        xhData.add(new XHData(true,5.5f,"2018/3/15"));
        xhData.add(new XHData(true,5.6f,"2018/3/15"));
        xhData.add(new XHData(true,80.5f,"2018/3/15"));
        xhData.add(new XHData(true,70.5f,"2018/3/15"));
        xhData.add(new XHData(true,200.5f,"2018/3/15"));
        xhData.add(new XHData(true,180.5f,"2018/3/15"));
        xhData.add(new XHData(true,160.5f,"2018/3/15"));
        xhData.add(new XHData(true,140.5f,"2018/3/15"));
        xhData.add(new XHData(true,190.5f,"2018/3/15"));
        xhData.add(new XHData(true,10.5f,"2018/3/15"));
        xhData.add(new XHData(true,30.5f,"2018/3/15"));
        xhData.add(new XHData(true,12.5f,"2018/3/15"));
        xhData.add(new XHData(true,5.5f,"2018/3/15"));
        xhData.add(new XHData(true,5.6f,"2018/3/15"));
        xhData.add(new XHData(true,80.5f,"2018/3/15"));
        xhData.add(new XHData(true,70.5f,"2018/3/15"));
        xhData.add(new XHData(true,200.5f,"2018/3/15"));
        xhData.add(new XHData(true,180.5f,"2018/3/15"));
        xhData.add(new XHData(true,160.5f,"2018/3/15"));
        xhData.add(new XHData(true,140.5f,"2018/3/15"));
        xhData.add(new XHData(true,190.5f,"2018/3/15"));
        xhData.add(new XHData(true,10.5f,"2018/3/15"));
        xhData.add(new XHData(true,30.5f,"2018/3/15"));
        xhData.add(new XHData(true,12.5f,"2018/3/15"));
        xhData.add(new XHData(true,5.5f,"2018/3/15"));
        xhData.add(new XHData(true,5.6f,"2018/3/15"));
        xhData.add(new XHData(true,80.5f,"2018/3/15"));
        xhData.add(new XHData(true,30.5f,"2018/3/15"));
        xhData.add(new XHData(true,12.5f,"2018/3/15"));
        xhData.add(new XHData(true,5.5f,"2018/3/15"));
        xhData.add(new XHData(true,5.6f,"2018/3/15"));
        xhData.add(new XHData(true,80.5f,"2018/3/15"));
        xhData.add(new XHData(true,70.5f,"2018/3/15"));
        xhData.add(new XHData(true,200.5f,"2018/3/15"));
        xhData.add(new XHData(true,180.5f,"2018/3/15"));
        xhData.add(new XHData(true,160.5f,"2018/3/15"));
        xhData.add(new XHData(true,140.5f,"2018/3/15"));
        xhData.add(new XHData(true,190.5f,"2018/3/15"));
        xhData.add(new XHData(true,10.5f,"2018/3/15"));
        xhData.add(new XHData(true,30.5f,"2018/3/15"));
        xhData.add(new XHData(true,12.5f,"2018/3/15"));
        xhData.add(new XHData(true,5.5f,"2018/3/15"));
        xhData.add(new XHData(true,5.6f,"2018/3/15"));
        xhData.add(new XHData(true,80.5f,"2018/3/15"));
        xhData.add(new XHData(true,70.5f,"2018/3/15"));
        xhData.add(new XHData(true,200.5f,"2018/3/15"));
        xhData.add(new XHData(true,180.5f,"2018/3/15"));
        xhData.add(new XHData(true,160.5f,"2018/3/15"));
        xhData.add(new XHData(true,140.5f,"2018/3/15"));
        xhData.add(new XHData(true,190.5f,"2018/3/15"));
        xhData.add(new XHData(true,10.5f,"2018/3/15"));
        xhData.add(new XHData(true,30.5f,"2018/3/15"));
        xhData.add(new XHData(true,12.5f,"2018/3/15"));
        xhData.add(new XHData(true,5.5f,"2018/3/15"));
        xhData.add(new XHData(true,5.6f,"2018/3/15"));
        xhData.add(new XHData(true,80.5f,"2018/3/15"));




        xhLineChar.setLineHOrLineW(30.3f,90.5f);
        xhLineChar.setDataArray(xhData);

这个只是简单实现了一下,效果并不是很好,如果有需求的哥们,可以自己改改style

更新(2019/06/26)作者重新写了一版,代码较为工整

更新:https://github.com/hanxinhao000/newXinhaoLine

//工作类介绍(部分)

//CharViewData 用户传递数据类 
//LineData,ScaleLineData          管理刻度线/边缘线
//PointData         老大哥NB了,管理全部关于坐标的实现
//RollingLine       管理滑动
//Up2DownLineData  高低线(目前BUG:设置如果个高于表刻度线会显示不正常)
//XScale             Y轴间隔
//CharDrawLineView  我只管画画
//CharLineView          我只管拿取数据,以及给CharDrawLineView  分配画的工作量

更新(2019/06/26)

代码片段

package erjinzhi.xinhao.xinhaolib.linedata;

import android.graphics.Color;
import android.graphics.Paint;

import java.util.ArrayList;
import java.util.List;

import erjinzhi.xinhao.xinhaolib.databean.CharViewData;
import erjinzhi.xinhao.xinhaolib.databean.LineCharBean;
import erjinzhi.xinhao.xinhaolib.databean.LineCharViewData;
import erjinzhi.xinhao.xinhaolib.databean.ScaleLineBoomStringBean;
import erjinzhi.xinhao.xinhaolib.databean.ScaleLineLifeStringBean;
import erjinzhi.xinhao.xinhaolib.databean.listener.DataNotifyDataSetChangedListener;
import erjinzhi.xinhao.xinhaolib.linedata.idata.IBaseData;
import erjinzhi.xinhao.xinhaolib.linedata.idata.IPointData;
import erjinzhi.xinhao.xinhaolib.linedata.idata.ImpPointData;
import erjinzhi.xinhao.xinhaolib.linedata.listener.IScaleLine;
import erjinzhi.xinhao.xinhaolib.linedata.listener.PointDataViewRefreshListener;
import erjinzhi.xinhao.xinhaolib.utils.UIUtils;

/**
 * 专门处理坐标的一个类
 */
public class PointData implements IBaseData, IPointData, DataNotifyDataSetChangedListener, ImpPointData, IScaleLine, ILineData {


    /**
     * 未处理之前的点数据
     */
    private List<LineCharBean> mList;

    /**
     * 通知视图刷新
     */
    private PointDataViewRefreshListener mPointDataViewRefreshListener;

    /**
     * 处理过后的数据
     */
    private List<LineCharViewData> mViewPointCoordinatesList;

    /**
     * View高度
     */
    private int mHeight;

    /**
     * 传入左边刻度线的值
     */
    private ArrayList<ScaleLineLifeStringBean> mScaleLineLifeStringBeans;
    /**
     * 获取刻度线底部的值
     */
    private ArrayList<ScaleLineBoomStringBean> scaleLineBoomStringBeans;

    /**
     * 刻度线个数
     */
    private int mScale = 0;

    /**
     * 自带画笔
     */

    private Paint mPaint;
    /**
     * 刻度线的长度
     */
    private int mScaleWidth;

    /**
     * 用户所传递过来的设置
     */
    private CharViewData mCharViewData;


    //平均值
    private float average = 0;
    private XScale mXScale;


    @Override
    public void setCharViewData(CharViewData mCharViewData) {
        this.mCharViewData = mCharViewData;
    }

    @Override
    public void setList(List<LineCharBean> mList) {

        this.mList = mList;
        mPaint = new Paint();
        mViewPointCoordinatesList = new ArrayList<>();
        //吧 Y轴的信息算出来
        mXScale = new XScale(mList.size());

        mPaint.setTextSize(20);


    }

    public float getAverage() {
        return average;
    }

    //设置View高度
    @Override
    public void setViewHeight(int mHeight) {
        this.mHeight = mHeight;

    }

    @Override
    public void setPointDataViewRefreshListener(PointDataViewRefreshListener mPointDataViewRefreshListener) {

        this.mPointDataViewRefreshListener = mPointDataViewRefreshListener;

    }

    //开始处理点数据
    @Override
    public void calculate() {

        //先初始化画笔
        initPaint();
        //获取当前最大数
        int max = maxiMumNumber();
        //获取当前最小数
        int mid = miniMumNumber();
        //计算当前平均值
        averageCalculate(max, mid);
        //平均点已计算,开始计算每个点所在的高度
        pointHeight(mid);
        //高度已计算完毕,开始计算宽度
        pointWidth();
        //计算左边Text的位置
        calculateLifeText(max, mid);
        //计算底部Text的位置
        calculateBoomText();
        //通知视图说数据全部已算好
        if (mPointDataViewRefreshListener != null) {
            mPointDataViewRefreshListener.refreshPointDataView();
        }

    }

    //设置刻度线个数
    @Override
    public void setScale(int mScale) {

        this.mScale = mScale;

    }

    //设置刻度线长度
    @Override
    public void setScaleWidth(int mScaleWidth) {

        this.mScaleWidth = mScaleWidth;

    }

    /**
     * 计算刻度线(左边)
     *
     * @param max
     * @param mid
     */
    private void calculateLifeText(int max, int mid) {
        //刻度线分为10个
        int temp = (mScaleWidth / NUMBER_OF_SCALE_LINES);

        if (mScaleLineLifeStringBeans == null) {
            mScaleLineLifeStringBeans = new ArrayList<>();
        } else {
            //清空刻度线
            mScaleLineLifeStringBeans.clear();
        }

        //平均刻度线
        int t = absoluteValue(max - mid) / (NUMBER_OF_SCALE_LINES);


        for (int i = 0; i <= NUMBER_OF_SCALE_LINES; i++) {


            ScaleLineLifeStringBean scaleLineLifeStringBean = new ScaleLineLifeStringBean();

            scaleLineLifeStringBean.setmX(SCALE);


            //获取最大点位置

            float maxIndex = getMaxIndex();

            //获取最小点位置
            float minIndex = getMinIndex();

            //再除以N份
            float temp_s = (maxIndex - minIndex) / (NUMBER_OF_SCALE_LINES);

            //获取底部位置
            int boom = mHeight;

            boom -= (BOTTOM_DISTANCE + POINT_INTERVAL);

            scaleLineLifeStringBean.setmY(boom + (temp_s * i));

            scaleLineLifeStringBean.setText((mid + (t * i)) + "");

            mScaleLineLifeStringBeans.add(scaleLineLifeStringBean);
        }


    }

    //计算刻度线(底部)
    @Override
    public void calculateBoomText() {

        //整理数据
        if (scaleLineBoomStringBeans == null) {
            scaleLineBoomStringBeans = new ArrayList<>();
        }


        //将字符串传入,并计算坐标
        for (int i = 0; i < mList.size(); i++) {
            ScaleLineBoomStringBean scaleLineBoomStringBean = new ScaleLineBoomStringBean();

            scaleLineBoomStringBean.setText(mList.get(i).getTextBoom());

            int temp = (mHeight - BOTTOM_DISTANCE + BOOM_TEXT_DISTANCE + 20);

            int temp_x = mXScale.getXScales().get(i) + LEFT_DISTANCE + POINT_INTERVAL;

            scaleLineBoomStringBean.setmX(temp_x);

            scaleLineBoomStringBean.setmY(temp);

            //计算线的坐标X startX

            int startX = temp_x;

            //计算线的坐标Y startY

            int startY = mHeight - BOTTOM_DISTANCE;

            //计算线的坐标X endX

            int endX = temp_x;

            //计算线的坐标Y endY

            int endY = mHeight - BOTTOM_DISTANCE - NUMBER_OF_SCALE_LINES_LINEG;


            scaleLineBoomStringBean.setLineStartX(startX);

            scaleLineBoomStringBean.setLineStartY(startY);

            scaleLineBoomStringBean.setLineEndX(endX);

            scaleLineBoomStringBean.setLineEndY(endY);

            scaleLineBoomStringBeans.add(scaleLineBoomStringBean);


        }


    }


    /**
     * 取得画笔
     */
    @Override
    public Paint getPaint() {
        return mPaint;
    }

    //初始化画笔
    private void initPaint() {

        mPaint.setColor(Color.parseColor("#35ADA7"));
        mPaint.setStrokeWidth(20);
        mPaint.setAntiAlias(true);
        mPaint.setTextSize(30);
    }

    //计算点的宽度
    private void pointWidth() {

        ArrayList<Integer> xScales = mXScale.getXScales();

        for (int i = 0; i < mViewPointCoordinatesList.size(); i++) {

            LineCharViewData lineCharViewData = mViewPointCoordinatesList.get(i);

            //与左边线对齐
            int temp = xScales.get(i) + LEFT_DISTANCE;
            //点之与线的padding
            temp += POINT_INTERVAL;
            lineCharViewData.setViewDataX(temp);


        }

    }


    //计算每个点所占用的高度
    private void pointHeight(int mid) {

        //因为我们用了padding所以要减去相对应的坐标
        int tempHeight = mHeight;

        tempHeight -= (BOTTOM_DISTANCE + TOP_DISTANCE);

        //使用for循环开始乘
        for (int i = 0; i < mList.size(); i++) {

            //计算之前的点
            int data = mList.get(i).getData();
            //开始计算每个点实质在View中所占用的高度,并存入
            LineCharViewData lineCharViewData = new LineCharViewData();

            float midTemp = mid * average;

            float temp = data * average;
            //减去最小值高度,始终使最小值在底部,不然可能会出现"占位"
            temp -= midTemp;
            //如果为0的话可能就没有了,所以如果是0的话默认在底部
            if (average == 0) {
                temp = tempHeight + TOP_DISTANCE;
            } else {
                //减反,如果不理解的哥们,可去除本行(注释)加以验证
                temp = tempHeight - temp;
                //减去顶部的距离
                temp = temp + TOP_DISTANCE;
                //减去padding的距离
                temp = temp - POINT_INTERVAL;
            }


            //设置点信息
            lineCharViewData.setViewDataY(temp);
            lineCharViewData.setData(data);
            mViewPointCoordinatesList.add(lineCharViewData);

        }

    }

    //获取最大点位置
    private float getMaxIndex() {

        int temp = mViewPointCoordinatesList.get(0).getData();

        float index = 0;

        for (int i = 0; i < mViewPointCoordinatesList.size(); i++) {

            if (mViewPointCoordinatesList.get(i).getData() > temp) {
                temp = mViewPointCoordinatesList.get(i).getData();
                index = mViewPointCoordinatesList.get(i).getViewDataY();
            }


        }


        return index;

    }

    //获取最小点位置

    private float getMinIndex() {

        int temp = mViewPointCoordinatesList.get(0).getData();

        float index = 0;

        for (int i = 0; i < mViewPointCoordinatesList.size(); i++) {

            if (mViewPointCoordinatesList.get(i).getData() < temp) {
                temp = mViewPointCoordinatesList.get(i).getData();
                index = mViewPointCoordinatesList.get(i).getViewDataY();
            }


        }


        return index;

    }

    /**
     * 计算每个点的平均值
     */

    private void averageCalculate(int max, int mid) {

        UIUtils.loge("大:" + max + " 小:" + mid);

        //用最大值减去最小值 获取中间值(必须为正数)
        int middle = absoluteValue(max - mid);
        //因为我们用了padding所以要减去相对应的坐标
        float tempHeight = mHeight;

        tempHeight -= (BOTTOM_DISTANCE + TOP_DISTANCE + (POINT_INTERVAL * 2));


        //用高度除以中间值 得到每个点的平均值
        //如果为0就直接等于0
        if (middle == 0) {
            average = tempHeight;
        } else {
            average = tempHeight / middle;
        }
        UIUtils.loge(average + "  ---每个的等分");


    }

    /**
     * 如果为负数就一定要让他变为正数
     */

    private int absoluteValue(int temp) {

        if (temp < 0) {
            temp *= -1;
        }

        return temp;
    }

    /**
     * 获取当前最大数
     */


    private int maxiMumNumber() {

        int temp = mList.get(0).getData();

        for (int i = 1; i < mList.size(); i++) {

            if (mList.get(i).getData() > temp) {
                temp = mList.get(i).getData();
            }

        }
        //如果显示高低线,那就看看高低线的值是不是最大的
        if (mCharViewData.isShowUp2DownLine()) {
            float max = mCharViewData.getUp2Down()[1];
            if (temp < max) {
                temp = (int) max;
            }
        }

        return temp;

    }


    /**
     * 获取当前最小数
     */

    private int miniMumNumber() {
        int temp = mList.get(0).getData();
        for (int i = 1; i < mList.size(); i++) {
            if (mList.get(i).getData() < temp) {
                temp = mList.get(i).getData();
            }

        }

        //如果显示高低线,那就看看高低线的值是不是最大的
        if (mCharViewData.isShowUp2DownLine()) {
            float min = mCharViewData.getUp2Down()[0];
            if (temp > min) {
                temp = (int) min;
            }
        }

        return temp;

    }

    /**
     * 获取到计算过后的点
     *
     * @return
     */
    @Override
    public List<LineCharViewData> getViewPointCoordinatesList() {
        return mViewPointCoordinatesList;
    }

    /**
     * 用户通知刷新数据
     */
    @Override
    public void notifyDataSetChanged() {


    }


    /**
     * 设置刻度线左边的值
     */

    @Override
    public void setScaleLineLifeStringBeans(ArrayList<ScaleLineLifeStringBean> scaleLineLifeStringBeans) {
        mScaleLineLifeStringBeans = scaleLineLifeStringBeans;
    }


    /**
     * 设置刻度线底部的值
     *
     * @param scaleLineBoomStringBeans
     */
    @Override
    public void setScaleLineBoomStringBeans(ArrayList<ScaleLineBoomStringBean> scaleLineBoomStringBeans) {
        this.scaleLineBoomStringBeans = scaleLineBoomStringBeans;
    }

    /**
     * 获取左边的一些东西
     *
     * @return
     */
    @Override
    public ArrayList<ScaleLineLifeStringBean> getScaleLineLifeStringBeans() {

        return mScaleLineLifeStringBeans;
    }

    /**
     * 获取底部的一些东西
     *
     * @return
     */
    @Override
    public ArrayList<ScaleLineBoomStringBean> getScaleLineBoomStringBeans() {


        return scaleLineBoomStringBeans;
    }
}

简单版本:github:https://github.com/hanxinhao000/line
载入样式版本:https://github.com/hanxinhao000/lineUp

相关文章

网友评论

      本文标题:Android简单自定义图表

      本文链接:https://www.haomeiwen.com/subject/fzohqftx.html