折线图(七)自定义属性

项目下载地址:https://github.com/Sam474850601/ChartView

效果图如下:



xml代码

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:orientation="vertical"
    android:layout_height="match_parent">
    <TextView
        android:layout_width="match_parent"
        android:text="折线图"
        android:layout_height="?attr/actionBarSize"
        android:background="#009900"
        android:textSize="18sp"
        android:textColor="#ffffff"
        android:gravity="center"
        android:textStyle="bold"
        />
    <ScrollView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">

        <HorizontalScrollView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content">

            <sam.android.utils.widget.LineChartView
                xmlns:app="http://schemas.android.com/apk/res-auto"
                android:id="@+id/lineChartView"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                app:chartValueTextSize="12sp"
                app:minHeight="100dp"
                app:minWith="100dp"
                app:xMaxValue="15"
                app:xUnit="16dp"
                app:xName="@string/xName"
                app:yName="@string/yName"
                app:yChartMargin="20dp"
                app:yMaxValue="12"
                app:yUnit="20dp" />

        </HorizontalScrollView>
    </ScrollView>
</LinearLayout>

<resources>
    <string name="xName">数目(个/月)</string>
    <string name="yName">月份</string>
</resources>

运行代码图:






运行完整代码:


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

    <declare-styleable name="LineChartView">
        <!-- 视图x轴距离边缘的距离-->
        <attr name="xChartMargin" format="dimension"/>
        <!-- 视图y轴距离边缘的距离-->
        <attr name="yChartMargin" format="dimension"/>
        <!-- 单位文字大小-->
        <attr name="chartValueTextSize" format="dimension"/>
        <!-- 每x轴单位需要多少px距离-->
        <attr name="xUnit" format="dimension"/>
        <!-- 每y轴单位需要多少px距离-->
        <attr name="yUnit" format="dimension"/>
        <!-- x轴峰值是多少单位-->
        <attr name="xMaxValue" format="float"/>
        <!-- y轴峰值是多少单位-->
        <attr name="yMaxValue" format="float"/>
        <!-- 折线图最少宽度是多少-->
        <attr name="minWith" format="dimension"/>
        <!-- 折线图最少高度是多少-->
        <attr name="minHeight" format="dimension"/>
        <!-- x 轴名字 -->
        <attr name="xName" format="string"/>
        <!-- y  轴名字 -->
        <attr name="yName" format="string"/>
    </declare-styleable>

</resources>

content_main.xml


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:orientation="vertical"
    android:layout_height="match_parent">
    <TextView
        android:layout_width="match_parent"
        android:text="折线图"
        android:layout_height="?attr/actionBarSize"
        android:background="#009900"
        android:textSize="18sp"
        android:textColor="#ffffff"
        android:gravity="center"
        android:textStyle="bold"
        />
    <ScrollView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">

        <HorizontalScrollView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content">

            <sam.android.utils.widget.LineChartView
                xmlns:app="http://schemas.android.com/apk/res-auto"
                android:id="@+id/lineChartView"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                app:chartValueTextSize="12sp"
                app:minHeight="100dp"
                app:minWith="100dp"
                app:xMaxValue="15"
                app:xUnit="16dp"
                app:xName="@string/xName"
                app:yName="@string/yName"
                app:yChartMargin="20dp"
                app:yMaxValue="12"
                app:yUnit="20dp" />

        </HorizontalScrollView>
    </ScrollView>
</LinearLayout>
<resources>
    <string name="xName">数目(个/月)</string>
    <string name="yName">月份</string>
</resources>

strings.xml


public class MainActivity extends AppCompatActivity {

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

        LineChartView lineChartView = (LineChartView) findViewById(R.id.lineChartView);
        Random random = new Random();
        int[] colors ={Color.RED, Color.BLACK, Color.BLUE, Color.CYAN, Color.GREEN};
        for(int i =  0; i <  5; i ++)
        {
            LineChartInfo info = new LineChartInfo();
            info.setColor(colors[i]);
            int x =0;
            int y =0;
            List<LineChartPoint> pointList = new ArrayList<>();
            for(int j =0 ; j < 4; j ++)
            {
                if(0 != j)
                {
                    x += random.nextInt(3)+1;//随机设置实际值
                    y += random.nextInt(3)+1;
                }
                LineChartPoint point = new LineChartPoint();
                point.setxValue(x);
                point.setyValue(y);
                pointList.add(point);//添加折线点
            }
            info.setPoints(pointList);//将对应的折线点添加到对应的折线信息上
            lineChartView.addLineCharInfo(info);
        }
        lineChartView.update();//刷新界面


    }


}

/**
 * 折线信息
 * @author  Sam
 */
public class LineChartInfo
{
    /**
     *    折线名字
     */
    private String name;


    /**
     * 折线颜色
     */
    private int color;

    /**
     * 折线所有的折线点
     */
    private List<LineChartPoint> points;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getColor() {
        return color;
    }

    public void setColor(int color) {
        this.color = color;
    }



    public List<LineChartPoint> getPoints() {
        return points;
    }

    public void setPoints(List<LineChartPoint> points) {
        this.points = points;
    }

}

/**
 * 折线点
 * @author  Sam
 */
public class LineChartPoint {

    /**
     * y坐标轴的实际值
     */
    private float yValue;

    /**
     * x坐标轴的实际值
     */
    private float xValue;

    public float getxValue() {
        return xValue;
    }





    public void setxValue(float xValue) {
        this.xValue = xValue;
    }

    public float getyValue() {
        return yValue;
    }

    public void setyValue(float yValue) {
        this.yValue = yValue;
    }
}


/**
 * 绘制折线图详情
 * @author  Sam
 */
public class LineChartView extends View

{
    /*
    **
    * x 坐标最大值的文字长度
    */
    private int maxXChartValueLen = 0;

    /**
     * y 坐标最大值的文字长度
     */
    private int maxYChartValueLen = 0;


    /**
     * x轴每一单位多少px, 默认为2px/单位
     */
    private float xUnit = 16;


    /**
     * y轴每一单位多少px,默认为10px/单位
     */
    private float yUnit = 16;
    /**
     * x 轴最大单位值(非x位置值,其x位置值等于maxXValue*xUnit), 默认为200个单位
     */
    private float maxXValue = 30;

    /**
     * y 轴最大单位值 (非y位置值,其y位置值等于maxYValue*yUnit).默认为300个单位
     */
    private float maxYValue = 12;


    /**
     * 折线图距离左边或右边多少距离,默认为100px
     */
    private float chartViewMarginX = 100;


    /**
     * 折线图距离上班边或下边多少距离, ,默认为100px
     */
    private float chartViewMarginY = 100;


    /**
     * 折线图x坐标名字
     */
    private String xName = "x";


    /**
     * 折线图y坐标名字
     */
    private String yName = "y";


    /**
     * 坐标轴字体大小,默认32px
     */
    private float coordinateValueTextSize = 32;

    /**
     * 坐标轴文字到坐标轴距离
     */
    private float charToCoordinateAxisDistance = 15;


    /**
     * 箭头到最大单位值的距离
     */
    private float arrowToTextDistance = 100;

    /**
     * 表示设置的视图最小宽度
     */
    private float configWith = 480;

    /**
     * 表示设置的视图最小高度
     */
    private float configHeigh = 800;

    private int lineNamesHeight;

    /**
     * y轴最大的c长度
     */
    float realYAxisLen;


    /**
     * x轴最大的长度
     */
    float realXAxisLen;

    /**
     * x坐标轴到底部距离
     */
    float marginXAxisDistance;


    /**
     * y坐标轴到左边距离
     */
    float marginYAxisDistance;

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);

        for(LineChartInfo lineChartInfo : lineChartInfos)
        {
            List<LineChartPoint> points = lineChartInfo.getPoints();
            for(LineChartPoint point : points)
            {
                maxXValue = Math.max(maxXValue, point.getxValue());
                maxYValue = Math.max(maxYValue, point.getyValue());
            }
        }

        if (MeasureSpec.AT_MOST == heightMode) {
            heightSize = (int) Math.min(heightSize, configHeigh);
        } else if (MeasureSpec.UNSPECIFIED == heightMode) {
            heightSize = (int) configHeigh;
        }

        if (MeasureSpec.AT_MOST == widthMode) {
            widthSize = (int) Math.min(widthSize, configWith);
        } else if (MeasureSpec.UNSPECIFIED == widthMode) {
            widthSize = (int) configWith;
        }


        //x轴名字 CharObj
        CharObj xNameCharObj = getCharObj(xName, coordinateValueTextSize);

        //y轴名字 CharObj
        CharObj yNameCharObj = getCharObj(yName, coordinateValueTextSize);

        // x 轴最大单位值 CharObj
        CharObj maxXValueCharObj = getCharObj(maxXValue + "", coordinateValueTextSize);

        // y 轴最大单位值 CharObj
        CharObj maxYValueCharObj = getCharObj(maxYValue + "", coordinateValueTextSize);

        //计算x坐标轴到底部距离 , 文字到坐标轴距离+ 文字高度+marginY+上方名字长度
        marginXAxisDistance = charToCoordinateAxisDistance + (xNameCharObj.heightLen > maxXValueCharObj.heightLen ? xNameCharObj.heightLen : maxXValueCharObj.heightLen) + chartViewMarginY + lineNamesHeight;

        //计算y坐标轴到左边距离 , 文字到坐标轴距离+ 文字宽度+marginX
        marginYAxisDistance = charToCoordinateAxisDistance + (yNameCharObj.withLen > maxYValueCharObj.withLen ? yNameCharObj.withLen : maxYValueCharObj.withLen) + chartViewMarginX;

        float maxYValuePX = maxYValue * yUnit; //最大y轴的单位值的实际位置


        //显示视图需要的高度
        int needHeight = (int) (configHeigh > maxYValuePX ? configHeigh : maxYValuePX ) + (int) (arrowToTextDistance+marginXAxisDistance * 2);

        float maxXValuePX = maxXValue * xUnit;//最大的x轴单位值的实际位置

        //显示视图需要的宽度
        int needWidth = (int) (configWith > maxXValuePX ? configWith : maxXValuePX ) + (int)(arrowToTextDistance+marginYAxisDistance * 2);

        heightSize = heightSize > needHeight ? heightSize : needHeight;
        widthSize = widthSize > needWidth ? widthSize : needWidth;
        realXAxisLen = widthSize - marginYAxisDistance * 2;
        realYAxisLen = heightSize - marginXAxisDistance * 2;
        super.setMeasuredDimension((int) (widthSize+xNameCharObj.withLen), heightSize);
    }

    private float originY;
    private float originX;
    /**
     * 绘制坐标轴
     */
    private void drawAxis(Canvas canvas) {
        //计算原点y坐标
        originY = marginXAxisDistance + realYAxisLen;
        //计算原点x坐标相对手机的实际位置 = 文字到y坐标轴距离+ 文字的宽度
        originX = marginYAxisDistance;
        Paint paint = new Paint();
        paint.setStrokeWidth(5);
        paint.setTextSize(coordinateValueTextSize);
        canvas.drawLine(originX, originY, originX, marginXAxisDistance, paint);//画出y轴
        canvas.drawLine(originX, originY, originX + realXAxisLen, originY, paint);//画出x轴
        canvas.drawLine(originX, marginXAxisDistance, originX - 12, marginXAxisDistance + 12, paint);//画出y轴左半箭头
        canvas.drawLine(originX, marginXAxisDistance, originX + 12, marginXAxisDistance + 12, paint);//画出y轴右半箭头
        canvas.drawLine(originX + realXAxisLen, originY, originX + realXAxisLen - 12, originY + 12, paint);//画出x轴上半箭头
        canvas.drawLine(originX + realXAxisLen, originY, originX + realXAxisLen - 12, originY - 12, paint);//画出x轴下半箭头

        Rect rect1 = new Rect();
        paint.setTextSize(coordinateValueTextSize+12);
        paint.getTextBounds(xName, 0, xName.length(), rect1);
        canvas.drawText(xName, originX + realXAxisLen-rect1.width()/4, originY + charToCoordinateAxisDistance + rect1.height(), paint);
        paint.getTextBounds(yName, 0, yName.length(), rect1);
        canvas.drawText(yName, originX +charToCoordinateAxisDistance+rect1.width()/2, marginXAxisDistance+ rect1.height()/2, paint);

        Paint oPaint = new Paint();
        oPaint.setStrokeWidth(2);

        for (int i = (int) xUnit, j = 1; i/xUnit <= maxXValue; i += xUnit, j++) {
            int o = 0; //标志物的高度
            if (0 == j % 10)
            {
                o = 20;

            }
            else if (0 == j % 5)
            {
                o = 15;
            }
            else
                o = 10;
            String value = "" + j;
            Rect rect = new Rect();
            paint.setTextSize(coordinateValueTextSize);
            paint.getTextBounds(value, 0, value.length(), rect);
            canvas.drawText("" + j, originX + i-rect.width()/2, originY+charToCoordinateAxisDistance+rect.height(), paint);
            canvas.drawLine(originX + i, originY, originX + i, originY - o, oPaint);
        }

        for (int i = (int) yUnit, j = 1; i/yUnit <= maxYValue; i += yUnit, j++) {
            int o = 0; //标志物的高度
            if (0 == j % 10)
            {
                o = 20;
            }
            else if (0 == j % 5)
            {
                o = 15;
            }
            else
                o = 10;

            String value = "" + j;
            Rect rect = new Rect();
            paint.setTextSize(coordinateValueTextSize);
            paint.getTextBounds(value, 0, value.length(), rect);
            canvas.drawText(value,originX-charToCoordinateAxisDistance-rect.width(), originY - i+rect.height()/2, paint);
            canvas.drawLine(originX, originY - i, originX + o, originY - i, oPaint);//画y标志物

        }

    }


    private List<LineChartInfo> lineChartInfos = new ArrayList<LineChartInfo>();

    public void addLineCharInfo(LineChartInfo info)
    {
        if(null != info)
            lineChartInfos.add(info);
    }

    //刷新界面
    public void update()
    {
        invalidate();
    }


    private void drawLineChartInfo(Canvas canvas)
    {
        if(lineChartInfos.isEmpty())
            return;

        for(LineChartInfo lineChartInfo : lineChartInfos)
        {
            List<LineChartPoint> points = lineChartInfo.getPoints();
            if(null == points || points.isEmpty())
                continue;
            Paint linePaint = new Paint();
            linePaint.setStrokeWidth(5);
            linePaint.setColor(lineChartInfo.getColor());
            Paint pointPaint = new Paint();
            pointPaint.setStrokeWidth(10);
            for(int i = 0; i < points.size()-1; i ++)
            {
                LineChartPoint point = points.get(i);
                LineChartPoint point2 = points.get(i+1);
                int x = (int) (originX+point.getxValue()*xUnit);
                int y = (int) (originY-point.getyValue()*yUnit);
                int x1 = (int) (originX+point2.getxValue()*xUnit);
                int y1 = (int) (originY-point2.getyValue()*yUnit);
                canvas.drawLine( x,y,x1,y1,linePaint);
                canvas.drawPoint(x1, y1, pointPaint);
            }
        }

    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        drawAxis(canvas);
        drawLineChartInfo(canvas);
    }

    /**
     * 获取文字会之后的CharObj
     *
     * @param value 文字
     * @param textSize 文字大小
     * @return CharObj
     */
    private CharObj getCharObj(String value, float textSize) {
        Paint paint = new Paint();
        paint.setTextSize(textSize);
        Rect rect = new Rect();
        paint.getTextBounds(value, 0, value.length(), rect);
        return new CharObj(rect.width(), rect.height());
    }

    /**
     * 记录绘制文字后,其文字高度,宽度。
     */
    class CharObj {
        float withLen;//测量文字的实际宽度
        float heightLen;//测量文字的实际高度

        CharObj(float withLen, float heightLen) {
            this.withLen = withLen;
            this.heightLen = heightLen;
        }
    }

    public LineChartView(Context context) {
        super(context);
    }

    public LineChartView(Context context, AttributeSet attrs) {
        super(context, attrs);
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.LineChartView);
        xUnit  = typedArray.getDimension( R.styleable.LineChartView_xUnit, xUnit);
        yUnit  = typedArray.getDimension( R.styleable.LineChartView_yUnit, yUnit);
        chartViewMarginX =  typedArray.getDimension( R.styleable.LineChartView_xChartMargin, marginXAxisDistance);
        chartViewMarginY = typedArray.getDimension( R.styleable.LineChartView_yChartMargin, marginYAxisDistance);
        xName = typedArray.getString( R.styleable.LineChartView_xName);
        xName = null == xName?"x":xName;
        yName = typedArray.getString( R.styleable.LineChartView_yName);
        yName = null == yName?"y":yName;
        maxXValue = typedArray.getFloat(R.styleable.LineChartView_xMaxValue, maxXValue);
        maxYValue = typedArray.getFloat(R.styleable.LineChartView_yMaxValue, maxYValue);
        coordinateValueTextSize = typedArray.getDimension(R.styleable.LineChartView_chartValueTextSize,coordinateValueTextSize );
        configWith = typedArray.getDimension(R.styleable.LineChartView_minWith,configWith );
        configHeigh = typedArray.getDimension(R.styleable.LineChartView_minHeight,configHeigh );
    }


}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值