先上图
分析
x轴
x轴起始x坐标 = 原点x坐标
x轴起始y坐标 = 原点y坐标
x轴终止x坐标 = 原点x坐标 + x轴长度 + j
x轴终止y坐标 = 原点y坐标
代码
canvas.drawLine(mXPoint,mYPoint,mXLength + mXPoint + 20,mYPoint,mainPaint);
x轴刻度
可以根据传入数据的量循环画刻度
线条 a 起始x坐标 = 原点x坐标 + 循环数 × x轴刻度长度
线条 a 起始y坐标 = 原点y坐标
线条 a 终止x坐标 = 原点x坐标 + 循环数 × x轴刻度长度
线条 a 终止y坐标 = 原点y坐标 - 5 (5 是刻度的高度,可以自己设置)
线条 a 刻度文字x坐标 = 原点x坐标 + 循环数 × x轴刻度长度
线条 a 刻度文字y坐标 = 原点y坐标 +15(15 是文字向下移动的距离,可以自己设置)
代码
for (int i=0;i<mXAxialScale.length;i++){
canvas.drawLine(mXPoint + i * mXScale,mYPoint,mXPoint + i * mXScale,mYPoint-5,mainPaint);
canvas.drawText(mXAxialScale[i],mXPoint + i * mXScale,mYPoint + 15,textPaint);
}
x轴最后的箭头
上箭头
起始x坐标 = 原点x坐标 + x轴长度 + j
起始y坐标 = 原点y坐标
终止x坐标 = 原点x坐标 + x轴长度 + 15(个人设置为15,可以自己设置)
终止y坐标 = 原点y坐标 - 5(个人设置为5,可以自己设置)
下箭头
起始x坐标 = 原点x坐标 + x轴长度 + j
起始y坐标 = 原点y坐标
终止x坐标 = 原点x坐标 + x轴长度 + 15(个人设置为15,可以自己设置)
终止y坐标 = 原点y坐标 + 5(个人设置为5,可以自己设置)
代码
//x上箭头
canvas.drawLine(mXLength + mXPoint + 20,mYPoint,mXLength + mXPoint + 15,mYPoint - 5,mainPaint);
//x下箭头
canvas.drawLine(mXLength + mXPoint + 20,mYPoint,mXPoint + mXLength + 15,mYPoint + 5,mainPaint);
y轴
y轴起始x坐标 = 原点x坐标
y轴起始y坐标 = 原点y坐标
y轴终止x坐标 = 原点x坐标
y轴终止y坐标 = 原点y坐标 - y轴长度 - i
代码
canvas.drawLine(mXPoint,mYPoint,mXPoint,mYPoint-mYLength-20,mainPaint);
y轴刻度
和x轴差不多,根据传入数据的量循环画即可
线条 b 起始x坐标 = 原点x坐标
线条 b 起始y坐标 = 原点y坐标 - 循环数 × y轴刻度长度
线条 b 终止x坐标 = 原点x坐标 + 5(5 是刻度的宽度,可以自己设置)
线条 b 终止y坐标 = 原点y坐标 - 循环数 × y轴刻度长度
线条 b 刻度文字x坐标 = 原点x坐标 - 20(20 是文字向左移动的距离,可以自己设置)
线条 b 刻度文字y坐标 = 原点y坐标 - 循环数 × y轴刻度长度
代码
for (int i=0;i<mYAxialScale.length;i++){
canvas.drawLine(mXPoint,mYPoint - i * mYScale,mXPoint + 5,mYPoint - i * mYScale,mainPaint);
canvas.drawText(mYAxialScale[i],mXPoint - 20,mYPoint - i * mYScale,textPaint);
}
y轴上面的箭头
左箭头
起始x坐标 = 原点x坐标
起始y坐标 = 原点y坐标 - y轴长度 - i
终止x坐标 = 原点x坐标 - 5(个人设置为5,可以自己设置)
终止y坐标 = 原点y坐标 - y轴长度 - 15(个人设置为15,可以自己设置)
右箭头
起始x坐标 = 原点x坐标
起始y坐标 = 原点y坐标 - y轴长度 - i
终止x坐标 = 原点x坐标 + 5(个人设置为5,可以自己设置)
终止y坐标 = 原点y坐标 - y轴长度 - 15(个人设置为15,可以自己设置)
代码
//y左箭头
canvas.drawLine(mXPoint,mYPoint - mYLength - 20,mXPoint - 5,mYPoint - mYLength - 15,mainPaint);
//y右箭头
canvas.drawLine(mXPoint,mYPoint - mYLength - 20,mXPoint + 5,mYPoint - mYLength - 15,mainPaint);
画标题
位置随意,不超出屏幕,不遮挡数据就行…
代码
canvas.drawText(mTitle, (mXPoint+mXLength+100)/2, mYPoint+100, textPaint);
画点、点与点之间的折线
循环画即可,直接上代码
for (int i=0;i<mData.length;i++){
if (i>0){
//原点坐标+第一个刻度,y坐标,原点+第二个刻度,y坐标
canvas.drawLine(mXPoint + (i - 1) * mXScale,getY(mData[i - 1]),mXPoint + i * mXScale,getY(mData[i]),mainPaint);
}
//画点
canvas.drawCircle(mXPoint + i * mXScale,getY(mData[i]),5,pointPaint);
canvas.drawText(mData[i],mXPoint + i * mXScale+10,getY(mData[i]),indexPaint);
}
获取折线的y坐标
private float getY(String y){
return mYPoint - Float.valueOf(y) * mYScale / mStep;
}
坐标原点可以自己设置,y轴和x轴长度也可以自己设置,只要不超过手机屏幕宽度和高度就可以
x轴和y轴刻长度可以设置为固定值,也可以通过传入数据的数量动态设置,但如果数量过多,刻度与刻度之间会比较拥挤,其他就是上面的内容了
全部代码
AndroidManifest.xml 设置横屏
<activity android:name=".MainActivity"
android:screenOrientation="landscape">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
自定义布局
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.iot.nss.customchart.ChartView
android:id="@+id/chart_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<Button
android:id="@+id/update_btn"
android:layout_alignParentRight="true"
android:text="更新数据"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</RelativeLayout>
ChartView.java
public class ChartView extends View {
private Paint mainPaint;//主画笔
private Paint pointPaint;//点
private Paint indexPaint;//坐标
private Paint textPaint;//文字
private int mXPoint = 150;//原点X轴坐标
private int mYPoint = 700;//原点Y轴坐标
private int mXScale;//X轴刻度长度
private int mYScale;//Y轴刻度长度
private int mXLength;//X轴长度
private int mYLength;//Y轴长度
private String[] mXAxialScale;//X轴刻度值
private String[] mYAxialScale;//Y轴刻度值
private String[] mData;//数据
private String mTitle;//标题
private int mStep;//y轴步增值
public void setInfo(String[] mXAxialScale, String[] mYAxialScale,String strTitle,int mStep) {
this.mXAxialScale = mXAxialScale;//横坐标
this.mYAxialScale = mYAxialScale;//纵坐标
this.mTitle = strTitle;
this.mStep = mStep;
}
public void setData(String[]data){
this.mData = data;
postInvalidate();//更新
}
//一定要写这个构造不然会报错 这个构造会在创建控件时自动调用
public ChartView(Context context, AttributeSet attrs) {
super(context, attrs);
initPaint();
}
private void initPaint() {
mainPaint = new Paint();
mainPaint.setStrokeWidth(2);//线宽
mainPaint.setAntiAlias(true);//去锯齿
mainPaint.setColor(Color.DKGRAY);//颜色
mainPaint.setTextSize(12);
//文字
textPaint = new Paint();
textPaint.setAntiAlias(true);
textPaint.setColor(Color.DKGRAY);
textPaint.setTextSize(12);
//坐标
indexPaint = new Paint();
indexPaint.setAntiAlias(true);
indexPaint.setColor(Color.RED);
indexPaint.setTextSize(12);
//点
pointPaint = new Paint();
pointPaint.setAntiAlias(true);
pointPaint.setStyle(Paint.Style.FILL);//实心
textPaint.setTextSize(16);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
getXScale();
//画x
canvas.drawLine(mXPoint,mYPoint,mXLength + mXPoint + 20,mYPoint,mainPaint);
//显示刻度
for (int i=0;i<mXAxialScale.length;i++){
canvas.drawLine(mXPoint + i * mXScale,mYPoint,mXPoint + i * mXScale,mYPoint-5,mainPaint);
canvas.drawText(mXAxialScale[i],mXPoint + i * mXScale,mYPoint + 15,textPaint);
}
//x上箭头
canvas.drawLine(mXLength + mXPoint + 20,mYPoint,mXLength + mXPoint + 15,mYPoint - 5,mainPaint);
//x下箭头
canvas.drawLine(mXLength + mXPoint + 20,mYPoint,mXPoint + mXLength + 15,mYPoint + 5,mainPaint);
//画y
canvas.drawLine(mXPoint,mYPoint,mXPoint,mYPoint-mYLength-20,mainPaint);
//显示刻度
for (int i=0;i<mYAxialScale.length;i++){
canvas.drawLine(mXPoint,mYPoint - i * mYScale,mXPoint + 5,mYPoint - i * mYScale,mainPaint);
canvas.drawText(mYAxialScale[i],mXPoint - 20,mYPoint - i * mYScale,textPaint);
}
//y左箭头
canvas.drawLine(mXPoint,mYPoint - mYLength - 20,mXPoint - 5,mYPoint - mYLength - 15,mainPaint);
//y右箭头
canvas.drawLine(mXPoint,mYPoint - mYLength - 20,mXPoint + 5,mYPoint - mYLength - 15,mainPaint);
//折线
for (int i=0;i<mData.length;i++){
if (i>0){
//原点坐标+第一个刻度,y坐标,原点+第二个刻度,y坐标
canvas.drawLine(mXPoint + (i - 1) * mXScale,getY(mData[i - 1]),mXPoint + i * mXScale,getY(mData[i]),mainPaint);
}
//画点
canvas.drawCircle(mXPoint + i * mXScale,getY(mData[i]),5,pointPaint);
canvas.drawText(mData[i],mXPoint + i * mXScale+10,getY(mData[i]),indexPaint);
}
//设置标题
canvas.drawText(mTitle, (mXPoint + mXLength + 100)/2, mYPoint + 100, textPaint);
}
//获取y坐标
private float getY(String y){
return mYPoint - Float.valueOf(y) * mYScale / mStep;
}
//设置x和y轴长度以及刻度长度
private void getXScale(){
mXLength = getWidth() - 200;
mYLength = getHeight() - 400;
mXScale = mXLength/(mXAxialScale.length - 1);
mYScale = mYLength/(mYAxialScale.length - 1);
}
}
主界面使用
private ChartView chartView;
private String[] xScale = {"0","1","2","3","4","5","6","7","8","9"};
private String[] yScale = {"0", "2", "4", "6", "8", "10", "12", "14", "16", "18", "20"};
private String[] data = new String[10];
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
chartView = findViewById(R.id.chart_view);
Button updateBtn = findViewById(R.id.update_btn);
chartView.setInfo(xScale,yScale,"测试",2);
updateData();
updateBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
updateData();
}
});
}
private void updateData(){
for (int i=0;i<data.length;i++){
data[i] = String.valueOf(new Random().nextInt(20));
}
chartView.setData(data);
}