安卓自定义View实现简单折线图

自定义View实现折线图:

运行效果:
这里写图片描述

少说废话,实现起来还是比较简单的,无非就是使用canvas进行绘图,以及坐标的计算,下面直接贴代码:

ChartView.java

/**
 * Created by wangke on 2017/2/20.
 * 自定义折线图
 */

public class ChartView extends View{

    private int mViewHeight; //当前View的高
    private int mViewWidth; //当前View的宽

    private Paint mPaintCdt;// 绘制坐标系的画笔
    private Paint mPaintSysPoint; //绘制坐标系上刻度点
    private Paint mPaintLinePoint; //绘制折线上的点
    private Paint mPaintText; //绘制文字
    private Paint mPaintLine; //绘制折线
    private Paint mPaintDash; //绘制虚线
    private Paint mPaintSys; //x,y轴

    private Rect mXBound;
    private Rect mYBound;

    private ArrayList<Point> pointList = null;
    private int X_MAX; //传入点的X的最大坐标
    private int Y_MAX; //传入点的Y的最大坐标
    private float mScreenXdistance; //x轴刻度在屏幕上的间隔
    private float mScreenYdistance; //y轴刻度在屏幕上的间隔

    //折线图距离四周的像素大小
    private int Margin = 80;

    private int coordinateSystemColor;
    private float coordinateSystemSize;
    private int lineColor;
    private float lineSize;
    private int lineColorPoint;
    private float lineColorPointRadius;
    private int scalePointColor;
    private float scalePointRadius;
    private boolean isShowDash;
    private int xScale;
    private int yScale;
    private float dashSize;
    private int dashColor;


    public ChartView(Context context) {
        super(context);
        InitPaint();
    }

    //设置点的数据
    public void setPoint(ArrayList<Point> points) {

        pointList = new ArrayList();

        pointList = points;


        int []xPointArray = new int[100];
        int []yPointArray = new int[100];

        //遍历传入的点的坐标,获取最大的x,y点的坐标,用来计算刻度
        for(int i=0;i<pointList.size();i++){

            Point point = pointList.get(i);
            xPointArray[i] = point.x;
            yPointArray[i] = point.y;

        }

        Arrays.sort(xPointArray);
        Arrays.sort(yPointArray);

        X_MAX = xPointArray[xPointArray.length-1];
        Y_MAX = yPointArray[yPointArray.length-1];

        Log.i("wk","X的最大坐标:"+xPointArray[xPointArray.length-1]);
        Log.i("wk","y的最大坐标:"+yPointArray[yPointArray.length-1]);

        //调用绘制
        invalidate();


    }

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

        mPaintCdt = new Paint(Paint.ANTI_ALIAS_FLAG);
        //设置画线
        mPaintCdt.setStyle(Paint.Style.STROKE);
        //设置线的宽度
        mPaintCdt.setStrokeWidth(lineSize);
        mPaintCdt.setColor(lineColor);
        mPaintSysPoint = new Paint(Paint.ANTI_ALIAS_FLAG);
        //设置填充
        mPaintSysPoint.setStyle(Paint.Style.FILL);
        mPaintSysPoint.setColor(scalePointColor);
        mPaintLinePoint = new Paint(Paint.ANTI_ALIAS_FLAG);
        //设置填充
        mPaintLinePoint.setStyle(Paint.Style.FILL);
        Log.i("wk","线上点的颜色:"+lineColor);
        mPaintLinePoint.setColor(lineColorPoint);
        //绘制xy轴
        mPaintSys = new Paint(Paint.ANTI_ALIAS_FLAG);
        //设置画线
        mPaintSys.setStyle(Paint.Style.STROKE);
        //设置线的宽度
        mPaintSys.setStrokeWidth(coordinateSystemSize);
        mPaintSys.setColor(coordinateSystemColor);
        mPaintText = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaintText.setTextAlign(Paint.Align.CENTER);
        mPaintText.setColor(Color.WHITE);
        mPaintText.setTextSize(30);
        mPaintLine = new Paint(Paint.ANTI_ALIAS_FLAG);
        //设置画线
        mPaintLine.setStyle(Paint.Style.STROKE);
        //设置线的宽度
        mPaintLine.setStrokeWidth(lineSize);
        //设置画笔的颜色
        mPaintLine.setColor(lineColor);
        mPaintDash = new Paint();
        mPaintDash.setStyle(Paint.Style.STROKE);
        mPaintDash.setStrokeWidth(dashSize);
        mPaintDash.setColor(dashColor);
        mPaintDash.setPathEffect(new DashPathEffect(new float[]{10,10},0));

        mXBound = new Rect();
        mYBound = new Rect();

        invalidate();

    }

    public ChartView(Context context, AttributeSet attrs) {
        super(context, attrs);

        //获取属性值
        getAttrs(context,attrs);
        InitPaint();
    }
    //获取设置的属性
    private void getAttrs(Context context,AttributeSet attrs) {

        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ChartView);


        //坐标系颜色
        coordinateSystemColor = ta.getColor(R.styleable.ChartView_coordinateSystemColor, Color.RED);
        coordinateSystemSize = ta.getDimension(R.styleable.ChartView_coordinateSystemLineSize, 3f);


        //折线颜色
        lineColor = ta.getColor(R.styleable.ChartView_lineColor, Color.BLACK);
        lineSize = ta.getDimension(R.styleable.ChartView_lineSize, 2f);


        //折线上坐标点颜色
        lineColorPoint = ta.getColor(R.styleable.ChartView_linePointColor, Color.RED);
        //折线上坐标点的半径
        lineColorPointRadius = ta.getDimension(R.styleable.ChartView_linePointRadius,6f);


        //刻度点颜色
        scalePointColor = ta.getColor(R.styleable.ChartView_scalePointColor, Color.RED);
        //刻度点半径
        scalePointRadius = ta.getDimension(R.styleable.ChartView_scalePointRadius, 6);

        //设置是否显示虚线
        isShowDash = ta.getBoolean(R.styleable.ChartView_showDash,false);

        dashSize = ta.getDimension(R.styleable.ChartView_setDashSize,2f);

        dashColor = ta.getColor(R.styleable.ChartView_setDashColor, Color.WHITE);

        xScale = ta.getInt(R.styleable.ChartView_setXScale,5);
        yScale = ta.getInt(R.styleable.ChartView_setYScale,5);

        ta.recycle();


        Log.i("wk","coordinateSystemColor:"+ coordinateSystemColor +"\n coordinateSystemSize:"+ coordinateSystemSize +"\n"+"lineColor:"+ lineColor +"\n"+"lineSize:"+ lineSize);




    }

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

        getAttrs(context,attrs);
        InitPaint();

    }


    //测量view
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        setMeasuredDimension(measureWidth(widthMeasureSpec),measureHeight(heightMeasureSpec));




    }


    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);

        //获取当前View的宽高
        mViewWidth = w;
        mViewHeight = h;

        Log.i("wk","宽度:"+w);
        Log.i("wk","高度:"+h);

    }

    //绘制
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        //绘制X轴Y轴 以及原点
        canvas.drawLine(Margin,mViewHeight-Margin, Margin,5, mPaintSys);

        canvas.drawLine(Margin,mViewHeight-Margin,mViewWidth-5,mViewHeight-Margin, mPaintSys);

        //绘制原点
        canvas.drawCircle(Margin,mViewHeight-Margin,scalePointRadius,mPaintSysPoint);

        //

        /**
         * 计算两个刻度之间的间距:
         *
         *     1.刻度的个数 = 传入坐标最大的坐标点/坐标轴间距
         *     2.两个刻度之间的间距 = 屏幕的宽或高 /刻度的个数
         *
         */


        int num_x = X_MAX/xScale; //x轴上需要绘制的刻度的个数
        mScreenXdistance = (mViewWidth- Margin *2)/(num_x*1f);

        Log.i("wk","需要绘制的刻度个数==>"+num_x+"两个刻度间间隔:"+ mScreenXdistance);

        int num_y = Y_MAX/yScale;
        mScreenYdistance = (mViewHeight-Margin*2)/(num_y*1f);
        Log.i("wk","需要绘制的刻度个数==>"+num_x+"两个刻度间间隔:"+ mScreenXdistance);


        //绘制 X,y轴刻度标记
        for(int i=0;i<pointList.size();i++){

            canvas.drawCircle(Margin +(i* mScreenXdistance),mViewHeight-Margin,scalePointRadius,mPaintSysPoint);

            canvas.drawCircle(Margin,mViewHeight-Margin-(i* mScreenYdistance),scalePointRadius,mPaintSysPoint);

            //计算刻度字体的宽高
            String index_x = String.valueOf(i*xScale);
            String index_y = String.valueOf(i*yScale);
            mPaintText.getTextBounds(index_x,0,index_x.length(),mXBound);
            mPaintText.getTextBounds(index_y,0,index_x.length(),mYBound);
            int indexWidth_x = mXBound.width();
            int indexHeight_x = mXBound.height();
            int indexWidth_y = mYBound.width();
            int indexHeight_y = mYBound.height();

            Log.i("wk","字体的宽度:"+indexWidth_x+"字体的高度:"+indexHeight_x);
            canvas.drawText(index_x, Margin +(i* mScreenXdistance),mViewHeight-Margin+indexHeight_x+indexHeight_x/2,mPaintText);
            if(i!=0) {
                canvas.drawText(index_y, Margin - indexHeight_y-indexWidth_y/2, mViewHeight - Margin - (i * mScreenYdistance), mPaintText);
            }

        }

        /**
         * 绘制折线
         */
        Point LastPoint = new Point(); //记录上一个坐标点
        LastPoint.x = Margin;
        LastPoint.y = mViewHeight-Margin;

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


            /**
             * 计算绘制点的坐标位置
             * 绘制点的坐标 =  (传入点的的最大的xy坐标/坐标轴上的间隔) * 坐标间隔对应的屏幕上的间隔
             */
//            canvas.drawCircle(LastPoint.x,LastPoint.y,4f,mPaintPoint);
            //计算出脱离坐标系的点所处的位置
            float point_x = (pointList.get(i).x/(xScale*1f))* mScreenXdistance;
            float point_y = (pointList.get(i).y/(yScale*1f))* mScreenYdistance;

            //坐标系内的点的位置

            float startX = LastPoint.x;
            float startY = LastPoint.y;

            float endX = Margin +point_x;
            float endY = mViewHeight-Margin-point_y;

            //需要计算此处


            canvas.drawLine(startX,startY,endX,endY,mPaintLine);

            //记录上一个坐标点的位置
            LastPoint.x = (int) endX;
            LastPoint.y = (int) endY;


            if(isShowDash) {
                //绘制横向虚线
                canvas.drawLine(Margin, mViewHeight - Margin - point_y -lineColorPointRadius/2, Margin + point_x - lineColorPointRadius/2, mViewHeight - Margin - point_y -lineColorPointRadius/2, mPaintDash);
                //绘制竖向虚线
                canvas.drawLine(LastPoint.x, LastPoint.y, LastPoint.x, mViewHeight - Margin - lineColorPointRadius, mPaintDash);
            }

            canvas.drawCircle(LastPoint.x, LastPoint.y, lineColorPointRadius, mPaintLinePoint);

        }


    }

    //测量view高度
    private int measureHeight(int heightMeasureSpec) {

        int result = 0;

        int specSize = MeasureSpec.getSize(heightMeasureSpec); //获取高的高度 单位 为px

        int specMode = MeasureSpec.getMode(heightMeasureSpec);//获取测量的模式

        //如果是精确测量,就将获取View的大小设置给将要返回的测量值
        if(specMode == MeasureSpec.EXACTLY){

            Log.i("wk","高度:精确测量 + specSize:==>"+specSize);

            result = specSize;

        }else{

            Log.i("wk","高度:UNSPECIFIED + specSize:==>"+specSize);

            result = 400;

            //如果设置成wrap_content时,给高度指定一个值
            if(specMode == MeasureSpec.AT_MOST){

                Log.i("wk","高度:最大值模式 + specSize:==>"+specSize);

                result = Math.min(result,specSize);

            }
        }



        return result;
    }

    //测量view宽度
    private int  measureWidth(int widthMeasureSpec) {


        int result = 0;

        int specSize = MeasureSpec.getSize(widthMeasureSpec); //获取高的高度

        int specMode = MeasureSpec.getMode(widthMeasureSpec);//获取测量的模式

        //如果是精确测量,就将获取View的大小设置给将要返回的测量值
        if(specMode == MeasureSpec.EXACTLY){

            Log.i("wk","宽度:精确测量 + specSize:==>"+specSize);

            result = specSize;

        }else{

            Log.i("wk","宽度:UNSPECIFIED + specSize:==>"+specSize);

            result = 400;
            //如果设置成wrap_content时,给高度指定一个值
            if(specMode == MeasureSpec.AT_MOST){

                Log.i("wk","宽度:最大值模式 + specSize:==>"+specSize);

                result = Math.min(result,specSize);
            }
        }

        return result;
    }
}

attrs.xml
自定义属性:

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <declare-styleable name="ChartView">
        <!--设置坐标系的颜色-->
        <attr name="coordinateSystemColor" format="color" />
        <!--设置坐标系线条的尺寸-->
        <attr name="coordinateSystemLineSize" format="dimension" />

        <!--设置刻度点的颜色-->
        <attr name="scalePointColor" format="color" />
        <!--设置刻度点的半径-->
        <attr name="scalePointRadius" format="dimension" />

        <!--设置折线的颜色-->
        <attr name="lineColor" format="color" />
        <!--设置着折线的宽度-->
        <attr name="lineSize" format="dimension" />

        <!--设置折线点的颜色-->
        <attr name="linePointColor" format="color" />
        <!--设置折线点的半径-->
        <attr name="linePointRadius" format="dimension" />

        <!--设置是否显示虚线-->
        <attr name="showDash" format="boolean" />
        <attr name="setDashSize" format="dimension"/>
        <attr name="setDashColor" format="color"/>

        <!--设置坐标轴上刻度的间隔-->
        <attr name="setXScale" format="integer" />
        <attr name="setYScale" format="integer" />


    </declare-styleable>


</resources>

activity_main.xml


    <com.merpyzf.studymultimedia.ChartView
        android:id="@+id/myChartView"
        android:layout_marginTop="20dp"
        android:background="@color/colorPrimary"
        android:layout_width="match_parent"
        android:layout_height="600dp"
        app:linePointColor="@color/colorAccent"
        app:scalePointColor="#f76"
        app:linePointRadius="5dp"
        app:lineColor="#fff600"
        app:lineSize="2dp"
        app:coordinateSystemColor="#000000"
        app:coordinateSystemLineSize="2dp"
        app:scalePointRadius="6dp"
        app:setDashSize="1dp"
        app:showDash="true"
        />

MainActivity.java

public class MainActivity extends AppCompatActivity {

    private MyService.MyBinder myBinder;
    private ChartView MyChartView;
    private ArrayList<Point> pointList;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        MyChartView = (ChartView) findViewById(R.id.myChartView);
        Random random = new Random();
        pointList = new ArrayList<Point>();

        for(int i=0;i<=10;i++){
            Point p = new Point(i*5,random.nextInt(30));
            pointList.add(p);
        }

        //给ChartView设置坐标
        MyChartView.setPoint(pointList);

    }
}

后面要花一段时间仔细的研究一下自定义View的知识,当然上面的这个折线图只是脑子一热敲出来的,不具备实用能力(⊙o⊙)…

量变引起质变

撒花 撒花 ^^O(∩∩)O哈哈~

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值