android 自定义view调用invalidate有时候保留上次绘制的线

如题,本人自定义一个折线图,应为数据接口合不上就弄了一个适配器模式匹配数据,但是多次跟新数据重新设置适配器时候,有时发现折线图会出现上一次数据绘制的一根线,代码如下:

        @Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    getPaint().reset();
    /**
     * 外框线
     */
    // 设置颜色
    getPaint().setColor(substrateColor);
    // 设置宽度
    getPaint().setStrokeWidth(2);
    // 线的坐标点 (四个为一条线)
    float[] pts = {LEFTUPX, LEFTUPY - 20, LEFTUPX, LEFTDOWNY, LEFTUPX, LEFTDOWNY, RIGHTDOWNX + 20, LEFTDOWNY};
    // 画线
    canvas.drawLines(pts, getPaint());

    /**
     * 箭头
     */
    // 通过路径画三角形
    Path path = new Path();
    getPaint().setStyle(Paint.Style.FILL);// 设置为空心
    path.moveTo(LEFTUPX - 5, LEFTUPY - 20);// 此点为多边形的起点
    path.lineTo(LEFTUPX + 5, LEFTUPY - 20);
    path.lineTo(LEFTUPX, LEFTUPY - 35);
    path.close(); // 使这些点构成封闭的多边形
    canvas.drawPath(path, getPaint());
    // 第二个箭头
    path.moveTo(RIGHTDOWNX + 20, LEFTDOWNY - 5);// 此点为多边形的起点
    path.lineTo(RIGHTDOWNX + 20, LEFTDOWNY + 5);
    path.lineTo(RIGHTDOWNX + 35, LEFTDOWNY);
    canvas.drawPath(path, getPaint());

    /**
     *  中间虚线
     */
    float[] pts2 = new float[(updownlines + leftrightlines) * 4];
    // 计算位置y轴标题
    if (updownlines != 0) {
        for (int i = 0; i < updownlines; i++) {

            float x1 = LEFTUPX;
            float y1 = LEFTDOWNY - (i + 1) * UPDOWNSPACE;
            float x2 = RIGHTDOWNX;
            float y2 = LEFTDOWNY - (i + 1) * UPDOWNSPACE;
            pts2[i * 4 + 0] = x1;
            pts2[i * 4 + 1] = y1;
            pts2[i * 4 + 2] = x2;
            pts2[i * 4 + 3] = y2;
            getPaint().setColor(Color.BLACK);
            getPaint().setTextSize(25);
            if (showRule != null) {
                if (showRule.showVerticalTitle(i))
                    canvas.drawText(String.valueOf(verticalTitle[i]), x1 + 5, y1 + 10, getPaint(titleColor));
            } else {
                canvas.drawText(String.valueOf(i), x1 + 5, y1 + 10, getPaint(titleColor));
            }
        }
    }

    // 计算位置x轴上标题
    if (leftrightlines != 0) {
        for (int i = 0; i < leftrightlines; i++) {
            float x1 = LEFTUPX + (i + 1) * LEFTRIGHTSPACE;
            float y1 = LEFTUPY;
            float x2 = LEFTUPX + (i + 1) * LEFTRIGHTSPACE;
            float y2 = LEFTDOWNY;
            pts2[(i + updownlines) * 4 + 0] = x1;
            pts2[(i + updownlines) * 4 + 1] = y1;
            pts2[(i + updownlines) * 4 + 2] = x2;
            pts2[(i + updownlines) * 4 + 3] = y2;
            if (showRule != null) {
                if (showRule.showLandsTitle(i)) {
                    Paint pen = getPaint();
                    if (showRule.markLandsTitle(String.valueOf(lanspaceTitle[i]))) {
                        pen.setColor(markTitleColor);
                        Typeface font = Typeface.create(Typeface.SANS_SERIF, Typeface.BOLD);
                        pen.setTypeface(font);

                    } else {
                        pen.setColor(titleColor);
                        Typeface font = Typeface.create(Typeface.SANS_SERIF, Typeface.NORMAL);
                        pen.setTypeface(font);
                    }
                    canvas.drawText(String.valueOf(lanspaceTitle[i]), x2 - 10, y2 + 30, pen);
                }
            } else {
                canvas.drawText(String.valueOf(i), x2 - 10, y2 + 30, getPaint(titleColor));
            }
        }
        if (showRule != null && !TextUtils.isEmpty(showRule.landsUnit())) {
            float[] widths = new float[1];
            float width = 0;
            for (int j = 0; j < (String.valueOf(lanspaceTitle[lanspaceTitle.length - 1])).length(); j++) {
                getPaint().getTextWidths(String.valueOf((String.valueOf(lanspaceTitle[lanspaceTitle.length - 1]).charAt(j))), widths);
                width += widths[0];
            }
            canvas.drawText(showRule.landsUnit(), LEFTUPX + (leftrightlines) * LEFTRIGHTSPACE + width, LEFTDOWNY + 30, getPaint(titleColor));
        }

    }

    getPaint().setColor(imaginaryLineColor);
    getPaint().setStrokeWidth(1);
    if (showRule != null && showRule.showImaginaryLine())
        canvas.drawLines(pts2, getPaint());
    getPaint().setStrokeWidth(2);
    if (showRule != null) {
             if (intervalTime == 0) {
                 number = 50;
                 count = dataSize - 1;
                 //一定要先画矮的那根线,否则会被遮盖
                 if (/*listY2.size() > 0*/list.size() > 0)
                         canvasDataLines(canvas, 1, firstLineColor, firstBelowColor);

                 if (list.size() > 0)
                        canvasDataLines(canvas, 0, secondLineColor, secondBelowColor);
                 isFinish = true;   
             } else {
                 if (list.size() > 0)
                     canvasDataLines(canvas, 1, firstLineColor, firstBelowColor);
                 if (list.size() > 0)
                     canvasDataLines(canvas, 0, secondLineColor, secondBelowColor);
                 handler.sendEmptyMessage(1);
             }
        }

        if (clickPoint != null) {
            Log.e("Click", "postInvalidate--->>>onDraw");
            //点击后的浮现效果出现
            selectedView.setPointf(clickPoint.getLine() == PointBean.THEFIRSTLINE ? clickPoint.getUppointF() : clickPoint.getDownPoint())
                    .setAlterBgColor(clickPoint.getLine() == PointBean.THEFIRSTLINE ? clickSecondLineBgColor : clickFirstLineBgColor)
                    .setFontColor(clickPoint.getLine() == PointBean.THEFIRSTLINE ? clickFirstLineFontColor : clickSecondLineFontColor)
                    .setCircleColor(clickPoint.getLine() == PointBean.THEFIRSTLINE ? clickSecondLineCircleColor : clickFirstLineCircleColor)
                    .setTextValue(clickPoint.getLine() == PointBean.THEFIRSTLINE ? clickPoint.getText() : clickPoint.getText2())
                    .setMargin(selectedView.getCrossBorder()?-50:30)
                    .draw(canvas);
            //如果需要这个浮现效果自动消失就用一个handler将clickPoint制空并且重新绘制就好了
        }


}

/**
 * 画折线
 *
 * @param canvas
 * @param listY
 * @param color
 * @param endColor
 */
private synchronized void canvasDataLines(Canvas canvas, int flag, int color, int endColor) {
    // 线的路径
    Path path2 = new Path();
    // 共几个转折点
    for (int i = 0; i < count; i++) {
        if (i == 0) {
            path2.moveTo(list.get(i).getX(), flag==0?list.get(i).getY1():list.get(i).getY2());
        } else {
            path2.lineTo(list.get(i).getX(), flag==0?list.get(i).getY1():list.get(i).getY2());
        }
    }
    // 上一个点  减去 下一个点的位置 计算中间点位置
    path2.lineTo(list.get(count - 1).getX() + (list.get(count).getX() - list.get(count - 1).getX()) / 50 * number,
            flag==0?list.get(count - 1).getY1():list.get(count - 1).getY2() + (flag==0?list.get(count).getY1():list.get(count).getY2() - flag==0?list.get(count - 1).getY1():list.get(count - 1).getY2()) / 50 * number);
    getPaint().setColor(color);
    getPaint().setStrokeWidth(2);
    getPaint().setStyle(Paint.Style.STROKE);// 设置为空心
    canvas.drawPath(path2, getPaint());

    path2.lineTo(list.get(count - 1).getX() + (list.get(count).getX() - list.get(count - 1).getX()) / 50 * number, LEFTDOWNY);
    path2.lineTo(list.get(0).getX(), LEFTDOWNY);
    path2.lineTo(list.get(0).getX(), flag==0?list.get(0).getY1():list.get(0).getY2());
    getPaint().setStyle(Paint.Style.FILL);// 设置为空心
    canvas.drawPath(path2, getShadeColorPaint(endColor,color));
    getPaint().reset();

}

4个回答

调用invalidate 的时候不是立即调用onDraw函数的,只是设置了一个flag,等待下一个绘制周期来的时候才会调用该View的onDraw函数
看代码中存放数据的list并没有加锁,所以在快速更新数据快速调用刷新的时候存在onDraw函数执行的过程中数据发生变化的风险

liangzi1717
liangzi1717 都加上了所有用到的地方都用synchronized (list)锁起来,不管是更新数据还是使用数据,清空画布也没用
2 年多之前 回复
chenxu2614
Tauren2614 回复Tauren2614: list更新数据的地方加锁了么,只在绘制的地方加起不到同步的作用的,给所有用到list的地方加同一个锁
2 年多之前 回复
chenxu2614
Tauren2614 回复Tauren2614: 在绘制开始时候添加个清空画布的操作
2 年多之前 回复
chenxu2614
Tauren2614 回复liangzi1717: 确认数据更新是正确的,list中没有上次数据的残留,你再绘制最开始的时候在画布上清空下看看呢
2 年多之前 回复
liangzi1717
liangzi1717 并且在使用和修改这个list的时候加上了synchronized (list) 也没用
2 年多之前 回复
liangzi1717
liangzi1717 但是我都用这句话上了锁:Collections.synchronizedList(new ArrayList());也并没有用哇,求大神指点
2 年多之前 回复

这是跟新数据:
synchronized (list) {
list.clear();
if (pointFs == null) {
pointFs = new ArrayList<>();
}
pointFs.clear();
dataSize=mAdapter.getSize();
for (int i = 0; i < dataSize; i++) {
float x1 = LEFTUPX + (mAdapter.getLands(i)) * LEFTRIGHTSPACE;//最左边的坐标+间隔*倍数
float y1 = LEFTDOWNY - (mAdapter.getTotal(i)) * UPDOWNSPACE;//最下面的坐标-间隔*倍数
float y2 = LEFTDOWNY - (mAdapter.getActual(i)) * UPDOWNSPACE;
list.add(new BrokenBean(x1, y2, y1));
//记录转折点
pointFs.add(new PointBean(new PointF(x1, y1), new PointF(x1, y2), PointBean.THEFIRSTLINE, mAdapter.getTotal(i), mAdapter.getActual(i)));

                 }
                 number = 1;
                 count = 1;
                 isFinish = false;
                 invalidate();
            }

这是画图时候:
synchronized (list) {
number = 50;
count = dataSize - 1;
//一定要先画矮的那根线,否则会被遮盖
if (/*listY2.size() > 0*/list.size() > 0)
canvasDataLines(canvas, 1, firstLineColor, firstBelowColor);

                 if (list.size() > 0)
                        canvasDataLines(canvas, 0, secondLineColor, secondBelowColor);
                 isFinish = true;   
        }

看起来可以排除,绘制过程数据变化的影响了,其它没看出啥问题来
或者可以把两次绘制之间的间隔改长点看看呢,大于32ms,改个一两百啥的
或者就是绘制的数据有问题,list就包含了上次的那根线

Csdn user default icon
上传中...
上传图片
插入图片
抄袭、复制答案,以达到刷声望分或其他目的的行为,在CSDN问答是严格禁止的,一经发现立刻封号。是时候展现真正的技术了!
立即提问