自定义view——折线图

Posted Gradle官方文件

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了自定义view——折线图相关的知识,希望对你有一定的参考价值。

安卓开发,自定义view折线图的练习

效果图

1.自定义view

public class BrokenLineView extends View 
    private float left;//折线图左边坐标
    private float bottom;//折线图底部坐标
    private float right;//折线图右边坐标
    private float top;//折线图顶部坐标


    private Context context;
    private Canvas mCanvas;
    private Path mPath;
    private Paint linePaint;
    private Paint mTextPaint;
    public BrokenLineView(Context context) 
        super(context);
    

    public BrokenLineView(Context context, AttributeSet attrs) 
        super(context, attrs);

        this.context = context;
        initPaint();
        initData();
    

    private void initPaint()
        mPath = new Path();
        linePaint = new Paint();
        linePaint.setColor(Color.YELLOW);//线条的颜色
        linePaint.setStrokeWidth(8);//线条的宽度
        linePaint.setAntiAlias(true);//取消锯齿
        linePaint.setStyle(Paint.Style.STROKE);//粗线
        mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG | Paint.LINEAR_TEXT_FLAG);
        mTextPaint.setColor(Color.WHITE);
        mCanvas = new Canvas();
    

    @Override
    protected void onDraw(Canvas canvas) 
        super.onDraw(canvas);

        canvas.save();//锁定画布
        drawXY(canvas);
        drawXYelement(canvas);
        drawLines(canvas);
        drawBitmap(canvas);
    

    /**
     * 获取父控件尺寸
     * @param w
     * @param h
     * @param oldw
     * @param oldh
     */
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) 
        super.onSizeChanged(w, h, oldw, oldh);
        left = w*(1/16f);
        right = w*(15/16f);
        top = w*(1/16f);
        bottom = w*(8/16f);
    


    //画XY坐标轴
    private void drawXY(Canvas canvas)

        /**
         * 连接三个坐标点
         * 1(left,top) 2(left,bottom) 3(right,bottom)
         */
        Log.i("123", "left: "+left+"top: "+top+"bottom: "+bottom);
        mPath.moveTo(left,top);
        mPath.lineTo(left,bottom);
        mPath.lineTo(right,bottom);

        //连接三个坐标
        canvas.drawPath(mPath,linePaint);
        //释放画布
        canvas.restore();
    

    //画坐标轴旁边的XY
    private void drawXYelement(Canvas canvas)
        canvas.save();

        mTextPaint.setTextSize(36);//文字大小

        //Y轴文字提示
        mTextPaint.setTextAlign(Paint.Align.LEFT);
        canvas.drawText("Y",left+20,top,mTextPaint);

        //X轴文字提示
        mTextPaint.setTextAlign(Paint.Align.RIGHT);
        canvas.drawText("X",right,bottom+50,mTextPaint);

        canvas.restore();
    

    private List<PointF> pointFList = new ArrayList<>();
    private String[] index_x = "","周一","周二","周三","周四","周五","周六","周天";
    private int[] index_y = 0,1,2,3,4,5,6,7,8;
    private float spaceX,spaceY;//横纵坐标间隔
    private float maxX,maxY;//横纵数据最大值
    private void initData()
        pointFList.add(new PointF(0.3F, 0.5F));
        pointFList.add(new PointF(1F, 22.7F));
        pointFList.add(new PointF(2F, 33.5F));
        pointFList.add(new PointF(3F, 36.2F));
        pointFList.add(new PointF(4F, 18.8F));
        pointFList.add(new PointF(5F, 15.5F));
        pointFList.add(new PointF(6F, 24.2F));
        pointFList.add(new PointF(7F, 52.5F));
    
    //画网格
    private void drawLines(Canvas canvas)
        canvas.save();
        linePaint.setStrokeWidth(2);

        int count = pointFList.size();
        spaceX = (right-left)/count;
        spaceY = (bottom-top)/count;
        // 计算除数的值为数据长度减一,8个数据,7条线。
        int divisor = count-1;

        //计算X轴最大值
        for (int i = 0; i < count; i++) 
            if (maxX<pointFList.get(i).x)
                maxX = pointFList.get(i).x;//X轴最大值
            
        
        //计算X轴最近的能被Count整除的值
        int remainderX = (int) maxX % divisor;
        maxX = remainderX == 0 ? ((int) maxX) : divisor - remainderX + maxX;

        //计算Y轴最大值
        for (int i = 0; i < count; i++) 
            if (maxY < pointFList.get(i).y)
                maxY = pointFList.get(i).y;//y轴最大值
            
        
        //计算Y轴最近的能被count整除的值
        int remainderY = ((int) maxY) % divisor;
        maxY = remainderY == 0 ? ((int) maxY) : divisor + remainderY + maxY;

        //锁定画布并设定透明度为75%
        int sc = canvas.saveLayerAlpha(0,0,canvas.getWidth(),canvas.getHeight(),75,Canvas.ALL_SAVE_FLAG);
        // 绘制横纵线段
        for (float y = bottom - spaceY; y >  top; y -= spaceY) 

            for (float x =  left; x < right; x += spaceX) 
                /*
                 * 绘制纵向线段
                 */
                if (y == top + spaceY) 
                    canvas.drawLine(x, y, x, y + spaceY * (count - 1), linePaint);
                

                /*
                 * 绘制横向线段
                 */
                if (x == right - spaceX) 
                    canvas.drawLine(x, y, x - spaceX * (count - 1), y, linePaint);
                
            
        
        //还原画布
        canvas.restoreToCount(sc);
        int num = 0;//用于给X轴赋值
        int num_y  = 0;//用于给Y轴赋值

        for (float y = bottom - spaceY; y > top; y -= spaceY) 
            for (float x = left; x < right; x += spaceX) 
                mTextPaint.setTextSize(28);

                /*
                 * 绘制横轴刻度数值
                 */
                if (y == bottom - spaceY) 
                    canvas.drawText(""+index_x[num], x+8, bottom+top/2, mTextPaint);
                

                /*
                 * 绘制纵轴刻度数值
                 * 简单来说就是,Y轴上的坐标点,X轴是恒定不变的,但是Y轴是变化的(buttom - 间距)+10的距离向上绘制
                 */
                if (x == left) 
                    canvas.drawText(""+index_y[num_y+1], left - (left/2), y + 10, mTextPaint);
                
                num++;
            
            num_y++;
        
    

    private void drawBitmap(Canvas canvas)
        /**
         * 我们给我们的区域先绘制一个颜色模块,做法很简单,生成一个图片即可,然后透明度设置下
         * Bitmap.createBitmap()
         * 关于他的6个方法,可查看博客:http://www.cnblogs.com/wangxiuheng/p/4503610.html
         */
        Log.i("123", "X: "+((int) (right - left - spaceX))+";Y:"+((int) (bottom - top - spaceY)));
        Bitmap bitmap = Bitmap.createBitmap(((int) (right - left - spaceX)), ((int) (bottom - top - spaceY)), Bitmap.Config.ARGB_8888);
        mCanvas.setBitmap(bitmap);
        /**
         * 为画布填充一个半透明的红色
         * drawARGB(a,r,g,b)a:透明度 r:红色g:绿色b:蓝色
         * */
        mCanvas.drawARGB(55,255,0,0);
        mPath.reset();//重置曲线
        canvas.drawBitmap(bitmap,left,top+spaceY,null);


        //绘制我们的坐标点
        drawText(canvas);
    


    public synchronized void setdata(List<PointF> mList, String signX, String signY, Activity activity)
        if (null == mList || mList.size() == 0)
            throw new IllegalArgumentException("没有数据展示");
        
        if (mList.size()>10)
            throw new IllegalArgumentException("数据太多建议使用散点图");
        

        //设置数据并重绘视图
        this.pointFList = mList;
        this.context = activity;

        invalidate();

    

    private void drawText(Canvas canvas) 
        Paint pointPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
        pointPaint.setStyle(Paint.Style.FILL);//焦点的类型
        pointPaint.setColor(Color.WHITE);//焦点的颜色

        if(pointFList.size()==0)
            Toast.makeText(context,"暂无折现数据",Toast.LENGTH_SHORT).show();
        else 
                /*
         * 生成Path和绘制Point
         */
            for (int i = 0; i < pointFList.size(); i++) 
                // 计算x坐标
                float x = mCanvas.getWidth() / maxX * pointFList.get(i).x;
                // 计算y坐标
                float y = mCanvas.getHeight() / maxY * pointFList.get(i).y;
                y = mCanvas.getHeight() - y;

                // 绘制小点点
                mCanvas.drawCircle(x, y, 6, pointPaint);

            /*
             * 如果是第一个点则将其设置为Path的起点
             */
                if (i == 0) 
                    mPath.moveTo(x, y);
                

                // 连接各点
                mPath.lineTo(x, y);
            

            // 设置PathEffect
            linePaint.setPathEffect(new CornerPathEffect(10));

            // 重置线条宽度
            linePaint.setStrokeWidth(4);

            // 将Path绘制到我们自定的Canvas上
            mCanvas.drawPath(mPath, linePaint);
        
    

2.xml中引用

    <com.hsg.brokenlinedemo.BrokenLineView
        android:id="@+id/blv"
        android:layout_width="match_parent"
        android:layout_height="500dp"
        android:background="#8B7500"/>

3.activity中添加数据

        List<PointF> pointFs = new ArrayList<PointF>();
        pointFs.add(new PointF(0.3F, 0.5F));
        pointFs.add(new PointF(1F, 22.7F));
        pointFs.add(new PointF(2F, 33.5F));
        pointFs.add(new PointF(3F, 36.2F));
        pointFs.add(new PointF(4F, 18.8F));
        pointFs.add(new PointF(5F, 15.5F));
        pointFs.add(new PointF(6F, 24.2F));
        pointFs.add(new PointF(7F, 52.5F));

        blv.setdata(pointFs, "X轴提示文字", "Y轴提示文字",MainActivity.this);
创作打卡挑战赛 赢取流量/现金/CSDN周边激励大奖

以上是关于自定义view——折线图的主要内容,如果未能解决你的问题,请参考以下文章

Excel中制作折线图的横坐标是按时间顺序自动排列的,请问如何设置在横坐标上显示自定义的时间?

自定义view—折线图

自定义View——折线图

自定义View——折线图

echarts自定义折线图横坐标时间间隔踩坑总结

自定义view——芝麻分折线图