Android自定义图表:ChartView

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zhanggang740/article/details/51769423

效果图
Design images :
这里写图片描述

View Code:

/**
 * Created by JackWaiting on 2016/6/24.
 */
public class CharView extends View {

    private  Paint mRowLinePaint,mColLinePaint;  //横线、竖线的画笔
    private Paint redPaint,greenPaint,yellowPaint;
    private Paint mColTextPaint;  //横坐标画笔
    private int mTopPadding, mLeftPadding, mRightPadding, mBottomPadding;  //图标距离上下左右的边距
    private int mFontHeight, mFontTopMargin;  //字体的高度与字体上方的边距
    private int mRowNumber = 7, mColNumber = 30;  //行与列
    private int[] mRowYs,mColYs,mColTextYs;  //装动态行列坐标的数组
    private int mRowHeight, mColWidth;  //行高与列高
    private int mRowLineWidth , mColLineWidth ; //行线与列线的宽度
    private int mRowStartX, mRowEndX,mColStartY,mColEndY;
    private int mColTextWidth,mColTextHeight;
    private float[] lifeTimeData = new float[mColNumber+1];  //每月最多31个值
    private int mLifeTimeWidth;
    private float lifeTimeHeight;  //由于高度的精确度比较高,建议使用float
    private float mLifeTimeStartX[],mLifeTimeEndX[],mLifeTimeStartY[],mLifeTimeEndY[];
    private float mMaxLifeTimeData = 8f,mOneRowTip = 7.45f,mTwoRowTip = 6.34f,mThreeRowTip = 6.18f,mFourRowTip = 5.97f;
    private float mRowTipData[] = new float[]{0f,7.45f,0f,0f,6.34f,6.18f,5.97f,0f}; //不显示则用0f表示
    private String[] chars = new String[]{"0","5","10","15","20","25","30"};
    private  Path path;
    private int mCircleRadius = 20;
    private PathEffect effects;
    private int mRowTipWidth =150,mRowTipHeight = 60;

    public CharView(Context context) {
        super(context);
        init(context,null,0);
    }

    public CharView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context,null,0);
    }

    public CharView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context,attrs,defStyleAttr);
    }

    public void getLifeTimeData(float [] data){
        this.lifeTimeData = data;
    }

    private void init(Context context,AttributeSet attrs,int defStyle) {
        mTopPadding =  PixelUtil.dp2px(50, getContext());
        mRowLineWidth = PixelUtil.dp2px(1, getContext());
        mColLineWidth = PixelUtil.dp2px(1, getContext());
        mLeftPadding = PixelUtil.dp2px(20, getContext());
        mRightPadding = PixelUtil.dp2px(20, getContext());
        mBottomPadding = PixelUtil.dp2px(55, getContext());
        mFontHeight = PixelUtil.dp2px(10, getContext());
        mFontTopMargin = PixelUtil.dp2px(15, getContext());
        TypedArray a = context.obtainStyledAttributes(attrs,
                R.styleable.MyCharView);
        effects= new DashPathEffect(new float[]{5,5,5,5},10);
        path = new Path();
        mRowLinePaint = new Paint();
        mRowLinePaint.setColor(a.getColor(R.styleable.MyCharView_cv_row,getResources().getColor(R.color.row)));
        mRowLinePaint.setStrokeWidth(mRowLineWidth);
        mColLinePaint = new Paint();
        mColLinePaint.setColor(a.getColor(R.styleable.MyCharView_cv_col,getResources().getColor(R.color.col)));
        mColLinePaint.setStrokeWidth(mColLineWidth);
        mColLinePaint.setStyle(Paint.Style. STROKE);
        mColLinePaint.setAntiAlias( true);
        redPaint = new Paint();
        redPaint.setColor(a.getColor(R.styleable.MyCharView_cv_red,getResources().getColor(R.color.red)));
        redPaint.setStrokeWidth(PixelUtil.dp2px(2, getContext()));
        greenPaint = new Paint();
        greenPaint.setColor(a.getColor(R.styleable.MyCharView_cv_green,getResources().getColor(R.color.green)));
        greenPaint.setStrokeWidth(PixelUtil.dp2px(2, getContext()));
        yellowPaint = new Paint();
        yellowPaint.setColor(a.getColor(R.styleable.MyCharView_cv_row,getResources().getColor(R.color.yellow)));
        yellowPaint.setStrokeWidth(PixelUtil.dp2px(2, getContext()));

        mColTextPaint = new Paint();
        mColTextPaint.setColor(a.getColor(R.styleable.MyCharView_cv_row,Color.WHITE));
        mColTextPaint.setTextSize(50);
    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        for (int i= 0;i<mColYs.length;i++){
            if(i%5==0){
                canvas.drawLine(mColYs[i], mColStartY, mColYs[i],mColEndY, mRowLinePaint);
                canvas.drawText(chars[i/5]+"",mColTextYs[i/5]-(mColWidth/2), mColTextHeight,mColTextPaint);
            }
            else{
                //canvas.drawLine(mColYs[i], mColStartY, mColYs[i],mColEndY, mColLinePaint);
                path.moveTo(mColYs[i], mColStartY);
                path.lineTo(mColYs[i],mColEndY);
                mColLinePaint.setPathEffect(effects);
                canvas.drawPath(path, mColLinePaint);
            }
        }

        for (int i= 0;i<mRowYs.length;i++){
            canvas.drawLine(mRowStartX, mRowYs[i], mRowEndX, mRowYs[i], mRowLinePaint);
        }

        for (int i= 0;i<lifeTimeData.length-1;i++){
            if(lifeTimeData[i] >mOneRowTip){
                canvas.drawLine(mLifeTimeStartX[i], mLifeTimeStartY[i], mLifeTimeEndX[i], mLifeTimeEndY[i], redPaint);
                drawCircleView(i,redPaint,canvas);
            }
            else if(lifeTimeData[i] >mTwoRowTip && lifeTimeData[i] <= mOneRowTip){
                canvas.drawLine(mLifeTimeStartX[i], mLifeTimeStartY[i], mLifeTimeEndX[i], mLifeTimeEndY[i], greenPaint);
                drawCircleView(i,greenPaint,canvas);
            }
            else if(lifeTimeData[i]  >mThreeRowTip && lifeTimeData[i] < mTwoRowTip){
                canvas.drawLine(mLifeTimeStartX[i], mLifeTimeStartY[i], mLifeTimeEndX[i], mLifeTimeEndY[i], greenPaint);
                drawCircleView(i,greenPaint,canvas);
            }
            else if(lifeTimeData[i]  >mFourRowTip && lifeTimeData[i] <= mThreeRowTip){
                canvas.drawLine(mLifeTimeStartX[i], mLifeTimeStartY[i], mLifeTimeEndX[i], mLifeTimeEndY[i], yellowPaint);
                drawCircleView(i,yellowPaint,canvas);
            }
            else if(lifeTimeData[i]  >=0 && lifeTimeData[i] <= mFourRowTip){
                canvas.drawLine(mLifeTimeStartX[i], mLifeTimeStartY[i], mLifeTimeEndX[i], mLifeTimeEndY[i], redPaint);
                drawCircleView(i,redPaint,canvas);
            }
            else{
                canvas.drawLine(mLifeTimeStartX[i], mLifeTimeStartY[i], mLifeTimeEndX[i], mLifeTimeEndY[i], redPaint);
            }
        }
        //最后一点无法循环到,手动赋值
        canvas.drawCircle(mLifeTimeStartX[30],mLifeTimeStartY[30], mCircleRadius,redPaint);
        for(int i=0;i< mRowYs.length;i++){

            if(i==0 ||i==2 ||i == 3 ||i == 7){
                continue;
            }
            else{
                canvas.drawRect(0, mRowYs[i]-mRowTipHeight/2,mRowTipWidth, mRowYs[i]+mRowTipHeight/2, mRowLinePaint);  // 矩形
                canvas.drawText(mRowTipData[i]+"",20, mRowYs[i]+20,mColTextPaint);  //给每个矩形进行参数赋值
            }

        }
    }


    //每隔5个点画圆
    private void  drawCircleView(int i,Paint paint,Canvas canvas){
        if(i%5==0){
            canvas.drawCircle(mLifeTimeStartX[i],mLifeTimeStartY[i], mCircleRadius,paint);
        }
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        int viewWidth = w - mLeftPadding - mRightPadding;
        int viewHeight = h - mTopPadding - mBottomPadding - mFontHeight - mFontTopMargin;
        mColTextHeight = h - mTopPadding  - mFontTopMargin;
        mRowStartX = mLeftPadding - mRowLineWidth/2;
        mRowEndX = w - mRightPadding  - (mRowLineWidth*mRowNumber)+mRowLineWidth/2;
        mColStartY = mTopPadding -mColLineWidth/2;
        mColEndY  =  h-mBottomPadding - mFontHeight - mFontTopMargin - (mRowNumber+1);
        mRowYs = new int[mRowNumber + 1];
        mColYs = new int[mColNumber + 1];
        mColTextYs = new int[chars.length];
        mRowHeight = (viewHeight-(mRowNumber+1)*mRowLineWidth)/mRowNumber;
        mColWidth  = (viewWidth - (mColNumber+1)*mColLineWidth)/mColNumber;
        for (int i = 0; i < mRowYs.length ; i++){
            mRowYs[i] = mTopPadding + i * (mRowHeight+mRowLineWidth) ;
        }
        for (int i = 0; i < mColYs.length ; i++){
            mColYs[i] = mLeftPadding + i * (mColWidth+mColLineWidth);
        }
        for (int i = 0; i < mColTextYs.length ; i++){
            mColTextYs[i] = mLeftPadding + i * (mColWidth+mColLineWidth)*5;
        }

        //具体画线
        mLifeTimeStartX = new float[mColNumber+1];
        mLifeTimeStartY = new float[mColNumber+1];
        mLifeTimeEndX = new float[mColNumber+1];
        mLifeTimeEndY = new float[mColNumber+1];
        for (int i=0;i<mLifeTimeStartX.length;i++){
            mLifeTimeStartX[i] =  mLeftPadding + i * (mColWidth+mColLineWidth);
        }
        for (int i=0;i<mLifeTimeEndX.length;i++){
            mLifeTimeEndX[i] =  mLeftPadding +mColWidth+mColLineWidth+ i * (mColWidth+mColLineWidth);
        }
        getLifeTime();
    }

    //获取每个点进行连接
    private void getLifeTime() {

        //计算Y轴的坐标
        for (int i = 0;i<lifeTimeData.length;i++){
            if(lifeTimeData[i] >=mOneRowTip){
                mLifeTimeStartY[i] = mTopPadding + (((mMaxLifeTimeData-mOneRowTip)-(lifeTimeData[i]-mOneRowTip))/(mMaxLifeTimeData-mOneRowTip))*(mRowHeight+mRowLineWidth);
            }
            else if(lifeTimeData[i] >mTwoRowTip && lifeTimeData[i] < mOneRowTip){
                mLifeTimeStartY[i] = mTopPadding + mRowHeight+mRowLineWidth+ (((mOneRowTip-mTwoRowTip)-(lifeTimeData[i]-mTwoRowTip))/(mOneRowTip-mTwoRowTip))*3*(mRowHeight+mRowLineWidth);
            }
            else if(lifeTimeData[i]  >=mThreeRowTip && lifeTimeData[i] < mTwoRowTip){
                mLifeTimeStartY[i] = mTopPadding + ((mRowNumber+1)/2)*(mRowHeight+mRowLineWidth) + (((mTwoRowTip-mThreeRowTip)-(lifeTimeData[i]-mThreeRowTip))/(mTwoRowTip-mThreeRowTip))*(mRowHeight+mRowLineWidth);
            }
            else if(lifeTimeData[i]  >mFourRowTip && lifeTimeData[i] < mThreeRowTip){
                mLifeTimeStartY[i] = mTopPadding + ((mRowNumber+1)-3)*(mRowHeight+mRowLineWidth)+ (((mThreeRowTip-mFourRowTip)-(lifeTimeData[i]-mFourRowTip))/(mThreeRowTip-mFourRowTip))*(mRowHeight+mRowLineWidth);
            }
            else if(lifeTimeData[i]  >=0 && lifeTimeData[i] <= mFourRowTip){
                mLifeTimeStartY[i] = mTopPadding + ((mRowNumber+1)-2)*(mRowHeight+mRowLineWidth)+ ((mFourRowTip-lifeTimeData[i])/mFourRowTip)*(mRowHeight+mRowLineWidth);
            }
            else{
                mLifeTimeStartY[i] = mTopPadding + mRowNumber * ( mRowHeight + mRowLineWidth );
            }
        }
        //把Y轴连在一起
        for (int i=1;i<mLifeTimeStartY.length;i++){
            mLifeTimeEndY[i-1] = mLifeTimeStartY[i];
        }
        //mLifeTimeEndY[mLifeTimeStartX.length-1 ] = mLifeTimeStartY[mLifeTimeStartX.length-1];
    }
}

——————————————————————————–

Update

之前的代码只是对图表进行绘制,并未做点击事件,正常的图表是在点击某项数据的时候,会展现出来数据,本次更新已解决此问题。
效果图:

这里写图片描述

update view code:

@Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        drawCharts(canvas);
        if(mSpot >= 0 && mSpot <= 30){
             if(mLifeTimeStartY[mSpot] >=mTopPadding +mRowHeight+mRowLineWidth &&mLifeTimeStartY[mSpot] <=mTopPadding +5*(mRowHeight+mRowLineWidth)){
                drawTouchData(canvas,greenPaint);
            }
            else if(mLifeTimeStartY[mSpot] >=mTopPadding +5*(mRowHeight+mRowLineWidth) &&mLifeTimeStartY[mSpot] <=mTopPadding +6*(mRowHeight+mRowLineWidth)){
                drawTouchData(canvas,yellowPaint);
            }
            else{
                drawTouchData(canvas,redPaint);
             }
        }

    }

        @Override
    public boolean onTouchEvent(MotionEvent event) {

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                if(ignoreTouch(event.getX(),event.getY())){
                    mSpot = getTouchSpot(event.getX());
                    Log.i("进来了","ACTION_DOWN已处理"+mSpot);
//                    setPressed(true);
                   invalidate();
                    return true;
                }
                else{
                    mSpot =-1;
                }
                break;
            case MotionEvent.ACTION_MOVE:
                break;
            case MotionEvent.ACTION_UP:
//                setPressed(false);
                break;
            case MotionEvent.ACTION_CANCEL:
//                setPressed(false);

                break;
        }

        return super.onTouchEvent(event);
    }


    //根据点击的范围获取到当前点击的点
    private int  getTouchSpot(float x) {
        int index = 0;
        for (int i = 0;i<mLifeTimeStartX.length;i++){
            if(x>mLifeTimeStartX[i]-mColWidth/2 && x<mLifeTimeEndX[i] -mColWidth/2){
                index = i;
            }
        }
        return index;
    }


    //判断当前点击的范围是否需要处理
    private boolean ignoreTouch(float x , float y) {
        boolean ignore = false;
        if((x>=(mLeftPadding-mColWidth/2) &&x <=(mRowEndX+mColWidth/2))&&(y>=mTopPadding-mColWidth/2 && y<=mColEndY+mColWidth/2) ){
            ignore = true;
        }
        return  ignore;
    }


    //点击Touch后给出每个点的具体数据
    private void drawTouchData(Canvas canvas,Paint paint) {
        canvas.drawRect(mLifeTimeStartX[mSpot]-mRowTipWidth/2, mLifeTimeStartY[mSpot]-100-mRowTipHeight/2,mLifeTimeStartX[mSpot]+mRowTipWidth/2, mLifeTimeStartY[mSpot]+mRowTipHeight/2-100, paint);  // 矩形
        canvas.drawText(lifeTimeData[mSpot]+"",mLifeTimeStartX[mSpot]-mRowTipWidth/2+20, mLifeTimeStartY[mSpot]-80,mColTextPaint);  //查看任意一点的信息
    }

最新代码已更新至Github,以下是下载链接:

Code download:
Github最新代码下载地址:https://github.com/JackWaiting/ChartView

Android chartView

01-21

使用了helloCharts里面lineChartView,希望实时跟新 读取到的文本数据 但是线程不能设置低于1000ms,比如说设置200ms,画不到100个点就崩掉,找错误也找不出来。 测试的是testAPP :rnpackage lecho.lib.hellocharts.samples;rnrnimport android.os.Bundle;rnimport android.support.v7.app.AppCompatActivity;rnimport android.util.Log;rnimport android.widget.TextView;rnrnimport java.io.BufferedReader;rnimport java.io.IOException;rnimport java.io.InputStream;rnimport java.io.InputStreamReader;rnimport java.util.ArrayList;rnimport java.util.List;rnrnimport lecho.lib.hellocharts.gesture.ContainerScrollType;rnimport lecho.lib.hellocharts.model.Axis;rnimport lecho.lib.hellocharts.model.Line;rnimport lecho.lib.hellocharts.model.LineChartData;rnimport lecho.lib.hellocharts.model.PointValue;rnimport lecho.lib.hellocharts.model.Viewport;rnimport lecho.lib.hellocharts.view.LineChartView;rnrn/**rn * Created by Administrator on 2018/1/20.rn */rnrnpublic class testApp extends AppCompatActivityrn private LineChartView lineChartView;rn private LineChartData lineChartData; // 折线图显示的数据(包括坐标上的点)rn private List linesList; // 线的集合rn private List points; // 要显示的点 x,y值rn private List pointValueList;rn private PointValue poa;rn private int position = 0;rn //定时刷新折线图rn private boolean isFinish = false;rn private TextView textView;rn private Axis axisX; // X轴rn private Axis axisY; // Y轴rnrn private String TAG = "testApp";rn private Thread testThread;rn boolean isRun = false;rn @Overridern protected void onCreate(Bundle savedInstanceState) rn super.onCreate(savedInstanceState);rn setContentView(R.layout.test_activity);rn textView = (TextView)findViewById(R.id.text_test);rn lineChartView = (LineChartView) findViewById(R.id.line_chart1);rn intAxisView();rnrn isRun = true;rnrn testThread = new Thread(new Runnable() rnrn @Overridern public void run() rn while(isRun)rn rn try rn showMovingLineChart();rnrnrnrn Thread.sleep(1000);rn rn catch (Exception e)rn rn Log.e(TAG,"destroy"+e);rn finally rnrn rn rn rn );rn testThread.start();rnrn rnrn private void intAxisView()rn poa = new PointValue();rn pointValueList = new ArrayList();rn linesList = new ArrayList();rnrn /** 初始化Y轴 */rn axisY = new Axis();rn // axisY.setName("浓度(单位:XX)"); //添加Y轴的名称rn axisY.setHasLines(true); //Y轴分割线rn // axisY.setTextSize(10); //设置字体大小rn// axisY.setTextColor(Color.parseColor("#AFEEEE")); //设置Y轴颜色,默认浅灰色rn lineChartData = new LineChartData(linesList);rn lineChartData.setAxisYLeft(axisY); //设置Y轴在左边rnrn /** 初始化X轴 */rn axisX = new Axis();rn axisX.setHasTiltedLabels(false); //X坐标轴字体是斜的显示还是直的,true是斜的显示rn// axisX.setTextColor(Color.CYAN); //设置X轴颜色rn // axisX.setName("时间(单位:s)"); //X轴名称rn axisX.setHasLines(true); //X轴分割线rn // axisX.setTextSize(10); //设置字体大小rn axisX.setMaxLabelChars(1); //设置0的话X轴坐标值就间隔为1rn// List mAxisXValues = new ArrayList();rn// for (int i = 0; i < 61; i++) rn// mAxisXValues.add(new AxisValue(i).setLabel(i+""));rn// rn// axisX.setValues(mAxisXValues); //填充X轴的坐标名称rn lineChartData.setAxisXBottom(axisX); //X轴在底部rnrn lineChartView.setLineChartData(lineChartData);rnrn Viewport port = initViewPort(0,500); //初始化X轴500个间隔坐标rn lineChartView.setCurrentViewportWithAnimation(port);rn lineChartView.setInteractive(false); //设置不可交互rn lineChartView.setScrollEnabled(true);rn lineChartView.setValueTouchEnabled(false);rn lineChartView.setFocusableInTouchMode(false);rn lineChartView.setViewportCalculationEnabled(false);rn lineChartView.setContainerScrollEnabled(true, ContainerScrollType.VERTICAL);rn lineChartView.startDataAnimation();rnrn LoadData(); //加载待显示数据rn rnrnrn private Viewport initViewPort(float left,float right) rn Viewport port = new Viewport();rn port.top = 90; //Y轴上限,固定(不固定上下限的话,Y轴坐标值可自适应变化)rn port.bottom = 20; //Y轴下限,固定rn port.left = left; //X轴左边界,变化rn port.right = right; //X轴右边界,变化rn return port;rn rnrn private void LoadData() rn int len = 1024;rn points = new ArrayList();rn InputStream fileInput = getResources().openRawResource(R.raw.spodata);rnrn try rn len = fileInput.available();rn catch (IOException e) rn e.printStackTrace();rn rn char[] buffer = new char[len];rn int[] bytbuffer = new int[len];rnrn BufferedReader reader = new BufferedReader(new InputStreamReader(fileInput));rn if(fileInput.equals("")) rn return;rn rn try rn reader.read(buffer);rn String s = String.valueOf(buffer);rn String[] dataArr = s.split(" ");rn for (int i = 0; i < dataArr.length - 1; i++) rn if(!dataArr[i].equals("")) rn try rn bytbuffer[i] = Integer.valueOf(dataArr[i]);rn points.add(new PointValue(i + 1, bytbuffer[i]));rn rn catch(Exception E)rn rn textView.setText(E+"");rn rn rn rnrnrn catch (IOException e) rn e.printStackTrace();rn rnrn rn private void showMovingLineChart() rn poa = points.get(position);rn pointValueList.add(points.get(position)); //实时添加新的点rn // Log.e(TAG,"position"+poa);rn //根据新的点的集合画出新的线rn Line line = new Line(pointValueList); //rnrn // line.setColor(Color.parseColor("#FFCD41")); //设置折线颜色rn // line.setShape(ValueShape.CIRCLE); //设置折线图上数据点形状为 圆形 (共有三种 :ValueShape.SQUARE ValueShape.CIRCLE ValueShape.DIAMOND)rn line.setCubic(true); //曲线是否平滑,true是平滑曲线,false是折线rn //line.setHasLabels(true); //数据是否有标注rn// line.setHasLabelsOnlyForSelected(true); //点击数据坐标提示数据,设置了line.setHasLabels(true);之后点击无效rn line.setHasLines(true); //是否用线显示,如果为false则没有曲线只有点显示rn line.setHasPoints(false); //是否显示圆点 ,如果为false则没有原点只有点显示(每个数据点都是个大圆点)rnrn linesList.add(line); // 添加线rnrn lineChartData = new LineChartData(linesList);rn lineChartData.setAxisYLeft(axisY); //设置Y轴在左rn lineChartData.setAxisXBottom(axisX); //X轴在底部rn lineChartView.setLineChartData(lineChartData);rnrn float xAxisValue = points.get(position).getX();rn Log.e("xAxisValue", "" + xAxisValue);rn //根据点的横坐标实时变换X坐标轴的视图范围rn Viewport port;rn if (xAxisValue > 500) rn port = initViewPort(xAxisValue - 500, xAxisValue);rnrn else rn port = initViewPort(0, 500);rn rn lineChartView.setMaximumViewport(port);rn lineChartView.setCurrentViewport(port);rnrn position++;rnrn if (position >= 500) rn isRun = false;rn rn rnrnrnrnrn /**rn * 初始化数据点rn */rn private void loadDataTest() rn points = new ArrayList();rn for (int i = 0; i < 500; i++) rn points.add(new PointValue(i + 1, i % 5 * 10 + 30));rn rn rnrn @Overridern protected void onDestroy() rnrn super.onDestroy();rn try rn if(isRun == true)rn rn isRun = false;rn rn rn catch (Exception e)rn rn Log.e(TAG,"destroy"+e);rn rn rnrn 论坛

没有更多推荐了,返回首页