自定义View 实现左右拖动脉象图

版权声明:本文为博主原创文章,未经博主允许不得转载。 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);
    }
}

最后

本人小白,希望大佬们 看到有什么问题或者瑕疵,麻烦评论一下。谢谢。加油~

猜你喜欢

转载自blog.csdn.net/qq_27948659/article/details/78667003