Android开发记录(7)-自定义百分比动态折线图
2019-11-20 本文已影响0人
LH_1994
要求实现一个折线(曲线)图来查看android设备的内存和CPU占用情况。
场景主要用于设备测试和实时监控。
先看下实现效果
效果图.gif这里用的是随机生成数据演示,当然实际上CPU和内存使用情况不会如此大幅度变化。
实现过程
一、自定义属性
自定义属性的好处主要体现在适配不同分辨率的设备,因为自定义View的绘画单位是px,而我们指定设置dp值转为px就能让视图在不同设备中展示出一样的效果。
在values目录下新建attrs.xml文件,自定义属性如下:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="LineChartView">
<attr name="XPoint" format="reference|dimension"/>
<attr name="XScale" format="reference|dimension"/>
<attr name="YScale" format="reference|dimension"/>
<attr name="XLength" format="reference|dimension"/>
<attr name="YLength" format="reference|dimension"/>
</declare-styleable>
</resources>
二、自定义view
直接上代码
import java.util.ArrayList;
import java.util.List;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.view.View;
public class LineChartView extends View{
private Paint paint;
private float XPoint = 0;
private float XScale = 0;
private float YScale = 0;
private float XLength = 0;
private float YLength = 0;
private float DEFAULT_XPoint = 50;
private float DEFAULT_XScale = 8;
private float DEFAULT_YScale = 60;
private float DEFAULT_XLength = 320;
private float DEFAULT_YLength = 300;
private int MaxDataSize = 0 ;
//内存数据
private List<Float> dataMemory = new ArrayList<Float>();
//CPU数据
private List<Float> dataCPU = new ArrayList<Float>();
//刻度
private String[] YLabel = new String[]{"0","25","50","75","100"};
private Handler handler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
// TODO Auto-generated method stub
switch (msg.what) {
case 1:
LineChartView.this.invalidate();
}
return false;
}
});
public LineChartView(Context context) {
super(context);
init();
}
public LineChartView(Context context, AttributeSet attrs) {
super(context, attrs);
initAttr(context, attrs);
init();
}
public LineChartView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initAttr(context, attrs);
init();
}
/**
* 获得属性值
* @param context
* @param attrs
*/
private void initAttr(Context context, AttributeSet attrs) {
if (attrs == null) {
return;
}
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.LineChartView);
int n = array.getIndexCount();
for (int i = 0; i < n; i++) {
int attr = array.getIndex(i);
if (attr == R.styleable.LineChartView_XPoint) {
XPoint = array.getDimension(attr, dp2px(context, DEFAULT_XPoint));
} else if (attr == R.styleable.LineChartView_XScale) {
XScale = array.getDimension(attr, dp2px(context, DEFAULT_XScale));
} else if (attr == R.styleable.LineChartView_YScale) {
YScale = array.getDimension(attr, dp2px(context, DEFAULT_YScale));
}else if (attr == R.styleable.LineChartView_XLength) {
XLength = array.getDimension(attr, dp2px(context, DEFAULT_XLength));
}else if (attr == R.styleable.LineChartView_YLength) {
YLength = array.getDimension(attr, dp2px(context, DEFAULT_YLength));
}
}
array.recycle();
}
private void init(){
paint = new Paint();
paint.setStyle(Paint.Style.STROKE);
paint.setAntiAlias(true);
//刻度
YLabel[0]="0";
YLabel[1]="25";
YLabel[2]="50";
YLabel[3]="75";
YLabel[4]="100";
//计算最大数据数
MaxDataSize = Math.round(XLength / XScale);
}
/**
* 更新内存、CPU数据
* @param newDataMemory
* @param newDataCPU
*/
public void setNewData(Float newDataMemory,Float newDataCPU){
//memory
if(dataMemory.size() >= MaxDataSize){
dataMemory.remove(0);
}
dataMemory.add(newDataMemory);
//cpu
if(dataCPU.size() >= MaxDataSize){
dataCPU.remove(0);
}
dataCPU.add(newDataCPU);
//update
handler.sendEmptyMessage(1);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
paint.setColor(Color.BLUE);
//画Y轴
canvas.drawLine(XPoint, 0, XPoint, YLength, paint);
//Y轴箭头
canvas.drawLine(XPoint, 0, XPoint - 3, 6, paint); //箭头
canvas.drawLine(XPoint, 0, XPoint + 3, 6 ,paint);
//添加刻度和文字
for(int i=0; i * YScale < YLength; i++) {
canvas.drawLine(XPoint, YLength - i * YScale, XPoint + 5, YLength - i * YScale, paint); //刻度
canvas.drawText(YLabel[i], XPoint - 30, YLength - i * YScale, paint);//文字
}
//画X轴
canvas.drawLine(XPoint, YLength, XPoint + XLength, YLength, paint);
//画标识
paint.setColor(Color.RED);
canvas.drawText("内存百分比", XLength, 15, paint);//文字
canvas.drawLine(XLength-20, 15, XLength-10, 15, paint);
paint.setColor(Color.MAGENTA);
canvas.drawText("CPU百分比", XLength, 30, paint);//文字
canvas.drawLine(XLength-20, 30, XLength-10, 30, paint);
//画内存数据
paint.setColor(Color.RED);
if(dataMemory.size() > 1){
for(int i=1; i<dataMemory.size(); i++){
canvas.drawLine(XPoint + (i-1) * XScale, YLength - dataMemory.get(i-1) * (YScale*4),
XPoint + i * XScale, YLength - dataMemory.get(i) * (YScale*4), paint);
}
}
//画CPU数据
paint.setColor(Color.MAGENTA);
if(dataCPU.size() > 1){
for(int i=1; i<dataCPU.size(); i++){
canvas.drawLine(XPoint + (i-1) * XScale, YLength - dataCPU.get(i-1) * (YScale*4),
XPoint + i * XScale, YLength - dataCPU.get(i) * (YScale*4), paint);
}
}
}
/**
* sp2px
* @param context
* @param spValue
* @return
*/
private int sp2px(Context context, float spValue) {
final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
return (int) (spValue * fontScale + 0.5f);
}
/**
* dp2px
* @param context
* @param dpValue
* @return
*/
private int dp2px(Context context, float dpValue) {
final float densityScale = context.getResources().getDisplayMetrics().density;
return (int) (dpValue * densityScale + 0.5f);
}
}
具体的注释应该还算清晰。
三、XML中使用
<com.components.LineChartView
android:id="@+id/linechart"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:XPoint="50dp"
app:XScale="10dp"
app:YScale="40dp"
app:XLength="200dp"
app:YLength="200dp"
>
</com.components.LineChartView>
四、Activity中使用
请根据实际使用填充数据,这里只以演示效果2秒随机生成数值填充。如效果图所示。
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import com.components.LineChartView;
import butterknife.BindView;
import butterknife.ButterKnife;
public class TestActivity extends AppCompatActivity {
@BindView(R.id.linechart)
LineChartView linechart;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
ButterKnife.bind(this);
new Thread(){
public void run(){
while (true){
try {
Thread.sleep(2000);
linechart.setNewData((float)Math.random(),(float)Math.random());
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
}.start();
}
}