安卓自定义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哈哈~

人已赞赏
Android文章

Android RTMP推流之MediaCodec硬编码一(H.264进行flv封装)

2020-3-18 17:35:36

Android文章

android自定义View之从入门到放弃(二)实现评分控件 记录学习

2020-3-19 9:35:51

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
有新消息 消息中心
搜索