尽管设置了属性,但画布绘图未正确绘制

Posted

技术标签:

【中文标题】尽管设置了属性,但画布绘图未正确绘制【英文标题】:Canvas drawing not being drawn properly despite setting properties 【发布时间】:2015-11-27 02:40:53 【问题描述】:

我刚刚创建了 2 张图纸,试图让其中一张看起来与另一张相同,但我没有运气。至于屏幕截图,顶部的绘图是使用画布创建的,底部的绘图是使用 XML 创建的。 XML 绘图对我来说是完美的,但不幸的是,根据 android,我不应该使用它,因为它会返回“太多视图”警告。我已经尝试了以下画布绘制方法,但仍然没有得到想要的结果。

我已经在十几个模拟器上测试了这个项目,尽管 XML 绘图有这么多视图,但它们都只用了不到一秒钟的时间就出现了。

    根据设备的方向使用不同的绘图 使用浮点值而不是整数 多次调整浮点值

有没有人知道还有什么其他选项可以解决这个烦人的画布问题,并确保无论屏幕大小和方向如何,画布绘图看起来都与 XML 绘图完全相同?在这种情况下是否应该忽略“太多视图”警告和/或我将视图计数限制增加到 80 以上?所有相关的帮助将不胜感激。

Java 代码

http://pastebin.com/VXgkJR2Z

XML 代码

http://pastebin.com/JyVvxS5n

【问题讨论】:

所以你必须能够切换这些矩形或其他东西吗? 您可以创建自定义视图或使用include 来分离您的布局并绕过 lint 警告。我认为这不会对性能产生任何影响。毕竟,这只是一个警告。 【参考方案1】:

您可以在下面看到处理代码后的结果。跟xml差不多,但是如果用mignifier看的话还是有区别的。

所以在你画画的时候有一些重要的事情。

    不要使用整数。你正在失去精确度。我将所有 int 替换为浮动在您的代码中。 小心循环。您的循环精度也在下降。

代码中的常见模式:

float boxWidth = (getWidth() - mSideRectWidth) / 8; // I replaced int to float
for (int i = 0; i < 8; i++) 
    float position = i * boxWidth; // loss of precision
    ...

最好在循环中计算位置:

for (int i = 0; i < 8; i++) 
    float position = i * (getWidth() - mSideRectWidth) / 8;
    ...

    不要忘记笔画宽度。在计算形状和线条的位置时,您会错过该值。

这是我的完整代码:

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;

public class Car extends View 
    private final Paint mBlackPaint = new Paint();
    private final Paint mRedPaint = new Paint();
    private final TextPaint mTextPaint;

    public static final int BOXES_COUNT = 8;

    private float oneDp;
    private float textSize;
    private float windowHeight;

    public Car(Context context, AttributeSet attrs) 
        super(context, attrs);
        oneDp = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1,
            getResources().getDisplayMetrics());
        windowHeight = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10,
            getResources().getDisplayMetrics());
        textSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 15,
            getResources().getDisplayMetrics());

        mRedPaint.setColor(Color.parseColor("#CC3333"));

        mBlackPaint.setAntiAlias(true);
        mBlackPaint.setColor(Color.BLACK);
        mBlackPaint.setStrokeWidth(oneDp);
        mBlackPaint.setStyle(Paint.Style.STROKE);

        mTextPaint = new TextPaint(TextPaint.ANTI_ALIAS_FLAG);
        mTextPaint.setColor(Color.BLACK);
        mTextPaint.setTextAlign(Paint.Align.CENTER);
        mTextPaint.setTextSize(textSize);

        mWindowPaint = new Paint();
        mWindowPaint.setAntiAlias(true);
        mWindowPaint.setColor(Color.parseColor("#CC3333"));
        mWindowPaint.setStyle(Paint.Style.STROKE);
        mWindowPaint.setStrokeWidth(oneDp);
    

    private Paint mWindowPaint;
    RectF rect = new RectF();
    RectF rect2 = new RectF();

    @Override
    protected void onDraw(Canvas canvas) 
        super.onDraw(canvas);

        if (getWidth() == 0) return;

        int w = canvas.getWidth();
        int h = canvas.getHeight();

        //draw red rectangles
        float mSideRectWidth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 5,
            getResources().getDisplayMetrics());
        canvas.drawRect(0, 0, mSideRectWidth, getHeight(), mRedPaint); //draw left end rectangle
        canvas.drawRect(getWidth() - mSideRectWidth, 0, getWidth(), getHeight(),
            mRedPaint); //draw right end rectangle

        //draw grey boxes
        setBackgroundColor(Color.parseColor("#808080"));

        for (int i = 0; i < BOXES_COUNT; i++) 
            float leftPosition = mSideRectWidth
                + i * oneDp
                + (getWidth() - mSideRectWidth * 2 - (BOXES_COUNT - 1) * oneDp) * i / BOXES_COUNT;
            float rightPosition = mSideRectWidth
                + i * oneDp
                + (getWidth() - mSideRectWidth * 2 - (BOXES_COUNT - 1) * oneDp) * (i + 1)
                / BOXES_COUNT;
            if (i == 0) 
                fillRectLeft(canvas, leftPosition, rightPosition, (i + 1) + "");
             else if ( i == BOXES_COUNT - 1) 
                fillRectRight(canvas, leftPosition, rightPosition, (i + 1) + "");
             else 
                fillRect(canvas, leftPosition, rightPosition, (i + 1) + "");
            
        

        //draw black lines
        for (int i = 1; i < BOXES_COUNT; i++) 
            float position = mSideRectWidth + (getWidth() - mSideRectWidth * 2) * i / BOXES_COUNT;
            canvas.drawLine(position, 0, position, getHeight(), mBlackPaint);
        
    

    private void fillRect(Canvas canvas, float left, float right, String number) 
        rect.set(left + oneDp / 2, 0 + oneDp / 2, right - oneDp / 2, getHeight() - oneDp / 2);
        float xPos = left + ((right - left) / 2);
        float yPos = (canvas.getHeight() / 2) - ((mTextPaint.descent() + mTextPaint.ascent()) / 2);
        //((textPaint.descent() + textPaint.ascent()) / 2) is the distance from the baseline to the center.
        canvas.drawText(number, xPos, yPos, mTextPaint);
        //canvas.drawRect(rect, mWindowPaint);

        // top row
        rect2.set(left + oneDp / 2, 0 + oneDp / 2, left + (right - left) * 20 / 100 - oneDp / 2,
            windowHeight - oneDp / 2);
        canvas.drawRect(rect2, mWindowPaint);
        rect2.set(left + (right - left) * 27 / 100 + oneDp / 2, 0 + oneDp / 2,
            left + (right - left) * 47 / 100 - oneDp / 2, windowHeight - oneDp / 2);
        canvas.drawRect(rect2, mWindowPaint);
        rect2.set(left + (right - left) * 53 / 100 + oneDp / 2, 0 + oneDp / 2,
            left + (right - left) * 73 / 100 - oneDp / 2, windowHeight - oneDp / 2);
        canvas.drawRect(rect2, mWindowPaint);
        rect2.set(left + (right - left) * 80 / 100 + oneDp / 2, 0 + oneDp / 2,
            left + (right - left) * 100 / 100 - oneDp / 2, windowHeight - oneDp / 2);
        canvas.drawRect(rect2, mWindowPaint);

        // bottom row
        rect2.set(left + oneDp / 2, getHeight() - windowHeight + oneDp / 2,
            left + (right - left) * 20 / 100 - oneDp / 2, getHeight() - oneDp / 2);
        canvas.drawRect(rect2, mWindowPaint);
        rect2.set(left + (right - left) * 27 / 100 + oneDp / 2,
            getHeight() - windowHeight + oneDp / 2, left + (right - left) * 47 / 100 - oneDp / 2,
            getHeight() - oneDp / 2);
        canvas.drawRect(rect2, mWindowPaint);
        rect2.set(left + (right - left) * 53 / 100 + oneDp / 2,
            getHeight() - windowHeight + oneDp / 2, left + (right - left) * 73 / 100 - oneDp / 2,
            getHeight() - oneDp / 2);
        canvas.drawRect(rect2, mWindowPaint);
        rect2.set(left + (right - left) * 80 / 100 + oneDp / 2,
            getHeight() - windowHeight + oneDp / 2, left + (right - left) * 100 / 100 - oneDp / 2,
            getHeight() - oneDp / 2);
        canvas.drawRect(rect2, mWindowPaint);
    

    private void fillRectLeft(Canvas canvas, float left, float right, String number) 
        rect.set(left + oneDp / 2, 0 + oneDp / 2, right - oneDp / 2, getHeight() - oneDp / 2);
        float xPos = left + ((right - left) / 2);
        float yPos = (canvas.getHeight() / 2) - ((mTextPaint.descent() + mTextPaint.ascent()) / 2);
        //((textPaint.descent() + textPaint.ascent()) / 2) is the distance from the baseline to the center.
        canvas.drawText(number, xPos, yPos, mTextPaint);
        //canvas.drawRect(rect, mWindowPaint);

        // top row
        rect2.set(left + (right - left) * 4 / 100 + oneDp / 2, 0 + oneDp / 2,
            left + (right - left) * 24 / 100 - oneDp / 2, windowHeight - oneDp / 2);
        canvas.drawRect(rect2, mWindowPaint);
        rect2.set(left + (right - left) * 42 / 100 + oneDp / 2, 0 + oneDp / 2,
            left + (right - left) * 62 / 100 - oneDp / 2, windowHeight - oneDp / 2);
        canvas.drawRect(rect2, mWindowPaint);
        rect2.set(left + (right - left) * 80 / 100 + oneDp / 2, 0 + oneDp / 2,
            left + (right - left) * 100 / 100 - oneDp / 2, windowHeight - oneDp / 2);
        canvas.drawRect(rect2, mWindowPaint);

        // bottom row
        rect2.set(left + (right - left) * 4 / 100 + oneDp / 2,
            getHeight() - windowHeight + oneDp / 2, left + (right - left) * 24 / 100 - oneDp / 2,
            getHeight() - oneDp / 2);
        canvas.drawRect(rect2, mWindowPaint);
        rect2.set(left + (right - left) * 42 / 100 + oneDp / 2,
            getHeight() - windowHeight + oneDp / 2, left + (right - left) * 62 / 100 - oneDp / 2,
            getHeight() - oneDp / 2);
        canvas.drawRect(rect2, mWindowPaint);
        rect2.set(left + (right - left) * 80 / 100 + oneDp / 2,
            getHeight() - windowHeight + oneDp / 2, left + (right - left) * 100 / 100 - oneDp / 2,
            getHeight() - oneDp / 2);
        canvas.drawRect(rect2, mWindowPaint);
    

    private void fillRectRight(Canvas canvas, float left, float right, String number) 
        rect.set(left + oneDp / 2, 0 + oneDp / 2, right - oneDp / 2, getHeight() - oneDp / 2);
        float xPos = left + ((right - left) / 2);
        float yPos = (canvas.getHeight() / 2) - ((mTextPaint.descent() + mTextPaint.ascent()) / 2);
        //((textPaint.descent() + textPaint.ascent()) / 2) is the distance from the baseline to the center.
        canvas.drawText(number, xPos, yPos, mTextPaint);
        //canvas.drawRect(rect, mWindowPaint);

        // top row
        rect2.set(left + (right - left) * 0 / 105 + oneDp / 2, 0 + oneDp / 2,
            left + (right - left) * 20 / 105 - oneDp / 2, windowHeight - oneDp / 2);
        canvas.drawRect(rect2, mWindowPaint);
        rect2.set(left + (right - left) * 38 / 105 + oneDp / 2, 0 + oneDp / 2,
            left + (right - left) * 58 / 105 - oneDp / 2, windowHeight - oneDp / 2);
        canvas.drawRect(rect2, mWindowPaint);
        rect2.set(left + (right - left) * 78 / 105 + oneDp / 2, 0 + oneDp / 2,
            left + (right - left) * 98 / 105 - oneDp / 2, windowHeight - oneDp / 2);
        canvas.drawRect(rect2, mWindowPaint);

        // bottom row
        rect2.set(left + (right - left) * 0 / 105 + oneDp / 2,
            getHeight() - windowHeight + oneDp / 2, left + (right - left) * 20 / 105 - oneDp / 2,
            getHeight() - oneDp / 2);
        canvas.drawRect(rect2, mWindowPaint);
        rect2.set(left + (right - left) * 38 / 105 + oneDp / 2,
            getHeight() - windowHeight + oneDp / 2, left + (right - left) * 58 / 105 - oneDp / 2,
            getHeight() - oneDp / 2);
        canvas.drawRect(rect2, mWindowPaint);
        rect2.set(left + (right - left) * 78 / 105 + oneDp / 2,
            getHeight() - windowHeight + oneDp / 2, left + (right - left) * 98 / 105 - oneDp / 2,
            getHeight() - oneDp / 2);
        canvas.drawRect(rect2, mWindowPaint);
    

肖像

风景

【讨论】:

如果您将float 更改为double,您的答案将会得到改善。在大多数硬件上,单浮点数相对于双浮点数的唯一优势是,如果您正在处理它们的巨大数组,则单浮点数占用一半的空间,并使用一半的内存带宽。否则,double 将获胜。即使您有充分的理由存储浮点数,您也应该在计算中使用它们时将它们提升为双倍。 如何将红色窗口更改为mWindowPaint.setStyle(Paint.Style.STROKE_AND_FILL) 而不是mWindowPaint.setStyle(Paint.Style.STROKE)?特别是对于方框 2-7 中的一个红色窗口。 @MacaronLover 是的,你是对的。只需在绘制填充矩形之前添加行mWindowPaint.setStyle(Paint.Style.FILL_AND_STROKE);,然后在绘制空矩形之前返回mWindowPaint.setStyle(Paint.Style.STROKE) @Ilya 酷。我明白这一点,但有点失落。例如。只需要在方框 6 中填充右上角的红色窗口,需要做什么? @MacaronLover 在上面的代码中的fillRect 方法绘制位于中间的框(由垂直黑线分隔)。为了您的目的,最好使用有关填充或空窗口的信息创建 Box 类(布尔 topLeft、topMiddle、topRight、bottomLeft、bottomMiddle、bottomRight)。然后使用此信息向 fillRect 方法添加附加参数。最后,以下一个方式绘制if (box.topLeft) mWindowPaint.setStyle(Paint.Style.FILL_AND_STROKE); else mWindowPaint.setStyle(Paint.Style.STROKE); canvas.drawRect(rect2, mWindowPaint);【参考方案2】:

如何组合这两个变体 - 使用 addView(your_view, your_layout_params)

以编程方式添加这些视图

【讨论】:

以上是关于尽管设置了属性,但画布绘图未正确绘制的主要内容,如果未能解决你的问题,请参考以下文章

在 UWP 中使用模糊效果绘制图像未正确设置图像大小

在 HTML 画布上绘制的线条未出现在触摸事件中

画布圆圈未正确绘制[重复]

使用图像在画布上绘图时如何更正笔画位置?用打字稿角

在 Matplotlib 中设置绘图画布的大小

canvas知识点