版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_27948659/article/details/78667003
写在前面
(平时开发遇到的问题和解决思路)
项目需求:采集过的脉象图,可以左右滑动,类似查看历史波形,我这里采用 的办法是用HorScrollView 嵌套View,讲view的宽度设置宽一点,监听用户手势,来刷新波形。因为公司波形采集器,一秒数据在 1K,而手机一屏幕需要装下4秒的数据,所以刷新view 也需要局部刷新,不然手机cpu 会吃不消。
HorScrollView 接口
我们需要根绝 HorScrollView 滑动的距离来刷新指定位置的,所以我们接口只需要 一个,参数位 int类型的 index。
public interface EcgScrollInterface {
void onEcgScroll(int index);
}
自定义HorScrollview
由于项目中我们只需要关注HorScrollView滑动的距离。所以除了集成构造方法外,重写onScrollChanged() 方法
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
if ((this.mNew != l) && (this.mOld != oldl)) {
this.mNew = l;
this.mOld = oldl;
if (ecgScrollInterface != null) {
this.ecgScrollInterface.onEcgScroll(this.mNew);
}
}
super.onScrollChanged(l, t, oldl, oldt);
}
接下来就是我们的重头戏。
自定义view
public void setmWidth(int listSize) {
DisplayMetrics dm = getResources().getDisplayMetrics();
int height = dm.heightPixels;
this.mHeight = height;
baseHeight = mHeight * (1f / 2f);
sHeight = mHeight * (1f / 15f);
if (listSize >= screenSize && ecgXOffset != 0) {
this.mWidth = posToPx(listSize);
}
measure(floatToInt(mWidth), height);
}
左右可以拖动的脉象图,这里我们就需要设置view的宽度超过屏幕,这样的话我们就可以使HorScrollView 左右滑动。首先我们讲需要展示的一屏幕数据传入View中。
public void setWareData(List<Float> wareData, int index) {
this.index = posToPx(index);
WareData.clear();
WareData = new LinkedList<>();
WareData.addAll(wareData);
this.curPos.x = this.mInvalidRect.left;
EcgPath.reset();
for (int i = 0; i < WareData.size() - 1; i += 2) {
if (i == 0) {
EcgPath.moveTo(this.curPos.x, baseHeight - (WareData.get(i) * sHeight / EcgView.ecgSensitivity));
} else {
EcgPath.lineTo(this.curPos.x + (i * ecgXOffset),
baseHeight - (WareData.get(i) * sHeight / EcgView.ecgSensitivity));
}
}
invalidate(this.index, 0, this.index + getWidth() , getHeight());
}
这里 我们先将数据都绘制再 Path上,并且通过invalidate()方法,刷新指定区域内容。(局部刷新防止view 再绘制的时候由于绘制的点过多,到时绘制时间较长,超过17ms,和cpu消耗)
@Override
protected void onDraw(Canvas canvas) {
canvas.drawPath(EcgPath, mPaint);
super.onDraw(canvas);
}
由于我们之前已经讲波形绘制再 Path上,这里我们只需要再绘制方法中,将Path绘制再画布上。
数据源设置
wareFromView.setmWidth(testFloats.size());
LinearLayout.LayoutParams params1 = (LinearLayout.LayoutParams) wareFromView.getLayoutParams();
params1.width = wareFromView.posToPx(testFloats.size());
wareFromView.setLayoutParams(params1);
if (testFloats.size() > ConstantUtil.ONE_SCREEN_SIZE) {
wareFromView.setWareData(testFloats.subList(0, ConstantUtil.ONE_SCREEN_SIZE), 0);
} else {
wareFromView.setWareData(testFloats, 0);
}
默认加载第一屏幕数据
ecgHorScrollView.setEcgScrollInterface(new EcgScrollInterface() {
@Override
public void onEcgScroll(int index) {
if (index < 0) {
index = 0;
}
index = wareFromView.pxToPos(index);
// 最后一屏幕数据
if (index >= testFloats.size() - ConstantUtil.ONE_SCREEN_SIZE) {
index = testFloats.size() - ConstantUtil.ONE_SCREEN_SIZE;
}
if (index < testFloats.size() - ConstantUtil.ONE_SCREEN_SIZE) {
startIndex = index;
endIndex = index + ConstantUtil.ONE_SCREEN_SIZE;
wareFromView.setWareData(testFloats.subList(startIndex, endIndex), index);
}
}
});
我们实现 HorScrollview 中的接口方法,根绝接口传过来的位置偏移量,我们计算需要展示当前的数据。
最后附上 自定义view 全部代码:
public class WareFromView extends View {
private static final String TAG = "WareFromView";
// 数据
private List<Float> WareData = new LinkedList<>();
public float mWidth; // 控件宽
private float mHeight; // 控件高
private Paint mPaint = new Paint();
private int startIndex, endIndex = 0;//开始起点后结束起点
private float screenSize = 4000.0F;
private float dataSize; // 数据个数
public static float ecgXOffset; // 真实间隔
private int index;
private float baseHeight;
private float sHeight;
private Path EcgPath;
private Rect mInvalidRect = new Rect();
private PointF curPos;
private int wareWidth; // 一屏幕宽度
public WareFromView(Context context) {
super(context);
}
public WareFromView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
EcgPath = new Path();
mPaint = new Paint();
mPaint.setColor(Color.WHITE);
mPaint.setStrokeWidth(3.0F);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setAntiAlias(true);
this.curPos = new PointF(0.0F, 0.0F);
}
public WareFromView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public WareFromView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
DisplayMetrics dm = getResources().getDisplayMetrics();
int width = dm.widthPixels;
int high = dm.heightPixels;
float widthSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_MM, wave_speed, dm); // 25mm --> px
widthStart = (width % widthSize) / 2;
wareWidth = floatToInt(width - (widthStart * 2));
if (mWidth == 0.0) {
mWidth = width;
}
float highSize;
if (high / widthSize >= 3) {
highSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_MM, wave_speed, dm);
highStart = (high % highSize) / 2;
mHeight = (int) (high - highStart);
} else {
highStart = high % 3;
high = (int) (high - highStart);
highSize = high / 15;
mHeight = (int) (high - highStart);
}
float size = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_MM, wave_speed, dm); // 25mm --> px
ecgXOffset = size / 1000f;
setMeasuredDimension(floatToInt(mWidth), floatToInt(mHeight));
}
@Override
protected void onDraw(Canvas canvas) {
canvas.drawPath(EcgPath, mPaint);
super.onDraw(canvas);
}
public void setWareData(List<Float> wareData, int index) {
this.index = posToPx(index);
WareData.clear();
WareData = new LinkedList<>();
WareData.addAll(wareData);
this.curPos.x = this.mInvalidRect.left;
EcgPath.reset();
for (int i = 0; i < WareData.size() - 1; i += 2) {
if (i == 0) {
EcgPath.moveTo(this.curPos.x, baseHeight - (WareData.get(i) * sHeight / EcgView.ecgSensitivity));
} else {
EcgPath.lineTo(this.curPos.x + (i * ecgXOffset),
baseHeight - (WareData.get(i) * sHeight / EcgView.ecgSensitivity));
}
}
invalidate(this.index, 0, this.index + getWidth(), getHeight());
}
public void setmWidth(int listSize) {
DisplayMetrics dm = getResources().getDisplayMetrics();
int height = dm.heightPixels;
this.mHeight = height;
baseHeight = mHeight * (1f / 2f);
sHeight = mHeight * (1f / 15f);
if (listSize >= screenSize && ecgXOffset != 0) {
this.mWidth = posToPx(listSize);
}
measure(floatToInt(mWidth), height);
}
public void clearData() {
WareData.clear();
}
private int floatToInt(float f) {
int i = 0;
if (f > 0) {
i = (int) ((f * 10 + 5) / 10);
} else if (f < 0) {
i = (int) ((f * 10 - 5) / 10);
} else i = 0;
return i;
}
public void invalidate(int paramInt1, int paramInt2, int paramInt3, int paramInt4) {
this.mInvalidRect.set(paramInt1, paramInt2, paramInt3, paramInt4);
super.invalidate(paramInt1, paramInt2, paramInt3, paramInt4);
}
public int posToPx(int paramInt) {
return Math.round(1f * wareWidth / ConstantUtil.ONE_SCREEN_SIZE * paramInt);
}
public int pxToPos(int paramInt) {
return Math.round(1f * ConstantUtil.ONE_SCREEN_SIZE / wareWidth * paramInt);
}
}
最后
本人小白,希望大佬们 看到有什么问题或者瑕疵,麻烦评论一下。谢谢。加油~